Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

wanakana

Package Overview
Dependencies
Maintainers
4
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

wanakana - npm Package Compare versions

Comparing version 5.2.0 to 5.3.0

cjs/index.d.ts

6

CHANGELOG.md

@@ -29,2 +29,8 @@ <!-- markdownlint-disable MD024 -->

## [5.3.0] - 2023-11-19
### Added
Typescript definitions have been added 🎉
## [5.2.0] - 2023-09-30

@@ -31,0 +37,0 @@

2179

cjs/index.js
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
/**

@@ -25,12 +23,12 @@ * Returns detailed type as string (instead of just 'object' for arrays etc)

function typeOf(value) {
if (value === null) {
return 'null';
}
if (value !== Object(value)) {
return typeof value;
}
return {}.toString
.call(value)
.slice(8, -1)
.toLowerCase();
if (value === null) {
return 'null';
}
if (value !== Object(value)) {
return typeof value;
}
return {}.toString
.call(value)
.slice(8, -1)
.toLowerCase();
}

@@ -44,6 +42,6 @@

function isEmpty(input) {
if (typeOf(input) !== 'string') {
return true;
}
return !input.length;
if (typeOf(input) !== 'string') {
return true;
}
return !input.length;
}

@@ -59,22 +57,20 @@

function isCharInRange(char = '', start, end) {
if (isEmpty(char)) return false;
const code = char.charCodeAt(0);
return start <= code && code <= end;
if (isEmpty(char))
return false;
const code = char.charCodeAt(0);
return start <= code && code <= end;
}
const VERSION = '5.2.0';
const TO_KANA_METHODS = {
HIRAGANA: 'toHiragana',
KATAKANA: 'toKatakana',
HIRAGANA: 'toHiragana',
KATAKANA: 'toKatakana',
};
const ROMANIZATIONS = {
HEPBURN: 'hepburn',
HEPBURN: 'hepburn',
};
/**
* Default config for WanaKana, user passed options will be merged with these
* @type {DefaultOptions}
* @name defaultOptions
* @name DefaultOptions
* @property {Boolean} [useObsoleteKana=false] - Set to true to use obsolete characters, such as ゐ and ゑ.

@@ -88,3 +84,3 @@ * @example

* // => "only convert the katakana: ひらがな"
* @property {Object} [convertLongVowelMark=true] - Set to false to prevent conversions of 'ー' to extended vowels with toHiragana()
* @property {Boolean} [convertLongVowelMark=true] - Set to false to prevent conversions of 'ー' to extended vowels with toHiragana()
* @example

@@ -97,9 +93,9 @@ * toHiragana('ラーメン', { convertLongVowelMark: false });

* // => "hiragana KATAKANA"
* @property {Boolean|String} [IMEMode=false] - Set to true, 'toHiragana', or 'toKatakana' to handle conversion while it is being typed.
* @property {String} [romanization='hepburn'] - choose toRomaji() romanization map (currently only 'hepburn')
* @property {Object} [customKanaMapping] - custom map will be merged with default conversion
* @property {Boolean | 'toHiragana' | 'toKatakana'} [IMEMode=false] - Set to true, 'toHiragana', or 'toKatakana' to handle conversion while it is being typed.
* @property {'hepburn'} [romanization='hepburn'] - choose toRomaji() romanization map (currently only 'hepburn')
* @property {Object.<String, String>} [customKanaMapping] - custom map will be merged with default conversion
* @example
* toKana('wanakana', { customKanaMapping: { na: 'に', ka: 'Bana' }) };
* // => 'わにBanaに'
* @property {Object} [customRomajiMapping] - custom map will be merged with default conversion
* @property {Object.<String, String>} [customRomajiMapping] - custom map will be merged with default conversion
* @example

@@ -110,8 +106,8 @@ * toRomaji('つじぎり', { customRomajiMapping: { じ: 'zi', つ: 'tu', り: 'li' }) };

const DEFAULT_OPTIONS = {
useObsoleteKana: false,
passRomaji: false,
upcaseKatakana: false,
IMEMode: false,
convertLongVowelMark: true,
romanization: ROMANIZATIONS.HEPBURN,
useObsoleteKana: false,
passRomaji: false,
convertLongVowelMark: true,
upcaseKatakana: false,
IMEMode: false,
romanization: ROMANIZATIONS.HEPBURN,
};

@@ -130,7 +126,5 @@ const LATIN_UPPERCASE_START = 0x41;

const KANJI_END = 0x9faf;
const KANJI_ITERATION_MARK = 0x3005; // 々
const PROLONGED_SOUND_MARK = 0x30fc; // ー
const KANA_SLASH_DOT = 0x30fb; // ・
const ZENKAKU_NUMBERS = [0xff10, 0xff19];

@@ -144,3 +138,2 @@ const ZENKAKU_UPPERCASE = [UPPERCASE_ZENKAKU_START, UPPERCASE_ZENKAKU_END];

const ZENKAKU_SYMBOLS_CURRENCY = [0xffe0, 0xffee];
const HIRAGANA_CHARS = [0x3040, 0x309f];

@@ -154,54 +147,48 @@ const KATAKANA_CHARS = [0x30a0, 0x30ff];

const RARE_CJK = [0x3400, 0x4dbf];
const KANA_RANGES = [
HIRAGANA_CHARS,
KATAKANA_CHARS,
KANA_PUNCTUATION,
HANKAKU_KATAKANA,
HIRAGANA_CHARS,
KATAKANA_CHARS,
KANA_PUNCTUATION,
HANKAKU_KATAKANA,
];
const JA_PUNCTUATION_RANGES = [
CJK_SYMBOLS_PUNCTUATION,
KANA_PUNCTUATION,
KATAKANA_PUNCTUATION,
ZENKAKU_PUNCTUATION_1,
ZENKAKU_PUNCTUATION_2,
ZENKAKU_PUNCTUATION_3,
ZENKAKU_PUNCTUATION_4,
ZENKAKU_SYMBOLS_CURRENCY,
CJK_SYMBOLS_PUNCTUATION,
KANA_PUNCTUATION,
KATAKANA_PUNCTUATION,
ZENKAKU_PUNCTUATION_1,
ZENKAKU_PUNCTUATION_2,
ZENKAKU_PUNCTUATION_3,
ZENKAKU_PUNCTUATION_4,
ZENKAKU_SYMBOLS_CURRENCY,
];
// All Japanese unicode start and end ranges
// Includes kanji, kana, zenkaku latin chars, punctuation, and number ranges.
const JAPANESE_RANGES = [
...KANA_RANGES,
...JA_PUNCTUATION_RANGES,
ZENKAKU_UPPERCASE,
ZENKAKU_LOWERCASE,
ZENKAKU_NUMBERS,
COMMON_CJK,
RARE_CJK,
...KANA_RANGES,
...JA_PUNCTUATION_RANGES,
ZENKAKU_UPPERCASE,
ZENKAKU_LOWERCASE,
ZENKAKU_NUMBERS,
COMMON_CJK,
RARE_CJK,
];
const MODERN_ENGLISH = [0x0000, 0x007f];
const HEPBURN_MACRON_RANGES = [
[0x0100, 0x0101], // Ā ā
[0x0112, 0x0113], // Ē ē
[0x012a, 0x012b], // Ī ī
[0x014c, 0x014d], // Ō ō
[0x016a, 0x016b], // Ū ū
[0x0100, 0x0101],
[0x0112, 0x0113],
[0x012a, 0x012b],
[0x014c, 0x014d],
[0x016a, 0x016b], // Ū ū
];
const SMART_QUOTE_RANGES = [
[0x2018, 0x2019], // ‘ ’
[0x201c, 0x201d], // “ ”
[0x2018, 0x2019],
[0x201c, 0x201d], // “ ”
];
const ROMAJI_RANGES = [MODERN_ENGLISH, ...HEPBURN_MACRON_RANGES];
const EN_PUNCTUATION_RANGES = [
[0x20, 0x2f],
[0x3a, 0x3f],
[0x5b, 0x60],
[0x7b, 0x7e],
...SMART_QUOTE_RANGES,
[0x20, 0x2f],
[0x3a, 0x3f],
[0x5b, 0x60],
[0x7b, 0x7e],
...SMART_QUOTE_RANGES,
];

@@ -215,3 +202,3 @@

function isCharJapanese(char = '') {
return JAPANESE_RANGES.some(([start, end]) => isCharInRange(char, start, end));
return JAPANESE_RANGES.some(([start, end]) => isCharInRange(char, start, end));
}

@@ -222,3 +209,3 @@

* @param {String} [input=''] text
* @param {Regexp} [allowed] additional test allowed to pass for each char
* @param {RegExp} [allowed] additional test allowed to pass for each char
* @return {Boolean} true if passes checks

@@ -242,9 +229,9 @@ * @example

function isJapanese(input = '', allowed) {
const augmented = typeOf(allowed) === 'regexp';
return isEmpty(input)
? false
: [...input].every((char) => {
const isJa = isCharJapanese(char);
return !augmented ? isJa : isJa || allowed.test(char);
});
const augmented = typeOf(allowed) === 'regexp';
return isEmpty(input)
? false
: [...input].every((char) => {
const isJa = isCharJapanese(char);
return !augmented ? isJa : isJa || allowed.test(char);
});
}

@@ -276,5 +263,6 @@

}
function memoizeOne(resultFn, isEqual) {
if (isEqual === void 0) { isEqual = areInputsEqual; }
if (isEqual === void 0) {
isEqual = areInputsEqual;
}
var cache = null;

@@ -304,84 +292,87 @@ function memoized() {

var has = Object.prototype.hasOwnProperty;
function find(iter, tar, key) {
for (key of iter.keys()) {
if (dequal(key, tar)) return key;
}
for (key of iter.keys()) {
if (dequal(key, tar))
return key;
}
}
function dequal(foo, bar) {
var ctor, len, tmp;
if (foo === bar) return true;
if (foo && bar && (ctor=foo.constructor) === bar.constructor) {
if (ctor === Date) return foo.getTime() === bar.getTime();
if (ctor === RegExp) return foo.toString() === bar.toString();
if (ctor === Array) {
if ((len=foo.length) === bar.length) {
while (len-- && dequal(foo[len], bar[len]));
}
return len === -1;
}
if (ctor === Set) {
if (foo.size !== bar.size) {
return false;
}
for (len of foo) {
tmp = len;
if (tmp && typeof tmp === 'object') {
tmp = find(bar, tmp);
if (!tmp) return false;
}
if (!bar.has(tmp)) return false;
}
return true;
}
if (ctor === Map) {
if (foo.size !== bar.size) {
return false;
}
for (len of foo) {
tmp = len[0];
if (tmp && typeof tmp === 'object') {
tmp = find(bar, tmp);
if (!tmp) return false;
}
if (!dequal(len[1], bar.get(tmp))) {
return false;
}
}
return true;
}
if (ctor === ArrayBuffer) {
foo = new Uint8Array(foo);
bar = new Uint8Array(bar);
} else if (ctor === DataView) {
if ((len=foo.byteLength) === bar.byteLength) {
while (len-- && foo.getInt8(len) === bar.getInt8(len));
}
return len === -1;
}
if (ArrayBuffer.isView(foo)) {
if ((len=foo.byteLength) === bar.byteLength) {
while (len-- && foo[len] === bar[len]);
}
return len === -1;
}
if (!ctor || typeof foo === 'object') {
len = 0;
for (ctor in foo) {
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
}
return Object.keys(bar).length === len;
}
}
return foo !== foo && bar !== bar;
var ctor, len, tmp;
if (foo === bar)
return true;
if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
if (ctor === Date)
return foo.getTime() === bar.getTime();
if (ctor === RegExp)
return foo.toString() === bar.toString();
if (ctor === Array) {
if ((len = foo.length) === bar.length) {
while (len-- && dequal(foo[len], bar[len]))
;
}
return len === -1;
}
if (ctor === Set) {
if (foo.size !== bar.size) {
return false;
}
for (len of foo) {
tmp = len;
if (tmp && typeof tmp === 'object') {
tmp = find(bar, tmp);
if (!tmp)
return false;
}
if (!bar.has(tmp))
return false;
}
return true;
}
if (ctor === Map) {
if (foo.size !== bar.size) {
return false;
}
for (len of foo) {
tmp = len[0];
if (tmp && typeof tmp === 'object') {
tmp = find(bar, tmp);
if (!tmp)
return false;
}
if (!dequal(len[1], bar.get(tmp))) {
return false;
}
}
return true;
}
if (ctor === ArrayBuffer) {
foo = new Uint8Array(foo);
bar = new Uint8Array(bar);
}
else if (ctor === DataView) {
if ((len = foo.byteLength) === bar.byteLength) {
while (len-- && foo.getInt8(len) === bar.getInt8(len))
;
}
return len === -1;
}
if (ArrayBuffer.isView(foo)) {
if ((len = foo.byteLength) === bar.byteLength) {
while (len-- && foo[len] === bar[len])
;
}
return len === -1;
}
if (!ctor || typeof foo === 'object') {
len = 0;
for (ctor in foo) {
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor))
return false;
if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor]))
return false;
}
return Object.keys(bar).length === len;
}
}
return foo !== foo && bar !== bar;
}

@@ -397,78 +388,58 @@

function applyMapping(string, mapping, convertEnding) {
const root = mapping;
function nextSubtree(tree, nextChar) {
const subtree = tree[nextChar];
if (subtree === undefined) {
return undefined;
const root = mapping;
function nextSubtree(tree, nextChar) {
const subtree = tree[nextChar];
if (subtree === undefined) {
return undefined;
}
// if the next child node does not have a node value, set its node value to the input
return Object.assign({ '': tree[''] + nextChar }, tree[nextChar]);
}
// if the next child node does not have a node value, set its node value to the input
return Object.assign({ '': tree[''] + nextChar }, tree[nextChar]);
}
function newChunk(remaining, currentCursor) {
// start parsing a new chunk
const firstChar = remaining.charAt(0);
return parse(
Object.assign({ '': firstChar }, root[firstChar]),
remaining.slice(1),
currentCursor,
currentCursor + 1
);
}
function parse(tree, remaining, lastCursor, currentCursor) {
if (!remaining) {
if (convertEnding || Object.keys(tree).length === 1) {
// nothing more to consume, just commit the last chunk and return it
// so as to not have an empty element at the end of the result
return tree[''] ? [[lastCursor, currentCursor, tree['']]] : [];
}
// if we don't want to convert the ending, because there are still possible continuations
// return null as the final node value
return [[lastCursor, currentCursor, null]];
function newChunk(remaining, currentCursor) {
// start parsing a new chunk
const firstChar = remaining.charAt(0);
return parse(Object.assign({ '': firstChar }, root[firstChar]), remaining.slice(1), currentCursor, currentCursor + 1);
}
if (Object.keys(tree).length === 1) {
return [[lastCursor, currentCursor, tree['']]].concat(
newChunk(remaining, currentCursor)
);
function parse(tree, remaining, lastCursor, currentCursor) {
if (!remaining) {
if (convertEnding || Object.keys(tree).length === 1) {
// nothing more to consume, just commit the last chunk and return it
// so as to not have an empty element at the end of the result
return tree[''] ? [[lastCursor, currentCursor, tree['']]] : [];
}
// if we don't want to convert the ending, because there are still possible continuations
// return null as the final node value
return [[lastCursor, currentCursor, null]];
}
if (Object.keys(tree).length === 1) {
return [[lastCursor, currentCursor, tree['']]].concat(newChunk(remaining, currentCursor));
}
const subtree = nextSubtree(tree, remaining.charAt(0));
if (subtree === undefined) {
return [[lastCursor, currentCursor, tree['']]].concat(newChunk(remaining, currentCursor));
}
// continue current branch
return parse(subtree, remaining.slice(1), lastCursor, currentCursor + 1);
}
const subtree = nextSubtree(tree, remaining.charAt(0));
if (subtree === undefined) {
return [[lastCursor, currentCursor, tree['']]].concat(
newChunk(remaining, currentCursor)
);
}
// continue current branch
return parse(subtree, remaining.slice(1), lastCursor, currentCursor + 1);
}
return newChunk(string, 0);
return newChunk(string, 0);
}
// transform the tree, so that for example hepburnTree['ゔ']['ぁ'][''] === 'va'
// or kanaTree['k']['y']['a'][''] === 'きゃ'
function transform(tree) {
return Object.entries(tree).reduce((map, [char, subtree]) => {
const endOfBranch = typeOf(subtree) === 'string';
// eslint-disable-next-line no-param-reassign
map[char] = endOfBranch ? { '': subtree } : transform(subtree);
return map;
}, {});
return Object.entries(tree).reduce((map, [char, subtree]) => {
const endOfBranch = typeOf(subtree) === 'string';
// eslint-disable-next-line no-param-reassign
map[char] = endOfBranch ? { '': subtree } : transform(subtree);
return map;
}, {});
}
function getSubTreeOf(tree, string) {
return string.split('').reduce((correctSubTree, char) => {
if (correctSubTree[char] === undefined) {
// eslint-disable-next-line no-param-reassign
correctSubTree[char] = {};
}
return correctSubTree[char];
}, tree);
return string.split('').reduce((correctSubTree, char) => {
if (correctSubTree[char] === undefined) {
// eslint-disable-next-line no-param-reassign
correctSubTree[char] = {};
}
return correctSubTree[char];
}, tree);
}
/**

@@ -486,46 +457,38 @@ * Creates a custom mapping tree, returns a function that accepts a defaultMap which the newly created customMapping will be merged with and returned

function createCustomMapping(customMap = {}) {
const customTree = {};
if (typeOf(customMap) === 'object') {
Object.entries(customMap).forEach(([roma, kana]) => {
let subTree = customTree;
roma.split('').forEach((char) => {
if (subTree[char] === undefined) {
subTree[char] = {};
const customTree = {};
if (typeOf(customMap) === 'object') {
Object.entries(customMap).forEach(([roma, kana]) => {
let subTree = customTree;
roma.split('').forEach((char) => {
if (subTree[char] === undefined) {
subTree[char] = {};
}
subTree = subTree[char];
});
subTree[''] = kana;
});
}
return function makeMap(map) {
const mapCopy = JSON.parse(JSON.stringify(map));
function transformMap(mapSubtree, customSubtree) {
if (mapSubtree === undefined || typeOf(mapSubtree) === 'string') {
return customSubtree;
}
return Object.entries(customSubtree).reduce((newSubtree, [char, subtree]) => {
// eslint-disable-next-line no-param-reassign
newSubtree[char] = transformMap(mapSubtree[char], subtree);
return newSubtree;
}, mapSubtree);
}
subTree = subTree[char];
});
subTree[''] = kana;
});
}
return function makeMap(map) {
const mapCopy = JSON.parse(JSON.stringify(map));
function transformMap(mapSubtree, customSubtree) {
if (mapSubtree === undefined || typeOf(mapSubtree) === 'string') {
return customSubtree;
}
return Object.entries(customSubtree).reduce(
(newSubtree, [char, subtree]) => {
// eslint-disable-next-line no-param-reassign
newSubtree[char] = transformMap(mapSubtree[char], subtree);
return newSubtree;
},
mapSubtree
);
}
return transformMap(mapCopy, customTree);
};
return transformMap(mapCopy, customTree);
};
}
// allow consumer to pass either function or object as customMapping
function mergeCustomMapping(map, customMapping) {
if (!customMapping) {
return map;
}
return typeOf(customMapping) === 'function'
? customMapping(map)
: createCustomMapping(customMapping)(map);
if (!customMapping) {
return map;
}
return typeOf(customMapping) === 'function'
? customMapping(map)
: createCustomMapping(customMapping)(map);
}

@@ -537,82 +500,76 @@

const BASIC_KUNREI = {
a: 'あ', i: 'い', u: 'う', e: 'え', o: 'お',
k: { a: 'か', i: 'き', u: 'く', e: 'け', o: 'こ', },
s: { a: 'さ', i: 'し', u: 'す', e: 'せ', o: 'そ', },
t: { a: 'た', i: 'ち', u: 'つ', e: 'て', o: 'と', },
n: { a: 'な', i: 'に', u: 'ぬ', e: 'ね', o: 'の', },
h: { a: 'は', i: 'ひ', u: 'ふ', e: 'へ', o: 'ほ', },
m: { a: 'ま', i: 'み', u: 'む', e: 'め', o: 'も', },
y: { a: 'や', u: 'ゆ', o: 'よ' },
r: { a: 'ら', i: 'り', u: 'る', e: 'れ', o: 'ろ', },
w: { a: 'わ', i: 'ゐ', e: 'ゑ', o: 'を', },
g: { a: 'が', i: 'ぎ', u: 'ぐ', e: 'げ', o: 'ご', },
z: { a: 'ざ', i: 'じ', u: 'ず', e: 'ぜ', o: 'ぞ', },
d: { a: 'だ', i: 'ぢ', u: 'づ', e: 'で', o: 'ど', },
b: { a: 'ば', i: 'び', u: 'ぶ', e: 'べ', o: 'ぼ', },
p: { a: 'ぱ', i: 'ぴ', u: 'ぷ', e: 'ぺ', o: 'ぽ', },
v: { a: 'ゔぁ', i: 'ゔぃ', u: 'ゔ', e: 'ゔぇ', o: 'ゔぉ', },
a: 'あ', i: 'い', u: 'う', e: 'え', o: 'お',
k: { a: 'か', i: 'き', u: 'く', e: 'け', o: 'こ', },
s: { a: 'さ', i: 'し', u: 'す', e: 'せ', o: 'そ', },
t: { a: 'た', i: 'ち', u: 'つ', e: 'て', o: 'と', },
n: { a: 'な', i: 'に', u: 'ぬ', e: 'ね', o: 'の', },
h: { a: 'は', i: 'ひ', u: 'ふ', e: 'へ', o: 'ほ', },
m: { a: 'ま', i: 'み', u: 'む', e: 'め', o: 'も', },
y: { a: 'や', u: 'ゆ', o: 'よ' },
r: { a: 'ら', i: 'り', u: 'る', e: 'れ', o: 'ろ', },
w: { a: 'わ', i: 'ゐ', e: 'ゑ', o: 'を', },
g: { a: 'が', i: 'ぎ', u: 'ぐ', e: 'げ', o: 'ご', },
z: { a: 'ざ', i: 'じ', u: 'ず', e: 'ぜ', o: 'ぞ', },
d: { a: 'だ', i: 'ぢ', u: 'づ', e: 'で', o: 'ど', },
b: { a: 'ば', i: 'び', u: 'ぶ', e: 'べ', o: 'ぼ', },
p: { a: 'ぱ', i: 'ぴ', u: 'ぷ', e: 'ぺ', o: 'ぽ', },
v: { a: 'ゔぁ', i: 'ゔぃ', u: 'ゔ', e: 'ゔぇ', o: 'ゔぉ', },
};
const SPECIAL_SYMBOLS$1 = {
'.': '。',
',': '、',
':': ':',
'/': '・',
'!': '!',
'?': '?',
'~': '〜',
'-': 'ー',
'‘': '「',
'’': '」',
'“': '『',
'”': '』',
'[': '[',
']': ']',
'(': '(',
')': ')',
'{': '{',
'}': '}',
'.': '。',
',': '、',
':': ':',
'/': '・',
'!': '!',
'?': '?',
'~': '〜',
'-': 'ー',
'‘': '「',
'’': '」',
'“': '『',
'”': '』',
'[': '[',
']': ']',
'(': '(',
')': ')',
'{': '{',
'}': '}',
};
const CONSONANTS = {
k: 'き',
s: 'し',
t: 'ち',
n: 'に',
h: 'ひ',
m: 'み',
r: 'り',
g: 'ぎ',
z: 'じ',
d: 'ぢ',
b: 'び',
p: 'ぴ',
v: 'ゔ',
q: 'く',
f: 'ふ',
k: 'き',
s: 'し',
t: 'ち',
n: 'に',
h: 'ひ',
m: 'み',
r: 'り',
g: 'ぎ',
z: 'じ',
d: 'ぢ',
b: 'び',
p: 'ぴ',
v: 'ゔ',
q: 'く',
f: 'ふ',
};
const SMALL_Y$1 = { ya: 'ゃ', yi: 'ぃ', yu: 'ゅ', ye: 'ぇ', yo: 'ょ' };
const SMALL_VOWELS = { a: 'ぁ', i: 'ぃ', u: 'ぅ', e: 'ぇ', o: 'ぉ' };
// typing one should be the same as having typed the other instead
const ALIASES = {
sh: 'sy', // sha -> sya
ch: 'ty', // cho -> tyo
cy: 'ty', // cyo -> tyo
chy: 'ty', // chyu -> tyu
shy: 'sy', // shya -> sya
j: 'zy', // ja -> zya
jy: 'zy', // jye -> zye
// exceptions to above rules
shi: 'si',
chi: 'ti',
tsu: 'tu',
ji: 'zi',
fu: 'hu',
sh: 'sy',
ch: 'ty',
cy: 'ty',
chy: 'ty',
shy: 'sy',
j: 'zy',
jy: 'zy',
// exceptions to above rules
shi: 'si',
chi: 'ti',
tsu: 'tu',
ji: 'zi',
fu: 'hu',
};
// xtu -> っ
const SMALL_LETTERS = Object.assign(
{
const SMALL_LETTERS = Object.assign({
tu: 'っ',

@@ -622,161 +579,137 @@ wa: 'ゎ',

ke: 'ヶ',
},
SMALL_VOWELS,
SMALL_Y$1
);
}, SMALL_VOWELS, SMALL_Y$1);
// don't follow any notable patterns
const SPECIAL_CASES = {
yi: 'い',
wu: 'う',
ye: 'いぇ',
wi: 'うぃ',
we: 'うぇ',
kwa: 'くぁ',
whu: 'う',
// because it's not thya for てゃ but tha
// and tha is not てぁ, but てゃ
tha: 'てゃ',
thu: 'てゅ',
tho: 'てょ',
dha: 'でゃ',
dhu: 'でゅ',
dho: 'でょ',
yi: 'い',
wu: 'う',
ye: 'いぇ',
wi: 'うぃ',
we: 'うぇ',
kwa: 'くぁ',
whu: 'う',
// because it's not thya for てゃ but tha
// and tha is not てぁ, but てゃ
tha: 'てゃ',
thu: 'てゅ',
tho: 'てょ',
dha: 'でゃ',
dhu: 'でゅ',
dho: 'でょ',
};
const AIUEO_CONSTRUCTIONS = {
wh: 'う',
kw: 'く',
qw: 'く',
q: 'く',
gw: 'ぐ',
sw: 'す',
ts: 'つ',
th: 'て',
tw: 'と',
dh: 'で',
dw: 'ど',
fw: 'ふ',
f: 'ふ',
wh: 'う',
kw: 'く',
qw: 'く',
q: 'く',
gw: 'ぐ',
sw: 'す',
ts: 'つ',
th: 'て',
tw: 'と',
dh: 'で',
dw: 'ど',
fw: 'ふ',
f: 'ふ',
};
/* eslint-enable */
function createRomajiToKanaMap$1() {
const kanaTree = transform(BASIC_KUNREI);
// pseudo partial application
const subtreeOf = (string) => getSubTreeOf(kanaTree, string);
// add tya, sya, etc.
Object.entries(CONSONANTS).forEach(([consonant, yKana]) => {
Object.entries(SMALL_Y$1).forEach(([roma, kana]) => {
// for example kyo -> き + ょ
subtreeOf(consonant + roma)[''] = yKana + kana;
const kanaTree = transform(BASIC_KUNREI);
// pseudo partial application
const subtreeOf = (string) => getSubTreeOf(kanaTree, string);
// add tya, sya, etc.
Object.entries(CONSONANTS).forEach(([consonant, yKana]) => {
Object.entries(SMALL_Y$1).forEach(([roma, kana]) => {
// for example kyo -> き + ょ
subtreeOf(consonant + roma)[''] = yKana + kana;
});
});
});
Object.entries(SPECIAL_SYMBOLS$1).forEach(([symbol, jsymbol]) => {
subtreeOf(symbol)[''] = jsymbol;
});
// things like うぃ, くぃ, etc.
Object.entries(AIUEO_CONSTRUCTIONS).forEach(([consonant, aiueoKana]) => {
Object.entries(SMALL_VOWELS).forEach(([vowel, kana]) => {
const subtree = subtreeOf(consonant + vowel);
subtree[''] = aiueoKana + kana;
Object.entries(SPECIAL_SYMBOLS$1).forEach(([symbol, jsymbol]) => {
subtreeOf(symbol)[''] = jsymbol;
});
});
// different ways to write ん
['n', "n'", 'xn'].forEach((nChar) => {
subtreeOf(nChar)[''] = 'ん';
});
// c is equivalent to k, but not for chi, cha, etc. that's why we have to make a copy of k
kanaTree.c = JSON.parse(JSON.stringify(kanaTree.k));
Object.entries(ALIASES).forEach(([string, alternative]) => {
const allExceptLast = string.slice(0, string.length - 1);
const last = string.charAt(string.length - 1);
const parentTree = subtreeOf(allExceptLast);
// copy to avoid recursive containment
parentTree[last] = JSON.parse(JSON.stringify(subtreeOf(alternative)));
});
function getAlternatives(string) {
return [...Object.entries(ALIASES), ...[['c', 'k']]].reduce(
(list, [alt, roma]) => (string.startsWith(roma) ? list.concat(string.replace(roma, alt)) : list),
[]
);
}
Object.entries(SMALL_LETTERS).forEach(([kunreiRoma, kana]) => {
const last = (char) => char.charAt(char.length - 1);
const allExceptLast = (chars) => chars.slice(0, chars.length - 1);
const xRoma = `x${kunreiRoma}`;
const xSubtree = subtreeOf(xRoma);
xSubtree[''] = kana;
// ltu -> xtu -> っ
const parentTree = subtreeOf(`l${allExceptLast(kunreiRoma)}`);
parentTree[last(kunreiRoma)] = xSubtree;
// ltsu -> ltu -> っ
getAlternatives(kunreiRoma).forEach((altRoma) => {
['l', 'x'].forEach((prefix) => {
const altParentTree = subtreeOf(prefix + allExceptLast(altRoma));
altParentTree[last(altRoma)] = subtreeOf(prefix + kunreiRoma);
});
// things like うぃ, くぃ, etc.
Object.entries(AIUEO_CONSTRUCTIONS).forEach(([consonant, aiueoKana]) => {
Object.entries(SMALL_VOWELS).forEach(([vowel, kana]) => {
const subtree = subtreeOf(consonant + vowel);
subtree[''] = aiueoKana + kana;
});
});
});
Object.entries(SPECIAL_CASES).forEach(([string, kana]) => {
subtreeOf(string)[''] = kana;
});
// add kka, tta, etc.
function addTsu(tree) {
return Object.entries(tree).reduce((tsuTree, [key, value]) => {
if (!key) {
// we have reached the bottom of this branch
// eslint-disable-next-line no-param-reassign
tsuTree[key] = `っ${value}`;
} else {
// more subtrees
// eslint-disable-next-line no-param-reassign
tsuTree[key] = addTsu(value);
}
return tsuTree;
}, {});
}
// have to explicitly name c here, because we made it a copy of k, not a reference
[...Object.keys(CONSONANTS), 'c', 'y', 'w', 'j'].forEach((consonant) => {
const subtree = kanaTree[consonant];
subtree[consonant] = addTsu(subtree);
});
// nn should not be っん
delete kanaTree.n.n;
// solidify the results, so that there there is referential transparency within the tree
return Object.freeze(JSON.parse(JSON.stringify(kanaTree)));
// different ways to write ん
['n', "n'", 'xn'].forEach((nChar) => {
subtreeOf(nChar)[''] = 'ん';
});
// c is equivalent to k, but not for chi, cha, etc. that's why we have to make a copy of k
kanaTree.c = JSON.parse(JSON.stringify(kanaTree.k));
Object.entries(ALIASES).forEach(([string, alternative]) => {
const allExceptLast = string.slice(0, string.length - 1);
const last = string.charAt(string.length - 1);
const parentTree = subtreeOf(allExceptLast);
// copy to avoid recursive containment
parentTree[last] = JSON.parse(JSON.stringify(subtreeOf(alternative)));
});
function getAlternatives(string) {
return [...Object.entries(ALIASES), ...[['c', 'k']]].reduce((list, [alt, roma]) => (string.startsWith(roma) ? list.concat(string.replace(roma, alt)) : list), []);
}
Object.entries(SMALL_LETTERS).forEach(([kunreiRoma, kana]) => {
const last = (char) => char.charAt(char.length - 1);
const allExceptLast = (chars) => chars.slice(0, chars.length - 1);
const xRoma = `x${kunreiRoma}`;
const xSubtree = subtreeOf(xRoma);
xSubtree[''] = kana;
// ltu -> xtu -> っ
const parentTree = subtreeOf(`l${allExceptLast(kunreiRoma)}`);
parentTree[last(kunreiRoma)] = xSubtree;
// ltsu -> ltu -> っ
getAlternatives(kunreiRoma).forEach((altRoma) => {
['l', 'x'].forEach((prefix) => {
const altParentTree = subtreeOf(prefix + allExceptLast(altRoma));
altParentTree[last(altRoma)] = subtreeOf(prefix + kunreiRoma);
});
});
});
Object.entries(SPECIAL_CASES).forEach(([string, kana]) => {
subtreeOf(string)[''] = kana;
});
// add kka, tta, etc.
function addTsu(tree) {
return Object.entries(tree).reduce((tsuTree, [key, value]) => {
if (!key) {
// we have reached the bottom of this branch
// eslint-disable-next-line no-param-reassign
tsuTree[key] = `っ${value}`;
}
else {
// more subtrees
// eslint-disable-next-line no-param-reassign
tsuTree[key] = addTsu(value);
}
return tsuTree;
}, {});
}
// have to explicitly name c here, because we made it a copy of k, not a reference
[...Object.keys(CONSONANTS), 'c', 'y', 'w', 'j'].forEach((consonant) => {
const subtree = kanaTree[consonant];
subtree[consonant] = addTsu(subtree);
});
// nn should not be っん
delete kanaTree.n.n;
// solidify the results, so that there there is referential transparency within the tree
return Object.freeze(JSON.parse(JSON.stringify(kanaTree)));
}
let romajiToKanaMap = null;
function getRomajiToKanaTree() {
if (romajiToKanaMap == null) {
romajiToKanaMap = createRomajiToKanaMap$1();
}
return romajiToKanaMap;
if (romajiToKanaMap == null) {
romajiToKanaMap = createRomajiToKanaMap$1();
}
return romajiToKanaMap;
}
const USE_OBSOLETE_KANA_MAP = createCustomMapping({
wi: 'ゐ',
we: 'ゑ',
wi: 'ゐ',
we: 'ゑ',
});
function IME_MODE_MAP(map) {
// in IME mode, we do not want to convert single ns
const mapCopy = JSON.parse(JSON.stringify(map));
mapCopy.n.n = { '': 'ん' };
mapCopy.n[' '] = { '': 'ん' };
return mapCopy;
// in IME mode, we do not want to convert single ns
const mapCopy = JSON.parse(JSON.stringify(map));
mapCopy.n.n = { '': 'ん' };
mapCopy.n[' '] = { '': 'ん' };
return mapCopy;
}

@@ -790,4 +723,5 @@

function isCharUpperCase(char = '') {
if (isEmpty(char)) return false;
return isCharInRange(char, LATIN_UPPERCASE_START, LATIN_UPPERCASE_END);
if (isEmpty(char))
return false;
return isCharInRange(char, LATIN_UPPERCASE_START, LATIN_UPPERCASE_END);
}

@@ -801,4 +735,5 @@

function isCharLongDash(char = '') {
if (isEmpty(char)) return false;
return char.charCodeAt(0) === PROLONGED_SOUND_MARK;
if (isEmpty(char))
return false;
return char.charCodeAt(0) === PROLONGED_SOUND_MARK;
}

@@ -812,4 +747,5 @@

function isCharSlashDot(char = '') {
if (isEmpty(char)) return false;
return char.charCodeAt(0) === KANA_SLASH_DOT;
if (isEmpty(char))
return false;
return char.charCodeAt(0) === KANA_SLASH_DOT;
}

@@ -823,5 +759,7 @@

function isCharHiragana(char = '') {
if (isEmpty(char)) return false;
if (isCharLongDash(char)) return true;
return isCharInRange(char, HIRAGANA_START, HIRAGANA_END);
if (isEmpty(char))
return false;
if (isCharLongDash(char))
return true;
return isCharInRange(char, HIRAGANA_START, HIRAGANA_END);
}

@@ -842,37 +780,32 @@

function hiraganaToKatakana(input = '') {
const kata = [];
input.split('').forEach((char) => {
// Short circuit to avoid incorrect codeshift for 'ー' and '・'
if (isCharLongDash(char) || isCharSlashDot(char)) {
kata.push(char);
} else if (isCharHiragana(char)) {
// Shift charcode.
const code = char.charCodeAt(0) + (KATAKANA_START - HIRAGANA_START);
const kataChar = String.fromCharCode(code);
kata.push(kataChar);
} else {
// Pass non-hiragana chars through
kata.push(char);
}
});
return kata.join('');
const kata = [];
input.split('').forEach((char) => {
// Short circuit to avoid incorrect codeshift for 'ー' and '・'
if (isCharLongDash(char) || isCharSlashDot(char)) {
kata.push(char);
}
else if (isCharHiragana(char)) {
// Shift charcode.
const code = char.charCodeAt(0) + (KATAKANA_START - HIRAGANA_START);
const kataChar = String.fromCharCode(code);
kata.push(kataChar);
}
else {
// Pass non-hiragana chars through
kata.push(char);
}
});
return kata.join('');
}
// memoize and deeply compare args so we only recreate when necessary
const createRomajiToKanaMap = memoizeOne(
(IMEMode, useObsoleteKana, customKanaMapping) => {
const createRomajiToKanaMap = memoizeOne((IMEMode, useObsoleteKana, customKanaMapping) => {
let map = getRomajiToKanaTree();
map = IMEMode ? IME_MODE_MAP(map) : map;
map = useObsoleteKana ? USE_OBSOLETE_KANA_MAP(map) : map;
if (customKanaMapping) {
map = mergeCustomMapping(map, customKanaMapping);
map = mergeCustomMapping(map, customKanaMapping);
}
return map;
},
dequal
);
}, dequal);
/**

@@ -882,2 +815,3 @@ * Convert [Romaji](https://en.wikipedia.org/wiki/Romaji) to [Kana](https://en.wikipedia.org/wiki/Kana), lowercase text will result in [Hiragana](https://en.wikipedia.org/wiki/Hiragana) and uppercase text will result in [Katakana](https://en.wikipedia.org/wiki/Katakana).

* @param {DefaultOptions} [options=defaultOptions]
* @param {Object.<string, string>} [map] custom mapping
* @return {String} converted text

@@ -901,33 +835,27 @@ * @example

function toKana(input = '', options = {}, map) {
let config;
if (!map) {
config = mergeWithDefaultOptions(options);
map = createRomajiToKanaMap(
config.IMEMode,
config.useObsoleteKana,
config.customKanaMapping
);
} else {
config = options;
}
// throw away the substring index information and just concatenate all the kana
return splitIntoConvertedKana(input, config, map)
.map((kanaToken) => {
const [start, end, kana] = kanaToken;
if (kana === null) {
// haven't converted the end of the string, since we are in IME mode
return input.slice(start);
}
const enforceHiragana = config.IMEMode === TO_KANA_METHODS.HIRAGANA;
const enforceKatakana = config.IMEMode === TO_KANA_METHODS.KATAKANA
|| [...input.slice(start, end)].every(isCharUpperCase);
return enforceHiragana || !enforceKatakana
? kana
: hiraganaToKatakana(kana);
let config;
if (!map) {
config = mergeWithDefaultOptions(options);
map = createRomajiToKanaMap(config.IMEMode, config.useObsoleteKana, config.customKanaMapping);
}
else {
config = options;
}
// throw away the substring index information and just concatenate all the kana
return splitIntoConvertedKana(input, config, map)
.map((kanaToken) => {
const [start, end, kana] = kanaToken;
if (kana === null) {
// haven't converted the end of the string, since we are in IME mode
return input.slice(start);
}
const enforceHiragana = config.IMEMode === TO_KANA_METHODS.HIRAGANA;
const enforceKatakana = config.IMEMode === TO_KANA_METHODS.KATAKANA
|| [...input.slice(start, end)].every(isCharUpperCase);
return enforceHiragana || !enforceKatakana
? kana
: hiraganaToKatakana(kana);
})
.join('');
.join('');
}
/**

@@ -945,9 +873,7 @@ *

function splitIntoConvertedKana(input = '', options = {}, map) {
const { IMEMode, useObsoleteKana, customKanaMapping } = options;
if (!map) {
map = createRomajiToKanaMap(IMEMode, useObsoleteKana, customKanaMapping);
}
return applyMapping(input.toLowerCase(), map, !IMEMode);
const { IMEMode, useObsoleteKana, customKanaMapping } = options;
if (!map) {
map = createRomajiToKanaMap(IMEMode, useObsoleteKana, customKanaMapping);
}
return applyMapping(input.toLowerCase(), map, !IMEMode);
}

@@ -963,94 +889,74 @@

function makeOnInput(options) {
let prevInput;
// Enforce IMEMode if not already specified
const mergedConfig = Object.assign({}, mergeWithDefaultOptions(options), {
IMEMode: options.IMEMode || true,
});
const preConfiguredMap = createRomajiToKanaMap(
mergedConfig.IMEMode,
mergedConfig.useObsoleteKana,
mergedConfig.customKanaMapping
);
const triggers = [
...Object.keys(preConfiguredMap),
...Object.keys(preConfiguredMap).map((char) => char.toUpperCase()),
];
return function onInput({ target }) {
if (
target.value !== prevInput
&& target.dataset.ignoreComposition !== 'true'
) {
convertInput(target, mergedConfig, preConfiguredMap, triggers);
}
};
let prevInput;
// Enforce IMEMode if not already specified
const mergedConfig = Object.assign({}, mergeWithDefaultOptions(options), {
IMEMode: options.IMEMode || true,
});
const preConfiguredMap = createRomajiToKanaMap(mergedConfig.IMEMode, mergedConfig.useObsoleteKana, mergedConfig.customKanaMapping);
const triggers = [
...Object.keys(preConfiguredMap),
...Object.keys(preConfiguredMap).map((char) => char.toUpperCase()),
];
return function onInput({ target }) {
if (target.value !== prevInput
&& target.dataset.ignoreComposition !== 'true') {
convertInput(target, mergedConfig, preConfiguredMap, triggers);
}
};
}
function convertInput(target, options, map, triggers, prevInput) {
const [head, textToConvert, tail] = splitInput(
target.value,
target.selectionEnd,
triggers
);
const convertedText = toKana(textToConvert, options, map);
const changed = textToConvert !== convertedText;
if (changed) {
const newCursor = head.length + convertedText.length;
const newValue = head + convertedText + tail;
// eslint-disable-next-line no-param-reassign
target.value = newValue;
if (tail.length) {
// push later on event loop (otherwise mid-text insertion can be 1 char too far to the right)
setTimeout(() => target.setSelectionRange(newCursor, newCursor), 1);
} else {
target.setSelectionRange(newCursor, newCursor);
const [head, textToConvert, tail] = splitInput(target.value, target.selectionEnd, triggers);
const convertedText = toKana(textToConvert, options, map);
const changed = textToConvert !== convertedText;
if (changed) {
const newCursor = head.length + convertedText.length;
const newValue = head + convertedText + tail;
// eslint-disable-next-line no-param-reassign
target.value = newValue;
if (tail.length) {
// push later on event loop (otherwise mid-text insertion can be 1 char too far to the right)
setTimeout(() => target.setSelectionRange(newCursor, newCursor), 1);
}
else {
target.setSelectionRange(newCursor, newCursor);
}
}
}
else {
// eslint-disable-next-line no-param-reassign
target.value;
}
}
function onComposition({ type, target, data }) {
// navigator.platform is not 100% reliable for singling out all OS,
// but for determining desktop "Mac OS" it is effective enough.
const isMacOS = /Mac/.test(window.navigator && window.navigator.platform);
// We don't want to ignore on Android:
// https://github.com/WaniKani/WanaKana/issues/82
// But MacOS IME auto-closes if we don't ignore:
// https://github.com/WaniKani/WanaKana/issues/71
// Other platform Japanese IMEs pass through happily
if (isMacOS) {
if (type === 'compositionupdate' && isJapanese(data)) {
// eslint-disable-next-line no-param-reassign
target.dataset.ignoreComposition = 'true';
// navigator.platform is not 100% reliable for singling out all OS,
// but for determining desktop "Mac OS" it is effective enough.
const isMacOS = /Mac/.test(window.navigator && window.navigator.platform);
// We don't want to ignore on Android:
// https://github.com/WaniKani/WanaKana/issues/82
// But MacOS IME auto-closes if we don't ignore:
// https://github.com/WaniKani/WanaKana/issues/71
// Other platform Japanese IMEs pass through happily
if (isMacOS) {
if (type === 'compositionupdate' && isJapanese(data)) {
// eslint-disable-next-line no-param-reassign
target.dataset.ignoreComposition = 'true';
}
if (type === 'compositionend') {
// eslint-disable-next-line no-param-reassign
target.dataset.ignoreComposition = 'false';
}
}
if (type === 'compositionend') {
// eslint-disable-next-line no-param-reassign
target.dataset.ignoreComposition = 'false';
}
}
}
function trackListeners(id, inputHandler, compositionHandler) {
LISTENERS = LISTENERS.concat({
id,
inputHandler,
compositionHandler,
});
LISTENERS = LISTENERS.concat({
id,
inputHandler,
compositionHandler,
});
}
function untrackListeners({ id: targetId }) {
LISTENERS = LISTENERS.filter(({ id }) => id !== targetId);
LISTENERS = LISTENERS.filter(({ id }) => id !== targetId);
}
function findListeners(el) {
return (
el && LISTENERS.find(({ id }) => id === el.getAttribute('data-wanakana-id'))
);
return (el && LISTENERS.find(({ id }) => id === el.getAttribute('data-wanakana-id')));
}
// Handle non-terminal inserted input conversion:

@@ -1061,58 +967,43 @@ // | -> わ| -> わび| -> わ|び -> わs|び -> わsh|び -> わshi|び -> わし|び

function splitInput(text = '', cursor = 0, triggers = []) {
let head;
let toConvert;
let tail;
if (cursor === 0 && triggers.includes(text[0])) {
[head, toConvert, tail] = workFromStart(text, triggers);
} else if (cursor > 0) {
[head, toConvert, tail] = workBackwards(text, cursor);
} else {
[head, toConvert] = takeWhileAndSlice(
text,
(char) => !triggers.includes(char)
);
[toConvert, tail] = takeWhileAndSlice(
toConvert,
(char) => !isJapanese(char)
);
}
return [head, toConvert, tail];
let head;
let toConvert;
let tail;
if (cursor === 0 && triggers.includes(text[0])) {
[head, toConvert, tail] = workFromStart(text, triggers);
}
else if (cursor > 0) {
[head, toConvert, tail] = workBackwards(text, cursor);
}
else {
[head, toConvert] = takeWhileAndSlice(text, (char) => !triggers.includes(char));
[toConvert, tail] = takeWhileAndSlice(toConvert, (char) => !isJapanese(char));
}
return [head, toConvert, tail];
}
function workFromStart(text, catalystChars) {
return [
'',
...takeWhileAndSlice(
text,
(char) => catalystChars.includes(char) || !isJapanese(char, /[0-9]/)
),
];
return [
'',
...takeWhileAndSlice(text, (char) => catalystChars.includes(char) || !isJapanese(char, /[0-9]/)),
];
}
function workBackwards(text = '', startIndex = 0) {
const [toConvert, head] = takeWhileAndSlice(
[...text.slice(0, startIndex)].reverse(),
(char) => !isJapanese(char)
);
return [
head.reverse().join(''),
toConvert
.split('')
.reverse()
.join(''),
text.slice(startIndex),
];
const [toConvert, head] = takeWhileAndSlice([...text.slice(0, startIndex)].reverse(), (char) => !isJapanese(char));
return [
head.reverse().join(''),
toConvert
.split('')
.reverse()
.join(''),
text.slice(startIndex),
];
}
function takeWhileAndSlice(source = {}, predicate = (x) => !!x) {
const result = [];
const { length } = source;
let i = 0;
while (i < length && predicate(source[i], i)) {
result.push(source[i]);
i += 1;
}
return [result.join(''), source.slice(i)];
const result = [];
const { length } = source;
let i = 0;
while (i < length && predicate(source[i], i)) {
result.push(source[i]);
i += 1;
}
return [result.join(''), source.slice(i)];
}

@@ -1123,42 +1014,32 @@

const onCompositionStart = () => console.log('compositionstart');
const onCompositionUpdate = ({
target: { value, selectionStart, selectionEnd },
data,
}) => console.log('compositionupdate', {
data,
value,
selectionStart,
selectionEnd,
const onCompositionUpdate = ({ target: { value, selectionStart, selectionEnd }, data, }) => console.log('compositionupdate', {
data,
value,
selectionStart,
selectionEnd,
});
const onCompositionEnd = () => console.log('compositionend');
const events = {
input: onInput,
compositionstart: onCompositionStart,
compositionupdate: onCompositionUpdate,
compositionend: onCompositionEnd,
input: onInput,
compositionstart: onCompositionStart,
compositionupdate: onCompositionUpdate,
compositionend: onCompositionEnd,
};
const addDebugListeners = (input) => {
Object.entries(events).forEach(([event, handler]) => input.addEventListener(event, handler)
);
Object.entries(events).forEach(([event, handler]) => input.addEventListener(event, handler));
};
const removeDebugListeners = (input) => {
Object.entries(events).forEach(([event, handler]) => input.removeEventListener(event, handler)
);
Object.entries(events).forEach(([event, handler]) => input.removeEventListener(event, handler));
};
const ELEMENTS = ['TEXTAREA', 'INPUT'];
let idCounter = 0;
const newId = () => {
idCounter += 1;
return `${Date.now()}${idCounter}`;
idCounter += 1;
return `${Date.now()}${idCounter}`;
};
/**
* Binds eventListener for 'input' events to an input field to automagically replace values with kana
* Can pass `{ IMEMode: 'toHiragana' || 'toKatakana' }` to enforce kana conversion type
* @param {HTMLElement} element textarea, input[type="text"] etc
* @param {HTMLInputElement | HTMLTextAreaElement} element textarea, input[type="text"] etc
* @param {DefaultOptions} [options=defaultOptions] defaults to { IMEMode: true } using `toKana`

@@ -1169,35 +1050,31 @@ * @example

function bind(element = {}, options = {}, debug = false) {
if (!ELEMENTS.includes(element.nodeName)) {
throw new Error(
`Element provided to Wanakana bind() was not a valid input or textarea element.\n Received: (${JSON.stringify(
element
)})`
);
}
if (element.hasAttribute('data-wanakana-id')) {
return;
}
const onInput = makeOnInput(options);
const id = newId();
const attributes = [
{ name: 'data-wanakana-id', value: id },
{ name: 'lang', value: 'ja' },
{ name: 'autoCapitalize', value: 'none' },
{ name: 'autoCorrect', value: 'off' },
{ name: 'autoComplete', value: 'off' },
{ name: 'spellCheck', value: 'false' },
];
const previousAttributes = {};
attributes.forEach((attribute) => {
previousAttributes[attribute.name] = element.getAttribute(attribute.name);
element.setAttribute(attribute.name, attribute.value);
});
element.dataset.previousAttributes = JSON.stringify(previousAttributes);
element.addEventListener('input', onInput);
element.addEventListener('compositionupdate', onComposition);
element.addEventListener('compositionend', onComposition);
trackListeners(id, onInput, onComposition);
if (debug === true) {
addDebugListeners(element);
}
if (!ELEMENTS.includes(element.nodeName)) {
throw new Error(`Element provided to Wanakana bind() was not a valid input or textarea element.\n Received: (${JSON.stringify(element)})`);
}
if (element.hasAttribute('data-wanakana-id')) {
return;
}
const onInput = makeOnInput(options);
const id = newId();
const attributes = [
{ name: 'data-wanakana-id', value: id },
{ name: 'lang', value: 'ja' },
{ name: 'autoCapitalize', value: 'none' },
{ name: 'autoCorrect', value: 'off' },
{ name: 'autoComplete', value: 'off' },
{ name: 'spellCheck', value: 'false' },
];
const previousAttributes = {};
attributes.forEach((attribute) => {
previousAttributes[attribute.name] = element.getAttribute(attribute.name);
element.setAttribute(attribute.name, attribute.value);
});
element.dataset.previousAttributes = JSON.stringify(previousAttributes);
element.addEventListener('input', onInput);
element.addEventListener('compositionupdate', onComposition);
element.addEventListener('compositionend', onComposition);
trackListeners(id, onInput, onComposition);
if (debug === true) {
addDebugListeners(element);
}
}

@@ -1207,32 +1084,29 @@

* Unbinds eventListener from input field
* @param {HTMLElement} element textarea, input
* @param {HTMLInputElement | HTMLTextAreaElement} element textarea, input
*/
function unbind(element, debug = false) {
const listeners = findListeners(element);
if (listeners == null) {
throw new Error(
`Element provided to Wanakana unbind() had no listener registered.\n Received: ${JSON.stringify(
element
)}`
);
}
const { inputHandler, compositionHandler } = listeners;
const attributes = JSON.parse(element.dataset.previousAttributes);
Object.keys(attributes).forEach((key) => {
if (attributes[key]) {
element.setAttribute(key, attributes[key]);
} else {
element.removeAttribute(key);
const listeners = findListeners(element);
if (listeners == null) {
throw new Error(`Element provided to Wanakana unbind() had no listener registered.\n Received: ${JSON.stringify(element)}`);
}
});
element.removeAttribute('data-previous-attributes');
element.removeAttribute('data-ignore-composition');
element.removeEventListener('input', inputHandler);
element.removeEventListener('compositionstart', compositionHandler);
element.removeEventListener('compositionupdate', compositionHandler);
element.removeEventListener('compositionend', compositionHandler);
untrackListeners(listeners);
if (debug === true) {
removeDebugListeners(element);
}
const { inputHandler, compositionHandler } = listeners;
const attributes = JSON.parse(element.dataset.previousAttributes);
Object.keys(attributes).forEach((key) => {
if (attributes[key]) {
element.setAttribute(key, attributes[key]);
}
else {
element.removeAttribute(key);
}
});
element.removeAttribute('data-previous-attributes');
element.removeAttribute('data-ignore-composition');
element.removeEventListener('input', inputHandler);
element.removeEventListener('compositionstart', compositionHandler);
element.removeEventListener('compositionupdate', compositionHandler);
element.removeEventListener('compositionend', compositionHandler);
untrackListeners(listeners);
if (debug === true) {
removeDebugListeners(element);
}
}

@@ -1246,4 +1120,5 @@

function isCharRomaji(char = '') {
if (isEmpty(char)) return false;
return ROMAJI_RANGES.some(([start, end]) => isCharInRange(char, start, end));
if (isEmpty(char))
return false;
return ROMAJI_RANGES.some(([start, end]) => isCharInRange(char, start, end));
}

@@ -1254,3 +1129,3 @@

* @param {String} [input=''] text
* @param {Regexp} [allowed] additional test allowed to pass for each char
* @param {RegExp} [allowed] additional test allowed to pass for each char
* @return {Boolean} true if [Romaji](https://en.wikipedia.org/wiki/Romaji)

@@ -1272,9 +1147,9 @@ * @example

function isRomaji(input = '', allowed) {
const augmented = typeOf(allowed) === 'regexp';
return isEmpty(input)
? false
: [...input].every((char) => {
const isRoma = isCharRomaji(char);
return !augmented ? isRoma : isRoma || allowed.test(char);
});
const augmented = typeOf(allowed) === 'regexp';
return isEmpty(input)
? false
: [...input].every((char) => {
const isRoma = isCharRomaji(char);
return !augmented ? isRoma : isRoma || allowed.test(char);
});
}

@@ -1288,3 +1163,3 @@

function isCharKatakana(char = '') {
return isCharInRange(char, KATAKANA_START, KATAKANA_END);
return isCharInRange(char, KATAKANA_START, KATAKANA_END);
}

@@ -1298,4 +1173,5 @@

function isCharKana(char = '') {
if (isEmpty(char)) return false;
return isCharHiragana(char) || isCharKatakana(char);
if (isEmpty(char))
return false;
return isCharHiragana(char) || isCharKatakana(char);
}

@@ -1320,4 +1196,5 @@

function isKana(input = '') {
if (isEmpty(input)) return false;
return [...input].every(isCharKana);
if (isEmpty(input))
return false;
return [...input].every(isCharKana);
}

@@ -1338,4 +1215,5 @@

function isHiragana(input = '') {
if (isEmpty(input)) return false;
return [...input].every(isCharHiragana);
if (isEmpty(input))
return false;
return [...input].every(isCharHiragana);
}

@@ -1358,4 +1236,5 @@

function isKatakana(input = '') {
if (isEmpty(input)) return false;
return [...input].every(isCharKatakana);
if (isEmpty(input))
return false;
return [...input].every(isCharKatakana);
}

@@ -1369,4 +1248,5 @@

function isCharIterationMark(char = '') {
if (isEmpty(char)) return false;
return char.charCodeAt(0) === KANJI_ITERATION_MARK;
if (isEmpty(char))
return false;
return char.charCodeAt(0) === KANJI_ITERATION_MARK;
}

@@ -1380,3 +1260,3 @@

function isCharKanji(char = '') {
return isCharInRange(char, KANJI_START, KANJI_END) || isCharIterationMark(char);
return isCharInRange(char, KANJI_START, KANJI_END) || isCharIterationMark(char);
}

@@ -1401,4 +1281,5 @@

function isKanji(input = '') {
if (isEmpty(input)) return false;
return [...input].every(isCharKanji);
if (isEmpty(input))
return false;
return [...input].every(isCharKanji);
}

@@ -1409,3 +1290,3 @@

* @param {String} input text
* @param {Object} [options={ passKanji: true }] optional config to pass through kanji
* @param {{ passKanji: Boolean}} [options={ passKanji: true }] optional config to pass through kanji
* @return {Boolean} true if mixed

@@ -1425,8 +1306,8 @@ * @example

function isMixed(input = '', options = { passKanji: true }) {
const chars = [...input];
let hasKanji = false;
if (!options.passKanji) {
hasKanji = chars.some(isKanji);
}
return (chars.some(isHiragana) || chars.some(isKatakana)) && chars.some(isRomaji) && !hasKanji;
const chars = [...input];
let hasKanji = false;
if (!options.passKanji) {
hasKanji = chars.some(isKanji);
}
return (chars.some(isHiragana) || chars.some(isKatakana)) && chars.some(isRomaji) && !hasKanji;
}

@@ -1438,110 +1319,92 @@

const LONG_VOWELS = {
a: 'あ',
i: 'い',
u: 'う',
e: 'え',
o: 'う',
a: 'あ',
i: 'い',
u: 'う',
e: 'え',
o: 'う',
};
// inject toRomaji to avoid circular dependency between toRomaji <-> katakanaToHiragana
function katakanaToHiragana(
input = '',
toRomaji,
{ isDestinationRomaji, convertLongVowelMark } = {}
) {
let previousKana = '';
return input
.split('')
.reduce((hira, char, index) => {
// Short circuit to avoid incorrect codeshift for 'ー' and '・'
if (
isCharSlashDot(char)
|| isCharInitialLongDash(char, index)
|| isKanaAsSymbol(char)
) {
function katakanaToHiragana(input = '', toRomaji, { isDestinationRomaji, convertLongVowelMark } = {}) {
let previousKana = '';
return input
.split('')
.reduce((hira, char, index) => {
// Short circuit to avoid incorrect codeshift for 'ー' and '・'
if (isCharSlashDot(char)
|| isCharInitialLongDash(char, index)
|| isKanaAsSymbol(char)) {
return hira.concat(char);
}
// Transform long vowels: 'オー' to 'おう'
if (convertLongVowelMark
&& previousKana
&& isCharInnerLongDash(char, index)) {
// Transform previousKana back to romaji, and slice off the vowel
const romaji = toRomaji(previousKana).slice(-1);
// However, ensure 'オー' => 'おお' => 'oo' if this is a transform on the way to romaji
if (isCharKatakana(input[index - 1])
&& romaji === 'o'
&& isDestinationRomaji) {
return hira.concat('お');
}
return hira.concat(LONG_VOWELS[romaji]);
// Transform all other chars
}
if (!isCharLongDash(char) && isCharKatakana(char)) {
const code = char.charCodeAt(0) + (HIRAGANA_START - KATAKANA_START);
const hiraChar = String.fromCharCode(code);
previousKana = hiraChar;
return hira.concat(hiraChar);
}
// Pass non katakana chars through
previousKana = '';
return hira.concat(char);
}
// Transform long vowels: 'オー' to 'おう'
if (
convertLongVowelMark
&& previousKana
&& isCharInnerLongDash(char, index)
) {
// Transform previousKana back to romaji, and slice off the vowel
const romaji = toRomaji(previousKana).slice(-1);
// However, ensure 'オー' => 'おお' => 'oo' if this is a transform on the way to romaji
if (
isCharKatakana(input[index - 1])
&& romaji === 'o'
&& isDestinationRomaji
) {
return hira.concat('お');
}
return hira.concat(LONG_VOWELS[romaji]);
// Transform all other chars
}
if (!isCharLongDash(char) && isCharKatakana(char)) {
const code = char.charCodeAt(0) + (HIRAGANA_START - KATAKANA_START);
const hiraChar = String.fromCharCode(code);
previousKana = hiraChar;
return hira.concat(hiraChar);
}
// Pass non katakana chars through
previousKana = '';
return hira.concat(char);
}, [])
.join('');
.join('');
}
let kanaToHepburnMap = null;
/* eslint-disable */
// prettier-ignore
const BASIC_ROMAJI = {
あ:'a', い:'i', う:'u', え:'e', お:'o',
か:'ka', き:'ki', く:'ku', け:'ke', こ:'ko',
さ:'sa', し:'shi', す:'su', せ:'se', そ:'so',
た:'ta', ち:'chi', つ:'tsu', て:'te', と:'to',
な:'na', に:'ni', ぬ:'nu', ね:'ne', の:'no',
は:'ha', ひ:'hi', ふ:'fu', へ:'he', ほ:'ho',
ま:'ma', み:'mi', む:'mu', め:'me', も:'mo',
ら:'ra', り:'ri', る:'ru', れ:'re', ろ:'ro',
や:'ya', ゆ:'yu', よ:'yo',
わ:'wa', ゐ:'wi', ゑ:'we', を:'wo',
ん: 'n',
が:'ga', ぎ:'gi', ぐ:'gu', げ:'ge', ご:'go',
ざ:'za', じ:'ji', ず:'zu', ぜ:'ze', ぞ:'zo',
だ:'da', ぢ:'ji', づ:'zu', で:'de', ど:'do',
ば:'ba', び:'bi', ぶ:'bu', べ:'be', ぼ:'bo',
ぱ:'pa', ぴ:'pi', ぷ:'pu', ぺ:'pe', ぽ:'po',
ゔぁ:'va', ゔぃ:'vi', ゔ:'vu', ゔぇ:'ve', ゔぉ:'vo',
あ: 'a', い: 'i', う: 'u', え: 'e', お: 'o',
か: 'ka', き: 'ki', く: 'ku', け: 'ke', こ: 'ko',
さ: 'sa', し: 'shi', す: 'su', せ: 'se', そ: 'so',
た: 'ta', ち: 'chi', つ: 'tsu', て: 'te', と: 'to',
な: 'na', に: 'ni', ぬ: 'nu', ね: 'ne', の: 'no',
は: 'ha', ひ: 'hi', ふ: 'fu', へ: 'he', ほ: 'ho',
ま: 'ma', み: 'mi', む: 'mu', め: 'me', も: 'mo',
ら: 'ra', り: 'ri', る: 'ru', れ: 're', ろ: 'ro',
や: 'ya', ゆ: 'yu', よ: 'yo',
わ: 'wa', ゐ: 'wi', ゑ: 'we', を: 'wo',
ん: 'n',
が: 'ga', ぎ: 'gi', ぐ: 'gu', げ: 'ge', ご: 'go',
ざ: 'za', じ: 'ji', ず: 'zu', ぜ: 'ze', ぞ: 'zo',
だ: 'da', ぢ: 'ji', づ: 'zu', で: 'de', ど: 'do',
ば: 'ba', び: 'bi', ぶ: 'bu', べ: 'be', ぼ: 'bo',
ぱ: 'pa', ぴ: 'pi', ぷ: 'pu', ぺ: 'pe', ぽ: 'po',
ゔぁ: 'va', ゔぃ: 'vi', ゔ: 'vu', ゔぇ: 've', ゔぉ: 'vo',
};
/* eslint-enable */
const SPECIAL_SYMBOLS = {
'。': '.',
'、': ',',
':': ':',
'・': '/',
'!': '!',
'?': '?',
'〜': '~',
'ー': '-',
'「': '‘',
'」': '’',
'『': '“',
'』': '”',
'[': '[',
']': ']',
'(': '(',
')': ')',
'{': '{',
'}': '}',
' ': ' ',
'。': '.',
'、': ',',
':': ':',
'・': '/',
'!': '!',
'?': '?',
'〜': '~',
'ー': '-',
'「': '‘',
'」': '’',
'『': '“',
'』': '”',
'[': '[',
']': ']',
'(': '(',
')': ')',
'{': '{',
'}': '}',
' ': ' ',
};
// んい -> n'i

@@ -1552,174 +1415,152 @@ const AMBIGUOUS_VOWELS = ['あ', 'い', 'う', 'え', 'お', 'や', 'ゆ', 'よ'];

const SMALL_AIUEO = {
ぁ: 'a',
ぃ: 'i',
ぅ: 'u',
ぇ: 'e',
ぉ: 'o',
ぁ: 'a',
ぃ: 'i',
ぅ: 'u',
ぇ: 'e',
ぉ: 'o',
};
const YOON_KANA = [
'き',
'に',
'ひ',
'み',
'り',
'ぎ',
'び',
'ぴ',
'ゔ',
'く',
'ふ',
'き',
'に',
'ひ',
'み',
'り',
'ぎ',
'び',
'ぴ',
'ゔ',
'く',
'ふ',
];
const YOON_EXCEPTIONS = {
し: 'sh',
ち: 'ch',
じ: 'j',
ぢ: 'j',
し: 'sh',
ち: 'ch',
じ: 'j',
ぢ: 'j',
};
const SMALL_KANA = {
っ: '',
ゃ: 'ya',
ゅ: 'yu',
ょ: 'yo',
ぁ: 'a',
ぃ: 'i',
ぅ: 'u',
ぇ: 'e',
ぉ: 'o',
っ: '',
ゃ: 'ya',
ゅ: 'yu',
ょ: 'yo',
ぁ: 'a',
ぃ: 'i',
ぅ: 'u',
ぇ: 'e',
ぉ: 'o',
};
// going with the intuitive (yet incorrect) solution where っや -> yya and っぃ -> ii
// in other words, just assume the sokuon could have been applied to anything
const SOKUON_WHITELIST = {
b: 'b',
c: 't',
d: 'd',
f: 'f',
g: 'g',
h: 'h',
j: 'j',
k: 'k',
m: 'm',
p: 'p',
q: 'q',
r: 'r',
s: 's',
t: 't',
v: 'v',
w: 'w',
x: 'x',
z: 'z',
b: 'b',
c: 't',
d: 'd',
f: 'f',
g: 'g',
h: 'h',
j: 'j',
k: 'k',
m: 'm',
p: 'p',
q: 'q',
r: 'r',
s: 's',
t: 't',
v: 'v',
w: 'w',
x: 'x',
z: 'z',
};
function getKanaToHepburnTree() {
if (kanaToHepburnMap == null) {
kanaToHepburnMap = createKanaToHepburnMap();
}
return kanaToHepburnMap;
if (kanaToHepburnMap == null) {
kanaToHepburnMap = createKanaToHepburnMap();
}
return kanaToHepburnMap;
}
function getKanaToRomajiTree(romanization) {
switch (romanization) {
case ROMANIZATIONS.HEPBURN:
return getKanaToHepburnTree();
default:
return {};
}
switch (romanization) {
case ROMANIZATIONS.HEPBURN:
return getKanaToHepburnTree();
default:
return {};
}
}
function createKanaToHepburnMap() {
const romajiTree = transform(BASIC_ROMAJI);
const subtreeOf = (string) => getSubTreeOf(romajiTree, string);
const setTrans = (string, transliteration) => {
subtreeOf(string)[''] = transliteration;
};
Object.entries(SPECIAL_SYMBOLS).forEach(([jsymbol, symbol]) => {
subtreeOf(jsymbol)[''] = symbol;
});
[...Object.entries(SMALL_Y), ...Object.entries(SMALL_AIUEO)].forEach(
([roma, kana]) => {
setTrans(roma, kana);
}
);
// きゃ -> kya
YOON_KANA.forEach((kana) => {
const firstRomajiChar = subtreeOf(kana)[''][0];
Object.entries(SMALL_Y).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, firstRomajiChar + yRoma);
const romajiTree = transform(BASIC_ROMAJI);
const subtreeOf = (string) => getSubTreeOf(romajiTree, string);
const setTrans = (string, transliteration) => {
subtreeOf(string)[''] = transliteration;
};
Object.entries(SPECIAL_SYMBOLS).forEach(([jsymbol, symbol]) => {
subtreeOf(jsymbol)[''] = symbol;
});
// きぃ -> kyi
Object.entries(SMALL_Y_EXTRA).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, firstRomajiChar + yRoma);
[...Object.entries(SMALL_Y), ...Object.entries(SMALL_AIUEO)].forEach(([roma, kana]) => {
setTrans(roma, kana);
});
});
Object.entries(YOON_EXCEPTIONS).forEach(([kana, roma]) => {
// じゃ -> ja
Object.entries(SMALL_Y).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, roma + yRoma[1]);
// きゃ -> kya
YOON_KANA.forEach((kana) => {
const firstRomajiChar = subtreeOf(kana)[''][0];
Object.entries(SMALL_Y).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, firstRomajiChar + yRoma);
});
// きぃ -> kyi
Object.entries(SMALL_Y_EXTRA).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, firstRomajiChar + yRoma);
});
});
// じぃ -> jyi, じぇ -> je
setTrans(`${kana}ぃ`, `${roma}yi`);
setTrans(`${kana}ぇ`, `${roma}e`);
});
romajiTree['っ'] = resolveTsu(romajiTree);
Object.entries(SMALL_KANA).forEach(([kana, roma]) => {
setTrans(kana, roma);
});
AMBIGUOUS_VOWELS.forEach((kana) => {
setTrans(`ん${kana}`, `n'${subtreeOf(kana)['']}`);
});
// NOTE: could be re-enabled with an option?
// // んば -> mbo
// const LABIAL = [
// 'ば', 'び', 'ぶ', 'べ', 'ぼ',
// 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ',
// 'ま', 'み', 'む', 'め', 'も',
// ];
// LABIAL.forEach((kana) => {
// setTrans(`ん${kana}`, `m${subtreeOf(kana)['']}`);
// });
return Object.freeze(JSON.parse(JSON.stringify(romajiTree)));
Object.entries(YOON_EXCEPTIONS).forEach(([kana, roma]) => {
// じゃ -> ja
Object.entries(SMALL_Y).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, roma + yRoma[1]);
});
// じぃ -> jyi, じぇ -> je
setTrans(`${kana}ぃ`, `${roma}yi`);
setTrans(`${kana}ぇ`, `${roma}e`);
});
romajiTree['っ'] = resolveTsu(romajiTree);
Object.entries(SMALL_KANA).forEach(([kana, roma]) => {
setTrans(kana, roma);
});
AMBIGUOUS_VOWELS.forEach((kana) => {
setTrans(`ん${kana}`, `n'${subtreeOf(kana)['']}`);
});
// NOTE: could be re-enabled with an option?
// // んば -> mbo
// const LABIAL = [
// 'ば', 'び', 'ぶ', 'べ', 'ぼ',
// 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ',
// 'ま', 'み', 'む', 'め', 'も',
// ];
// LABIAL.forEach((kana) => {
// setTrans(`ん${kana}`, `m${subtreeOf(kana)['']}`);
// });
return Object.freeze(JSON.parse(JSON.stringify(romajiTree)));
}
function resolveTsu(tree) {
return Object.entries(tree).reduce((tsuTree, [key, value]) => {
if (!key) {
// we have reached the bottom of this branch
const consonant = value.charAt(0);
// eslint-disable-next-line no-param-reassign
tsuTree[key] = Object.keys(SOKUON_WHITELIST).includes(consonant)
? SOKUON_WHITELIST[consonant] + value
: value;
} else {
// more subtrees
// eslint-disable-next-line no-param-reassign
tsuTree[key] = resolveTsu(value);
}
return tsuTree;
}, {});
return Object.entries(tree).reduce((tsuTree, [key, value]) => {
if (!key) {
// we have reached the bottom of this branch
const consonant = value.charAt(0);
// eslint-disable-next-line no-param-reassign
tsuTree[key] = Object.keys(SOKUON_WHITELIST).includes(consonant)
? SOKUON_WHITELIST[consonant] + value
: value;
}
else {
// more subtrees
// eslint-disable-next-line no-param-reassign
tsuTree[key] = resolveTsu(value);
}
return tsuTree;
}, {});
}
// memoize and deeply compare args so we only recreate when necessary
const createKanaToRomajiMap = memoizeOne(
(romanization, customRomajiMapping) => {
const createKanaToRomajiMap = memoizeOne((romanization, customRomajiMapping) => {
let map = getKanaToRomajiTree(romanization);
if (customRomajiMapping) {
map = mergeCustomMapping(map, customRomajiMapping);
map = mergeCustomMapping(map, customRomajiMapping);
}
return map;
},
dequal
);
}, dequal);
/**

@@ -1729,3 +1570,3 @@ * Convert kana to romaji

* @param {DefaultOptions} [options=defaultOptions]
* @param {Object} map custom mapping
* @param {Object.<string, string>} [map] custom mapping
* @return {String} converted text

@@ -1743,36 +1584,21 @@ * @example

function toRomaji(input = '', options = {}, map) {
const config = mergeWithDefaultOptions(options);
if (!map) {
map = createKanaToRomajiMap(
config.romanization,
config.customRomajiMapping
);
}
// just throw away the substring index information and simply concatenate all the kana
return splitIntoRomaji(input, config, map)
.map((romajiToken) => {
const [start, end, romaji] = romajiToken;
const makeUpperCase = config.upcaseKatakana && isKatakana(input.slice(start, end));
return makeUpperCase ? romaji.toUpperCase() : romaji;
const config = mergeWithDefaultOptions(options);
if (!map) {
map = createKanaToRomajiMap(config.romanization, config.customRomajiMapping);
}
// just throw away the substring index information and simply concatenate all the kana
return splitIntoRomaji(input, config, map)
.map((romajiToken) => {
const [start, end, romaji] = romajiToken;
const makeUpperCase = config.upcaseKatakana && isKatakana(input.slice(start, end));
return makeUpperCase ? romaji.toUpperCase() : romaji;
})
.join('');
.join('');
}
function splitIntoRomaji(input, options, map) {
if (!map) {
map = createKanaToRomajiMap(
options.romanization,
options.customRomajiMapping
);
}
const config = Object.assign({}, { isDestinationRomaji: true }, options);
return applyMapping(
katakanaToHiragana(input, toRomaji, config),
map,
!options.IMEMode
);
if (!map) {
map = createKanaToRomajiMap(options.romanization, options.customRomajiMapping);
}
const config = Object.assign({}, { isDestinationRomaji: true }, options);
return applyMapping(katakanaToHiragana(input, toRomaji, config), map, !options.IMEMode);
}

@@ -1786,4 +1612,5 @@

function isCharEnglishPunctuation(char = '') {
if (isEmpty(char)) return false;
return EN_PUNCTUATION_RANGES.some(([start, end]) => isCharInRange(char, start, end));
if (isEmpty(char))
return false;
return EN_PUNCTUATION_RANGES.some(([start, end]) => isCharInRange(char, start, end));
}

@@ -1807,17 +1634,14 @@

function toHiragana(input = '', options = {}) {
const config = mergeWithDefaultOptions(options);
if (config.passRomaji) {
const config = mergeWithDefaultOptions(options);
if (config.passRomaji) {
return katakanaToHiragana(input, toRomaji, config);
}
if (isMixed(input, { passKanji: true })) {
const convertedKatakana = katakanaToHiragana(input, toRomaji, config);
return toKana(convertedKatakana.toLowerCase(), config);
}
if (isRomaji(input) || isCharEnglishPunctuation(input)) {
return toKana(input.toLowerCase(), config);
}
return katakanaToHiragana(input, toRomaji, config);
}
if (isMixed(input, { passKanji: true })) {
const convertedKatakana = katakanaToHiragana(input, toRomaji, config);
return toKana(convertedKatakana.toLowerCase(), config);
}
if (isRomaji(input) || isCharEnglishPunctuation(input)) {
return toKana(input.toLowerCase(), config);
}
return katakanaToHiragana(input, toRomaji, config);
}

@@ -1841,13 +1665,11 @@

function toKatakana(input = '', options = {}) {
const mergedOptions = mergeWithDefaultOptions(options);
if (mergedOptions.passRomaji) {
const mergedOptions = mergeWithDefaultOptions(options);
if (mergedOptions.passRomaji) {
return hiraganaToKatakana(input);
}
if (isMixed(input) || isRomaji(input) || isCharEnglishPunctuation(input)) {
const hiragana = toKana(input.toLowerCase(), mergedOptions);
return hiraganaToKatakana(hiragana);
}
return hiraganaToKatakana(input);
}
if (isMixed(input) || isRomaji(input) || isCharEnglishPunctuation(input)) {
const hiragana = toKana(input.toLowerCase(), mergedOptions);
return hiraganaToKatakana(hiragana);
}
return hiraganaToKatakana(input);
}

@@ -1861,4 +1683,5 @@

function isCharJapanesePunctuation(char = '') {
if (isEmpty(char) || isCharIterationMark(char)) return false;
return JA_PUNCTUATION_RANGES.some(([start, end]) => isCharInRange(char, start, end));
if (isEmpty(char) || isCharIterationMark(char))
return false;
return JA_PUNCTUATION_RANGES.some(([start, end]) => isCharInRange(char, start, end));
}

@@ -1870,53 +1693,48 @@

const isCharEnNum = (x) => /[0-9]/.test(x);
const TOKEN_TYPES = {
EN: 'en',
JA: 'ja',
EN_NUM: 'englishNumeral',
JA_NUM: 'japaneseNumeral',
EN_PUNC: 'englishPunctuation',
JA_PUNC: 'japanesePunctuation',
KANJI: 'kanji',
HIRAGANA: 'hiragana',
KATAKANA: 'katakana',
SPACE: 'space',
OTHER: 'other',
EN: 'en',
JA: 'ja',
EN_NUM: 'englishNumeral',
JA_NUM: 'japaneseNumeral',
EN_PUNC: 'englishPunctuation',
JA_PUNC: 'japanesePunctuation',
KANJI: 'kanji',
HIRAGANA: 'hiragana',
KATAKANA: 'katakana',
SPACE: 'space',
OTHER: 'other',
};
// prettier-ignore
function getType(input, compact = false) {
const {
EN, JA, EN_NUM, JA_NUM, EN_PUNC, JA_PUNC, KANJI, HIRAGANA, KATAKANA, SPACE, OTHER,
} = TOKEN_TYPES;
if (compact) {
switch (true) {
case isCharJaNum(input): return OTHER;
case isCharEnNum(input): return OTHER;
case isCharEnSpace(input): return EN;
case isCharEnglishPunctuation(input): return OTHER;
case isCharJaSpace(input): return JA;
case isCharJapanesePunctuation(input): return OTHER;
case isCharJapanese(input): return JA;
case isCharRomaji(input): return EN;
default: return OTHER;
const { EN, JA, EN_NUM, JA_NUM, EN_PUNC, JA_PUNC, KANJI, HIRAGANA, KATAKANA, SPACE, OTHER, } = TOKEN_TYPES;
if (compact) {
switch (true) {
case isCharJaNum(input): return OTHER;
case isCharEnNum(input): return OTHER;
case isCharEnSpace(input): return EN;
case isCharEnglishPunctuation(input): return OTHER;
case isCharJaSpace(input): return JA;
case isCharJapanesePunctuation(input): return OTHER;
case isCharJapanese(input): return JA;
case isCharRomaji(input): return EN;
default: return OTHER;
}
}
} else {
switch (true) {
case isCharJaSpace(input): return SPACE;
case isCharEnSpace(input): return SPACE;
case isCharJaNum(input): return JA_NUM;
case isCharEnNum(input): return EN_NUM;
case isCharEnglishPunctuation(input): return EN_PUNC;
case isCharJapanesePunctuation(input): return JA_PUNC;
case isCharKanji(input): return KANJI;
case isCharHiragana(input): return HIRAGANA;
case isCharKatakana(input): return KATAKANA;
case isCharJapanese(input): return JA;
case isCharRomaji(input): return EN;
default: return OTHER;
else {
switch (true) {
case isCharJaSpace(input): return SPACE;
case isCharEnSpace(input): return SPACE;
case isCharJaNum(input): return JA_NUM;
case isCharEnNum(input): return EN_NUM;
case isCharEnglishPunctuation(input): return EN_PUNC;
case isCharJapanesePunctuation(input): return JA_PUNC;
case isCharKanji(input): return KANJI;
case isCharHiragana(input): return HIRAGANA;
case isCharKatakana(input): return KATAKANA;
case isCharJapanese(input): return JA;
case isCharRomaji(input): return EN;
default: return OTHER;
}
}
}
}
/**

@@ -1928,4 +1746,4 @@ * Splits input into array of strings separated by opinionated token types

* @param {String} input text
* @param {Object} [options={ compact: false, detailed: false}] options to modify output style
* @return {String|Object[]} text split into tokens containing values, or detailed object
* @param {{compact: Boolean | undefined, detailed: Boolean | undefined}} [options={ compact: false, detailed: false}] options to modify output style
* @return {(String[]|Array.<{type: String, value: String}>)} text split into tokens containing values, or detailed object
* @example

@@ -1987,28 +1805,22 @@ * tokenize('ふふフフ')

function tokenize(input, { compact = false, detailed = false } = {}) {
if (input == null || isEmpty(input)) {
return [];
}
const chars = [...input];
let initial = chars.shift();
let prevType = getType(initial, compact);
initial = detailed ? { type: prevType, value: initial } : initial;
const result = chars.reduce(
(tokens, char) => {
const currType = getType(char, compact);
const sameType = currType === prevType;
prevType = currType;
let newValue = char;
if (sameType) {
newValue = (detailed ? tokens.pop().value : tokens.pop()) + newValue;
}
return detailed
? tokens.concat({ type: currType, value: newValue })
: tokens.concat(newValue);
},
[initial]
);
return result;
if (input == null || isEmpty(input)) {
return [];
}
const chars = [...input];
let initial = chars.shift();
let prevType = getType(initial, compact);
initial = detailed ? { type: prevType, value: initial } : initial;
const result = chars.reduce((tokens, char) => {
const currType = getType(char, compact);
const sameType = currType === prevType;
prevType = currType;
let newValue = char;
if (sameType) {
newValue = (detailed ? tokens.pop().value : tokens.pop()) + newValue;
}
return detailed
? tokens.concat({ type: currType, value: newValue })
: tokens.concat(newValue);
}, [initial]);
return result;
}

@@ -2018,9 +1830,7 @@

const isTrailingWithoutFinalKana = (input, leading) => !leading && !isKana(input[input.length - 1]);
const isInvalidMatcher = (input, matchKanji) =>
(matchKanji && ![...matchKanji].some(isKanji)) || (!matchKanji && isKana(input));
const isInvalidMatcher = (input, matchKanji) => (matchKanji && ![...matchKanji].some(isKanji)) || (!matchKanji && isKana(input));
/**
* Strips [Okurigana](https://en.wikipedia.org/wiki/Okurigana)
* @param {String} input text
* @param {Object} [options={ leading: false, matchKanji: '' }] optional config
* @param {{ leading: Boolean | undefined, matchKanji: string | undefined }} [options={ leading: false, matchKanji: '' }] optional config
* @return {String} text with okurigana removed

@@ -2040,16 +1850,11 @@ * @example

function stripOkurigana(input = '', { leading = false, matchKanji = '' } = {}) {
if (
!isJapanese(input) ||
isLeadingWithoutInitialKana(input, leading) ||
isTrailingWithoutFinalKana(input, leading) ||
isInvalidMatcher(input, matchKanji)
) {
return input;
}
const chars = matchKanji || input;
const okuriganaRegex = new RegExp(
leading ? `^${tokenize(chars).shift()}` : `${tokenize(chars).pop()}$`
);
return input.replace(okuriganaRegex, '');
if (!isJapanese(input) ||
isLeadingWithoutInitialKana(input, leading) ||
isTrailingWithoutFinalKana(input, leading) ||
isInvalidMatcher(input, matchKanji)) {
return input;
}
const chars = matchKanji || input;
const okuriganaRegex = new RegExp(leading ? `^${tokenize(chars).shift()}` : `${tokenize(chars).pop()}$`);
return input.replace(okuriganaRegex, '');
}

@@ -2056,0 +1861,0 @@

@@ -21,12 +21,12 @@ /**

function typeOf(value) {
if (value === null) {
return 'null';
}
if (value !== Object(value)) {
return typeof value;
}
return {}.toString
.call(value)
.slice(8, -1)
.toLowerCase();
if (value === null) {
return 'null';
}
if (value !== Object(value)) {
return typeof value;
}
return {}.toString
.call(value)
.slice(8, -1)
.toLowerCase();
}

@@ -40,6 +40,6 @@

function isEmpty(input) {
if (typeOf(input) !== 'string') {
return true;
}
return !input.length;
if (typeOf(input) !== 'string') {
return true;
}
return !input.length;
}

@@ -55,22 +55,20 @@

function isCharInRange(char = '', start, end) {
if (isEmpty(char)) return false;
const code = char.charCodeAt(0);
return start <= code && code <= end;
if (isEmpty(char))
return false;
const code = char.charCodeAt(0);
return start <= code && code <= end;
}
const VERSION = '5.2.0';
const TO_KANA_METHODS = {
HIRAGANA: 'toHiragana',
KATAKANA: 'toKatakana',
HIRAGANA: 'toHiragana',
KATAKANA: 'toKatakana',
};
const ROMANIZATIONS = {
HEPBURN: 'hepburn',
HEPBURN: 'hepburn',
};
/**
* Default config for WanaKana, user passed options will be merged with these
* @type {DefaultOptions}
* @name defaultOptions
* @name DefaultOptions
* @property {Boolean} [useObsoleteKana=false] - Set to true to use obsolete characters, such as ゐ and ゑ.

@@ -84,3 +82,3 @@ * @example

* // => "only convert the katakana: ひらがな"
* @property {Object} [convertLongVowelMark=true] - Set to false to prevent conversions of 'ー' to extended vowels with toHiragana()
* @property {Boolean} [convertLongVowelMark=true] - Set to false to prevent conversions of 'ー' to extended vowels with toHiragana()
* @example

@@ -93,9 +91,9 @@ * toHiragana('ラーメン', { convertLongVowelMark: false });

* // => "hiragana KATAKANA"
* @property {Boolean|String} [IMEMode=false] - Set to true, 'toHiragana', or 'toKatakana' to handle conversion while it is being typed.
* @property {String} [romanization='hepburn'] - choose toRomaji() romanization map (currently only 'hepburn')
* @property {Object} [customKanaMapping] - custom map will be merged with default conversion
* @property {Boolean | 'toHiragana' | 'toKatakana'} [IMEMode=false] - Set to true, 'toHiragana', or 'toKatakana' to handle conversion while it is being typed.
* @property {'hepburn'} [romanization='hepburn'] - choose toRomaji() romanization map (currently only 'hepburn')
* @property {Object.<String, String>} [customKanaMapping] - custom map will be merged with default conversion
* @example
* toKana('wanakana', { customKanaMapping: { na: 'に', ka: 'Bana' }) };
* // => 'わにBanaに'
* @property {Object} [customRomajiMapping] - custom map will be merged with default conversion
* @property {Object.<String, String>} [customRomajiMapping] - custom map will be merged with default conversion
* @example

@@ -106,8 +104,8 @@ * toRomaji('つじぎり', { customRomajiMapping: { じ: 'zi', つ: 'tu', り: 'li' }) };

const DEFAULT_OPTIONS = {
useObsoleteKana: false,
passRomaji: false,
upcaseKatakana: false,
IMEMode: false,
convertLongVowelMark: true,
romanization: ROMANIZATIONS.HEPBURN,
useObsoleteKana: false,
passRomaji: false,
convertLongVowelMark: true,
upcaseKatakana: false,
IMEMode: false,
romanization: ROMANIZATIONS.HEPBURN,
};

@@ -126,7 +124,5 @@ const LATIN_UPPERCASE_START = 0x41;

const KANJI_END = 0x9faf;
const KANJI_ITERATION_MARK = 0x3005; // 々
const PROLONGED_SOUND_MARK = 0x30fc; // ー
const KANA_SLASH_DOT = 0x30fb; // ・
const ZENKAKU_NUMBERS = [0xff10, 0xff19];

@@ -140,3 +136,2 @@ const ZENKAKU_UPPERCASE = [UPPERCASE_ZENKAKU_START, UPPERCASE_ZENKAKU_END];

const ZENKAKU_SYMBOLS_CURRENCY = [0xffe0, 0xffee];
const HIRAGANA_CHARS = [0x3040, 0x309f];

@@ -150,54 +145,48 @@ const KATAKANA_CHARS = [0x30a0, 0x30ff];

const RARE_CJK = [0x3400, 0x4dbf];
const KANA_RANGES = [
HIRAGANA_CHARS,
KATAKANA_CHARS,
KANA_PUNCTUATION,
HANKAKU_KATAKANA,
HIRAGANA_CHARS,
KATAKANA_CHARS,
KANA_PUNCTUATION,
HANKAKU_KATAKANA,
];
const JA_PUNCTUATION_RANGES = [
CJK_SYMBOLS_PUNCTUATION,
KANA_PUNCTUATION,
KATAKANA_PUNCTUATION,
ZENKAKU_PUNCTUATION_1,
ZENKAKU_PUNCTUATION_2,
ZENKAKU_PUNCTUATION_3,
ZENKAKU_PUNCTUATION_4,
ZENKAKU_SYMBOLS_CURRENCY,
CJK_SYMBOLS_PUNCTUATION,
KANA_PUNCTUATION,
KATAKANA_PUNCTUATION,
ZENKAKU_PUNCTUATION_1,
ZENKAKU_PUNCTUATION_2,
ZENKAKU_PUNCTUATION_3,
ZENKAKU_PUNCTUATION_4,
ZENKAKU_SYMBOLS_CURRENCY,
];
// All Japanese unicode start and end ranges
// Includes kanji, kana, zenkaku latin chars, punctuation, and number ranges.
const JAPANESE_RANGES = [
...KANA_RANGES,
...JA_PUNCTUATION_RANGES,
ZENKAKU_UPPERCASE,
ZENKAKU_LOWERCASE,
ZENKAKU_NUMBERS,
COMMON_CJK,
RARE_CJK,
...KANA_RANGES,
...JA_PUNCTUATION_RANGES,
ZENKAKU_UPPERCASE,
ZENKAKU_LOWERCASE,
ZENKAKU_NUMBERS,
COMMON_CJK,
RARE_CJK,
];
const MODERN_ENGLISH = [0x0000, 0x007f];
const HEPBURN_MACRON_RANGES = [
[0x0100, 0x0101], // Ā ā
[0x0112, 0x0113], // Ē ē
[0x012a, 0x012b], // Ī ī
[0x014c, 0x014d], // Ō ō
[0x016a, 0x016b], // Ū ū
[0x0100, 0x0101],
[0x0112, 0x0113],
[0x012a, 0x012b],
[0x014c, 0x014d],
[0x016a, 0x016b], // Ū ū
];
const SMART_QUOTE_RANGES = [
[0x2018, 0x2019], // ‘ ’
[0x201c, 0x201d], // “ ”
[0x2018, 0x2019],
[0x201c, 0x201d], // “ ”
];
const ROMAJI_RANGES = [MODERN_ENGLISH, ...HEPBURN_MACRON_RANGES];
const EN_PUNCTUATION_RANGES = [
[0x20, 0x2f],
[0x3a, 0x3f],
[0x5b, 0x60],
[0x7b, 0x7e],
...SMART_QUOTE_RANGES,
[0x20, 0x2f],
[0x3a, 0x3f],
[0x5b, 0x60],
[0x7b, 0x7e],
...SMART_QUOTE_RANGES,
];

@@ -211,3 +200,3 @@

function isCharJapanese(char = '') {
return JAPANESE_RANGES.some(([start, end]) => isCharInRange(char, start, end));
return JAPANESE_RANGES.some(([start, end]) => isCharInRange(char, start, end));
}

@@ -218,3 +207,3 @@

* @param {String} [input=''] text
* @param {Regexp} [allowed] additional test allowed to pass for each char
* @param {RegExp} [allowed] additional test allowed to pass for each char
* @return {Boolean} true if passes checks

@@ -238,9 +227,9 @@ * @example

function isJapanese(input = '', allowed) {
const augmented = typeOf(allowed) === 'regexp';
return isEmpty(input)
? false
: [...input].every((char) => {
const isJa = isCharJapanese(char);
return !augmented ? isJa : isJa || allowed.test(char);
});
const augmented = typeOf(allowed) === 'regexp';
return isEmpty(input)
? false
: [...input].every((char) => {
const isJa = isCharJapanese(char);
return !augmented ? isJa : isJa || allowed.test(char);
});
}

@@ -272,5 +261,6 @@

}
function memoizeOne(resultFn, isEqual) {
if (isEqual === void 0) { isEqual = areInputsEqual; }
if (isEqual === void 0) {
isEqual = areInputsEqual;
}
var cache = null;

@@ -300,84 +290,87 @@ function memoized() {

var has = Object.prototype.hasOwnProperty;
function find(iter, tar, key) {
for (key of iter.keys()) {
if (dequal(key, tar)) return key;
}
for (key of iter.keys()) {
if (dequal(key, tar))
return key;
}
}
function dequal(foo, bar) {
var ctor, len, tmp;
if (foo === bar) return true;
if (foo && bar && (ctor=foo.constructor) === bar.constructor) {
if (ctor === Date) return foo.getTime() === bar.getTime();
if (ctor === RegExp) return foo.toString() === bar.toString();
if (ctor === Array) {
if ((len=foo.length) === bar.length) {
while (len-- && dequal(foo[len], bar[len]));
}
return len === -1;
}
if (ctor === Set) {
if (foo.size !== bar.size) {
return false;
}
for (len of foo) {
tmp = len;
if (tmp && typeof tmp === 'object') {
tmp = find(bar, tmp);
if (!tmp) return false;
}
if (!bar.has(tmp)) return false;
}
return true;
}
if (ctor === Map) {
if (foo.size !== bar.size) {
return false;
}
for (len of foo) {
tmp = len[0];
if (tmp && typeof tmp === 'object') {
tmp = find(bar, tmp);
if (!tmp) return false;
}
if (!dequal(len[1], bar.get(tmp))) {
return false;
}
}
return true;
}
if (ctor === ArrayBuffer) {
foo = new Uint8Array(foo);
bar = new Uint8Array(bar);
} else if (ctor === DataView) {
if ((len=foo.byteLength) === bar.byteLength) {
while (len-- && foo.getInt8(len) === bar.getInt8(len));
}
return len === -1;
}
if (ArrayBuffer.isView(foo)) {
if ((len=foo.byteLength) === bar.byteLength) {
while (len-- && foo[len] === bar[len]);
}
return len === -1;
}
if (!ctor || typeof foo === 'object') {
len = 0;
for (ctor in foo) {
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
}
return Object.keys(bar).length === len;
}
}
return foo !== foo && bar !== bar;
var ctor, len, tmp;
if (foo === bar)
return true;
if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
if (ctor === Date)
return foo.getTime() === bar.getTime();
if (ctor === RegExp)
return foo.toString() === bar.toString();
if (ctor === Array) {
if ((len = foo.length) === bar.length) {
while (len-- && dequal(foo[len], bar[len]))
;
}
return len === -1;
}
if (ctor === Set) {
if (foo.size !== bar.size) {
return false;
}
for (len of foo) {
tmp = len;
if (tmp && typeof tmp === 'object') {
tmp = find(bar, tmp);
if (!tmp)
return false;
}
if (!bar.has(tmp))
return false;
}
return true;
}
if (ctor === Map) {
if (foo.size !== bar.size) {
return false;
}
for (len of foo) {
tmp = len[0];
if (tmp && typeof tmp === 'object') {
tmp = find(bar, tmp);
if (!tmp)
return false;
}
if (!dequal(len[1], bar.get(tmp))) {
return false;
}
}
return true;
}
if (ctor === ArrayBuffer) {
foo = new Uint8Array(foo);
bar = new Uint8Array(bar);
}
else if (ctor === DataView) {
if ((len = foo.byteLength) === bar.byteLength) {
while (len-- && foo.getInt8(len) === bar.getInt8(len))
;
}
return len === -1;
}
if (ArrayBuffer.isView(foo)) {
if ((len = foo.byteLength) === bar.byteLength) {
while (len-- && foo[len] === bar[len])
;
}
return len === -1;
}
if (!ctor || typeof foo === 'object') {
len = 0;
for (ctor in foo) {
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor))
return false;
if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor]))
return false;
}
return Object.keys(bar).length === len;
}
}
return foo !== foo && bar !== bar;
}

@@ -393,78 +386,58 @@

function applyMapping(string, mapping, convertEnding) {
const root = mapping;
function nextSubtree(tree, nextChar) {
const subtree = tree[nextChar];
if (subtree === undefined) {
return undefined;
const root = mapping;
function nextSubtree(tree, nextChar) {
const subtree = tree[nextChar];
if (subtree === undefined) {
return undefined;
}
// if the next child node does not have a node value, set its node value to the input
return Object.assign({ '': tree[''] + nextChar }, tree[nextChar]);
}
// if the next child node does not have a node value, set its node value to the input
return Object.assign({ '': tree[''] + nextChar }, tree[nextChar]);
}
function newChunk(remaining, currentCursor) {
// start parsing a new chunk
const firstChar = remaining.charAt(0);
return parse(
Object.assign({ '': firstChar }, root[firstChar]),
remaining.slice(1),
currentCursor,
currentCursor + 1
);
}
function parse(tree, remaining, lastCursor, currentCursor) {
if (!remaining) {
if (convertEnding || Object.keys(tree).length === 1) {
// nothing more to consume, just commit the last chunk and return it
// so as to not have an empty element at the end of the result
return tree[''] ? [[lastCursor, currentCursor, tree['']]] : [];
}
// if we don't want to convert the ending, because there are still possible continuations
// return null as the final node value
return [[lastCursor, currentCursor, null]];
function newChunk(remaining, currentCursor) {
// start parsing a new chunk
const firstChar = remaining.charAt(0);
return parse(Object.assign({ '': firstChar }, root[firstChar]), remaining.slice(1), currentCursor, currentCursor + 1);
}
if (Object.keys(tree).length === 1) {
return [[lastCursor, currentCursor, tree['']]].concat(
newChunk(remaining, currentCursor)
);
function parse(tree, remaining, lastCursor, currentCursor) {
if (!remaining) {
if (convertEnding || Object.keys(tree).length === 1) {
// nothing more to consume, just commit the last chunk and return it
// so as to not have an empty element at the end of the result
return tree[''] ? [[lastCursor, currentCursor, tree['']]] : [];
}
// if we don't want to convert the ending, because there are still possible continuations
// return null as the final node value
return [[lastCursor, currentCursor, null]];
}
if (Object.keys(tree).length === 1) {
return [[lastCursor, currentCursor, tree['']]].concat(newChunk(remaining, currentCursor));
}
const subtree = nextSubtree(tree, remaining.charAt(0));
if (subtree === undefined) {
return [[lastCursor, currentCursor, tree['']]].concat(newChunk(remaining, currentCursor));
}
// continue current branch
return parse(subtree, remaining.slice(1), lastCursor, currentCursor + 1);
}
const subtree = nextSubtree(tree, remaining.charAt(0));
if (subtree === undefined) {
return [[lastCursor, currentCursor, tree['']]].concat(
newChunk(remaining, currentCursor)
);
}
// continue current branch
return parse(subtree, remaining.slice(1), lastCursor, currentCursor + 1);
}
return newChunk(string, 0);
return newChunk(string, 0);
}
// transform the tree, so that for example hepburnTree['ゔ']['ぁ'][''] === 'va'
// or kanaTree['k']['y']['a'][''] === 'きゃ'
function transform(tree) {
return Object.entries(tree).reduce((map, [char, subtree]) => {
const endOfBranch = typeOf(subtree) === 'string';
// eslint-disable-next-line no-param-reassign
map[char] = endOfBranch ? { '': subtree } : transform(subtree);
return map;
}, {});
return Object.entries(tree).reduce((map, [char, subtree]) => {
const endOfBranch = typeOf(subtree) === 'string';
// eslint-disable-next-line no-param-reassign
map[char] = endOfBranch ? { '': subtree } : transform(subtree);
return map;
}, {});
}
function getSubTreeOf(tree, string) {
return string.split('').reduce((correctSubTree, char) => {
if (correctSubTree[char] === undefined) {
// eslint-disable-next-line no-param-reassign
correctSubTree[char] = {};
}
return correctSubTree[char];
}, tree);
return string.split('').reduce((correctSubTree, char) => {
if (correctSubTree[char] === undefined) {
// eslint-disable-next-line no-param-reassign
correctSubTree[char] = {};
}
return correctSubTree[char];
}, tree);
}
/**

@@ -482,46 +455,38 @@ * Creates a custom mapping tree, returns a function that accepts a defaultMap which the newly created customMapping will be merged with and returned

function createCustomMapping(customMap = {}) {
const customTree = {};
if (typeOf(customMap) === 'object') {
Object.entries(customMap).forEach(([roma, kana]) => {
let subTree = customTree;
roma.split('').forEach((char) => {
if (subTree[char] === undefined) {
subTree[char] = {};
const customTree = {};
if (typeOf(customMap) === 'object') {
Object.entries(customMap).forEach(([roma, kana]) => {
let subTree = customTree;
roma.split('').forEach((char) => {
if (subTree[char] === undefined) {
subTree[char] = {};
}
subTree = subTree[char];
});
subTree[''] = kana;
});
}
return function makeMap(map) {
const mapCopy = JSON.parse(JSON.stringify(map));
function transformMap(mapSubtree, customSubtree) {
if (mapSubtree === undefined || typeOf(mapSubtree) === 'string') {
return customSubtree;
}
return Object.entries(customSubtree).reduce((newSubtree, [char, subtree]) => {
// eslint-disable-next-line no-param-reassign
newSubtree[char] = transformMap(mapSubtree[char], subtree);
return newSubtree;
}, mapSubtree);
}
subTree = subTree[char];
});
subTree[''] = kana;
});
}
return function makeMap(map) {
const mapCopy = JSON.parse(JSON.stringify(map));
function transformMap(mapSubtree, customSubtree) {
if (mapSubtree === undefined || typeOf(mapSubtree) === 'string') {
return customSubtree;
}
return Object.entries(customSubtree).reduce(
(newSubtree, [char, subtree]) => {
// eslint-disable-next-line no-param-reassign
newSubtree[char] = transformMap(mapSubtree[char], subtree);
return newSubtree;
},
mapSubtree
);
}
return transformMap(mapCopy, customTree);
};
return transformMap(mapCopy, customTree);
};
}
// allow consumer to pass either function or object as customMapping
function mergeCustomMapping(map, customMapping) {
if (!customMapping) {
return map;
}
return typeOf(customMapping) === 'function'
? customMapping(map)
: createCustomMapping(customMapping)(map);
if (!customMapping) {
return map;
}
return typeOf(customMapping) === 'function'
? customMapping(map)
: createCustomMapping(customMapping)(map);
}

@@ -533,82 +498,76 @@

const BASIC_KUNREI = {
a: 'あ', i: 'い', u: 'う', e: 'え', o: 'お',
k: { a: 'か', i: 'き', u: 'く', e: 'け', o: 'こ', },
s: { a: 'さ', i: 'し', u: 'す', e: 'せ', o: 'そ', },
t: { a: 'た', i: 'ち', u: 'つ', e: 'て', o: 'と', },
n: { a: 'な', i: 'に', u: 'ぬ', e: 'ね', o: 'の', },
h: { a: 'は', i: 'ひ', u: 'ふ', e: 'へ', o: 'ほ', },
m: { a: 'ま', i: 'み', u: 'む', e: 'め', o: 'も', },
y: { a: 'や', u: 'ゆ', o: 'よ' },
r: { a: 'ら', i: 'り', u: 'る', e: 'れ', o: 'ろ', },
w: { a: 'わ', i: 'ゐ', e: 'ゑ', o: 'を', },
g: { a: 'が', i: 'ぎ', u: 'ぐ', e: 'げ', o: 'ご', },
z: { a: 'ざ', i: 'じ', u: 'ず', e: 'ぜ', o: 'ぞ', },
d: { a: 'だ', i: 'ぢ', u: 'づ', e: 'で', o: 'ど', },
b: { a: 'ば', i: 'び', u: 'ぶ', e: 'べ', o: 'ぼ', },
p: { a: 'ぱ', i: 'ぴ', u: 'ぷ', e: 'ぺ', o: 'ぽ', },
v: { a: 'ゔぁ', i: 'ゔぃ', u: 'ゔ', e: 'ゔぇ', o: 'ゔぉ', },
a: 'あ', i: 'い', u: 'う', e: 'え', o: 'お',
k: { a: 'か', i: 'き', u: 'く', e: 'け', o: 'こ', },
s: { a: 'さ', i: 'し', u: 'す', e: 'せ', o: 'そ', },
t: { a: 'た', i: 'ち', u: 'つ', e: 'て', o: 'と', },
n: { a: 'な', i: 'に', u: 'ぬ', e: 'ね', o: 'の', },
h: { a: 'は', i: 'ひ', u: 'ふ', e: 'へ', o: 'ほ', },
m: { a: 'ま', i: 'み', u: 'む', e: 'め', o: 'も', },
y: { a: 'や', u: 'ゆ', o: 'よ' },
r: { a: 'ら', i: 'り', u: 'る', e: 'れ', o: 'ろ', },
w: { a: 'わ', i: 'ゐ', e: 'ゑ', o: 'を', },
g: { a: 'が', i: 'ぎ', u: 'ぐ', e: 'げ', o: 'ご', },
z: { a: 'ざ', i: 'じ', u: 'ず', e: 'ぜ', o: 'ぞ', },
d: { a: 'だ', i: 'ぢ', u: 'づ', e: 'で', o: 'ど', },
b: { a: 'ば', i: 'び', u: 'ぶ', e: 'べ', o: 'ぼ', },
p: { a: 'ぱ', i: 'ぴ', u: 'ぷ', e: 'ぺ', o: 'ぽ', },
v: { a: 'ゔぁ', i: 'ゔぃ', u: 'ゔ', e: 'ゔぇ', o: 'ゔぉ', },
};
const SPECIAL_SYMBOLS$1 = {
'.': '。',
',': '、',
':': ':',
'/': '・',
'!': '!',
'?': '?',
'~': '〜',
'-': 'ー',
'‘': '「',
'’': '」',
'“': '『',
'”': '』',
'[': '[',
']': ']',
'(': '(',
')': ')',
'{': '{',
'}': '}',
'.': '。',
',': '、',
':': ':',
'/': '・',
'!': '!',
'?': '?',
'~': '〜',
'-': 'ー',
'‘': '「',
'’': '」',
'“': '『',
'”': '』',
'[': '[',
']': ']',
'(': '(',
')': ')',
'{': '{',
'}': '}',
};
const CONSONANTS = {
k: 'き',
s: 'し',
t: 'ち',
n: 'に',
h: 'ひ',
m: 'み',
r: 'り',
g: 'ぎ',
z: 'じ',
d: 'ぢ',
b: 'び',
p: 'ぴ',
v: 'ゔ',
q: 'く',
f: 'ふ',
k: 'き',
s: 'し',
t: 'ち',
n: 'に',
h: 'ひ',
m: 'み',
r: 'り',
g: 'ぎ',
z: 'じ',
d: 'ぢ',
b: 'び',
p: 'ぴ',
v: 'ゔ',
q: 'く',
f: 'ふ',
};
const SMALL_Y$1 = { ya: 'ゃ', yi: 'ぃ', yu: 'ゅ', ye: 'ぇ', yo: 'ょ' };
const SMALL_VOWELS = { a: 'ぁ', i: 'ぃ', u: 'ぅ', e: 'ぇ', o: 'ぉ' };
// typing one should be the same as having typed the other instead
const ALIASES = {
sh: 'sy', // sha -> sya
ch: 'ty', // cho -> tyo
cy: 'ty', // cyo -> tyo
chy: 'ty', // chyu -> tyu
shy: 'sy', // shya -> sya
j: 'zy', // ja -> zya
jy: 'zy', // jye -> zye
// exceptions to above rules
shi: 'si',
chi: 'ti',
tsu: 'tu',
ji: 'zi',
fu: 'hu',
sh: 'sy',
ch: 'ty',
cy: 'ty',
chy: 'ty',
shy: 'sy',
j: 'zy',
jy: 'zy',
// exceptions to above rules
shi: 'si',
chi: 'ti',
tsu: 'tu',
ji: 'zi',
fu: 'hu',
};
// xtu -> っ
const SMALL_LETTERS = Object.assign(
{
const SMALL_LETTERS = Object.assign({
tu: 'っ',

@@ -618,161 +577,137 @@ wa: 'ゎ',

ke: 'ヶ',
},
SMALL_VOWELS,
SMALL_Y$1
);
}, SMALL_VOWELS, SMALL_Y$1);
// don't follow any notable patterns
const SPECIAL_CASES = {
yi: 'い',
wu: 'う',
ye: 'いぇ',
wi: 'うぃ',
we: 'うぇ',
kwa: 'くぁ',
whu: 'う',
// because it's not thya for てゃ but tha
// and tha is not てぁ, but てゃ
tha: 'てゃ',
thu: 'てゅ',
tho: 'てょ',
dha: 'でゃ',
dhu: 'でゅ',
dho: 'でょ',
yi: 'い',
wu: 'う',
ye: 'いぇ',
wi: 'うぃ',
we: 'うぇ',
kwa: 'くぁ',
whu: 'う',
// because it's not thya for てゃ but tha
// and tha is not てぁ, but てゃ
tha: 'てゃ',
thu: 'てゅ',
tho: 'てょ',
dha: 'でゃ',
dhu: 'でゅ',
dho: 'でょ',
};
const AIUEO_CONSTRUCTIONS = {
wh: 'う',
kw: 'く',
qw: 'く',
q: 'く',
gw: 'ぐ',
sw: 'す',
ts: 'つ',
th: 'て',
tw: 'と',
dh: 'で',
dw: 'ど',
fw: 'ふ',
f: 'ふ',
wh: 'う',
kw: 'く',
qw: 'く',
q: 'く',
gw: 'ぐ',
sw: 'す',
ts: 'つ',
th: 'て',
tw: 'と',
dh: 'で',
dw: 'ど',
fw: 'ふ',
f: 'ふ',
};
/* eslint-enable */
function createRomajiToKanaMap$1() {
const kanaTree = transform(BASIC_KUNREI);
// pseudo partial application
const subtreeOf = (string) => getSubTreeOf(kanaTree, string);
// add tya, sya, etc.
Object.entries(CONSONANTS).forEach(([consonant, yKana]) => {
Object.entries(SMALL_Y$1).forEach(([roma, kana]) => {
// for example kyo -> き + ょ
subtreeOf(consonant + roma)[''] = yKana + kana;
const kanaTree = transform(BASIC_KUNREI);
// pseudo partial application
const subtreeOf = (string) => getSubTreeOf(kanaTree, string);
// add tya, sya, etc.
Object.entries(CONSONANTS).forEach(([consonant, yKana]) => {
Object.entries(SMALL_Y$1).forEach(([roma, kana]) => {
// for example kyo -> き + ょ
subtreeOf(consonant + roma)[''] = yKana + kana;
});
});
});
Object.entries(SPECIAL_SYMBOLS$1).forEach(([symbol, jsymbol]) => {
subtreeOf(symbol)[''] = jsymbol;
});
// things like うぃ, くぃ, etc.
Object.entries(AIUEO_CONSTRUCTIONS).forEach(([consonant, aiueoKana]) => {
Object.entries(SMALL_VOWELS).forEach(([vowel, kana]) => {
const subtree = subtreeOf(consonant + vowel);
subtree[''] = aiueoKana + kana;
Object.entries(SPECIAL_SYMBOLS$1).forEach(([symbol, jsymbol]) => {
subtreeOf(symbol)[''] = jsymbol;
});
});
// different ways to write ん
['n', "n'", 'xn'].forEach((nChar) => {
subtreeOf(nChar)[''] = 'ん';
});
// c is equivalent to k, but not for chi, cha, etc. that's why we have to make a copy of k
kanaTree.c = JSON.parse(JSON.stringify(kanaTree.k));
Object.entries(ALIASES).forEach(([string, alternative]) => {
const allExceptLast = string.slice(0, string.length - 1);
const last = string.charAt(string.length - 1);
const parentTree = subtreeOf(allExceptLast);
// copy to avoid recursive containment
parentTree[last] = JSON.parse(JSON.stringify(subtreeOf(alternative)));
});
function getAlternatives(string) {
return [...Object.entries(ALIASES), ...[['c', 'k']]].reduce(
(list, [alt, roma]) => (string.startsWith(roma) ? list.concat(string.replace(roma, alt)) : list),
[]
);
}
Object.entries(SMALL_LETTERS).forEach(([kunreiRoma, kana]) => {
const last = (char) => char.charAt(char.length - 1);
const allExceptLast = (chars) => chars.slice(0, chars.length - 1);
const xRoma = `x${kunreiRoma}`;
const xSubtree = subtreeOf(xRoma);
xSubtree[''] = kana;
// ltu -> xtu -> っ
const parentTree = subtreeOf(`l${allExceptLast(kunreiRoma)}`);
parentTree[last(kunreiRoma)] = xSubtree;
// ltsu -> ltu -> っ
getAlternatives(kunreiRoma).forEach((altRoma) => {
['l', 'x'].forEach((prefix) => {
const altParentTree = subtreeOf(prefix + allExceptLast(altRoma));
altParentTree[last(altRoma)] = subtreeOf(prefix + kunreiRoma);
});
// things like うぃ, くぃ, etc.
Object.entries(AIUEO_CONSTRUCTIONS).forEach(([consonant, aiueoKana]) => {
Object.entries(SMALL_VOWELS).forEach(([vowel, kana]) => {
const subtree = subtreeOf(consonant + vowel);
subtree[''] = aiueoKana + kana;
});
});
});
Object.entries(SPECIAL_CASES).forEach(([string, kana]) => {
subtreeOf(string)[''] = kana;
});
// add kka, tta, etc.
function addTsu(tree) {
return Object.entries(tree).reduce((tsuTree, [key, value]) => {
if (!key) {
// we have reached the bottom of this branch
// eslint-disable-next-line no-param-reassign
tsuTree[key] = `っ${value}`;
} else {
// more subtrees
// eslint-disable-next-line no-param-reassign
tsuTree[key] = addTsu(value);
}
return tsuTree;
}, {});
}
// have to explicitly name c here, because we made it a copy of k, not a reference
[...Object.keys(CONSONANTS), 'c', 'y', 'w', 'j'].forEach((consonant) => {
const subtree = kanaTree[consonant];
subtree[consonant] = addTsu(subtree);
});
// nn should not be っん
delete kanaTree.n.n;
// solidify the results, so that there there is referential transparency within the tree
return Object.freeze(JSON.parse(JSON.stringify(kanaTree)));
// different ways to write ん
['n', "n'", 'xn'].forEach((nChar) => {
subtreeOf(nChar)[''] = 'ん';
});
// c is equivalent to k, but not for chi, cha, etc. that's why we have to make a copy of k
kanaTree.c = JSON.parse(JSON.stringify(kanaTree.k));
Object.entries(ALIASES).forEach(([string, alternative]) => {
const allExceptLast = string.slice(0, string.length - 1);
const last = string.charAt(string.length - 1);
const parentTree = subtreeOf(allExceptLast);
// copy to avoid recursive containment
parentTree[last] = JSON.parse(JSON.stringify(subtreeOf(alternative)));
});
function getAlternatives(string) {
return [...Object.entries(ALIASES), ...[['c', 'k']]].reduce((list, [alt, roma]) => (string.startsWith(roma) ? list.concat(string.replace(roma, alt)) : list), []);
}
Object.entries(SMALL_LETTERS).forEach(([kunreiRoma, kana]) => {
const last = (char) => char.charAt(char.length - 1);
const allExceptLast = (chars) => chars.slice(0, chars.length - 1);
const xRoma = `x${kunreiRoma}`;
const xSubtree = subtreeOf(xRoma);
xSubtree[''] = kana;
// ltu -> xtu -> っ
const parentTree = subtreeOf(`l${allExceptLast(kunreiRoma)}`);
parentTree[last(kunreiRoma)] = xSubtree;
// ltsu -> ltu -> っ
getAlternatives(kunreiRoma).forEach((altRoma) => {
['l', 'x'].forEach((prefix) => {
const altParentTree = subtreeOf(prefix + allExceptLast(altRoma));
altParentTree[last(altRoma)] = subtreeOf(prefix + kunreiRoma);
});
});
});
Object.entries(SPECIAL_CASES).forEach(([string, kana]) => {
subtreeOf(string)[''] = kana;
});
// add kka, tta, etc.
function addTsu(tree) {
return Object.entries(tree).reduce((tsuTree, [key, value]) => {
if (!key) {
// we have reached the bottom of this branch
// eslint-disable-next-line no-param-reassign
tsuTree[key] = `っ${value}`;
}
else {
// more subtrees
// eslint-disable-next-line no-param-reassign
tsuTree[key] = addTsu(value);
}
return tsuTree;
}, {});
}
// have to explicitly name c here, because we made it a copy of k, not a reference
[...Object.keys(CONSONANTS), 'c', 'y', 'w', 'j'].forEach((consonant) => {
const subtree = kanaTree[consonant];
subtree[consonant] = addTsu(subtree);
});
// nn should not be っん
delete kanaTree.n.n;
// solidify the results, so that there there is referential transparency within the tree
return Object.freeze(JSON.parse(JSON.stringify(kanaTree)));
}
let romajiToKanaMap = null;
function getRomajiToKanaTree() {
if (romajiToKanaMap == null) {
romajiToKanaMap = createRomajiToKanaMap$1();
}
return romajiToKanaMap;
if (romajiToKanaMap == null) {
romajiToKanaMap = createRomajiToKanaMap$1();
}
return romajiToKanaMap;
}
const USE_OBSOLETE_KANA_MAP = createCustomMapping({
wi: 'ゐ',
we: 'ゑ',
wi: 'ゐ',
we: 'ゑ',
});
function IME_MODE_MAP(map) {
// in IME mode, we do not want to convert single ns
const mapCopy = JSON.parse(JSON.stringify(map));
mapCopy.n.n = { '': 'ん' };
mapCopy.n[' '] = { '': 'ん' };
return mapCopy;
// in IME mode, we do not want to convert single ns
const mapCopy = JSON.parse(JSON.stringify(map));
mapCopy.n.n = { '': 'ん' };
mapCopy.n[' '] = { '': 'ん' };
return mapCopy;
}

@@ -786,4 +721,5 @@

function isCharUpperCase(char = '') {
if (isEmpty(char)) return false;
return isCharInRange(char, LATIN_UPPERCASE_START, LATIN_UPPERCASE_END);
if (isEmpty(char))
return false;
return isCharInRange(char, LATIN_UPPERCASE_START, LATIN_UPPERCASE_END);
}

@@ -797,4 +733,5 @@

function isCharLongDash(char = '') {
if (isEmpty(char)) return false;
return char.charCodeAt(0) === PROLONGED_SOUND_MARK;
if (isEmpty(char))
return false;
return char.charCodeAt(0) === PROLONGED_SOUND_MARK;
}

@@ -808,4 +745,5 @@

function isCharSlashDot(char = '') {
if (isEmpty(char)) return false;
return char.charCodeAt(0) === KANA_SLASH_DOT;
if (isEmpty(char))
return false;
return char.charCodeAt(0) === KANA_SLASH_DOT;
}

@@ -819,5 +757,7 @@

function isCharHiragana(char = '') {
if (isEmpty(char)) return false;
if (isCharLongDash(char)) return true;
return isCharInRange(char, HIRAGANA_START, HIRAGANA_END);
if (isEmpty(char))
return false;
if (isCharLongDash(char))
return true;
return isCharInRange(char, HIRAGANA_START, HIRAGANA_END);
}

@@ -838,37 +778,32 @@

function hiraganaToKatakana(input = '') {
const kata = [];
input.split('').forEach((char) => {
// Short circuit to avoid incorrect codeshift for 'ー' and '・'
if (isCharLongDash(char) || isCharSlashDot(char)) {
kata.push(char);
} else if (isCharHiragana(char)) {
// Shift charcode.
const code = char.charCodeAt(0) + (KATAKANA_START - HIRAGANA_START);
const kataChar = String.fromCharCode(code);
kata.push(kataChar);
} else {
// Pass non-hiragana chars through
kata.push(char);
}
});
return kata.join('');
const kata = [];
input.split('').forEach((char) => {
// Short circuit to avoid incorrect codeshift for 'ー' and '・'
if (isCharLongDash(char) || isCharSlashDot(char)) {
kata.push(char);
}
else if (isCharHiragana(char)) {
// Shift charcode.
const code = char.charCodeAt(0) + (KATAKANA_START - HIRAGANA_START);
const kataChar = String.fromCharCode(code);
kata.push(kataChar);
}
else {
// Pass non-hiragana chars through
kata.push(char);
}
});
return kata.join('');
}
// memoize and deeply compare args so we only recreate when necessary
const createRomajiToKanaMap = memoizeOne(
(IMEMode, useObsoleteKana, customKanaMapping) => {
const createRomajiToKanaMap = memoizeOne((IMEMode, useObsoleteKana, customKanaMapping) => {
let map = getRomajiToKanaTree();
map = IMEMode ? IME_MODE_MAP(map) : map;
map = useObsoleteKana ? USE_OBSOLETE_KANA_MAP(map) : map;
if (customKanaMapping) {
map = mergeCustomMapping(map, customKanaMapping);
map = mergeCustomMapping(map, customKanaMapping);
}
return map;
},
dequal
);
}, dequal);
/**

@@ -878,2 +813,3 @@ * Convert [Romaji](https://en.wikipedia.org/wiki/Romaji) to [Kana](https://en.wikipedia.org/wiki/Kana), lowercase text will result in [Hiragana](https://en.wikipedia.org/wiki/Hiragana) and uppercase text will result in [Katakana](https://en.wikipedia.org/wiki/Katakana).

* @param {DefaultOptions} [options=defaultOptions]
* @param {Object.<string, string>} [map] custom mapping
* @return {String} converted text

@@ -897,33 +833,27 @@ * @example

function toKana(input = '', options = {}, map) {
let config;
if (!map) {
config = mergeWithDefaultOptions(options);
map = createRomajiToKanaMap(
config.IMEMode,
config.useObsoleteKana,
config.customKanaMapping
);
} else {
config = options;
}
// throw away the substring index information and just concatenate all the kana
return splitIntoConvertedKana(input, config, map)
.map((kanaToken) => {
const [start, end, kana] = kanaToken;
if (kana === null) {
// haven't converted the end of the string, since we are in IME mode
return input.slice(start);
}
const enforceHiragana = config.IMEMode === TO_KANA_METHODS.HIRAGANA;
const enforceKatakana = config.IMEMode === TO_KANA_METHODS.KATAKANA
|| [...input.slice(start, end)].every(isCharUpperCase);
return enforceHiragana || !enforceKatakana
? kana
: hiraganaToKatakana(kana);
let config;
if (!map) {
config = mergeWithDefaultOptions(options);
map = createRomajiToKanaMap(config.IMEMode, config.useObsoleteKana, config.customKanaMapping);
}
else {
config = options;
}
// throw away the substring index information and just concatenate all the kana
return splitIntoConvertedKana(input, config, map)
.map((kanaToken) => {
const [start, end, kana] = kanaToken;
if (kana === null) {
// haven't converted the end of the string, since we are in IME mode
return input.slice(start);
}
const enforceHiragana = config.IMEMode === TO_KANA_METHODS.HIRAGANA;
const enforceKatakana = config.IMEMode === TO_KANA_METHODS.KATAKANA
|| [...input.slice(start, end)].every(isCharUpperCase);
return enforceHiragana || !enforceKatakana
? kana
: hiraganaToKatakana(kana);
})
.join('');
.join('');
}
/**

@@ -941,9 +871,7 @@ *

function splitIntoConvertedKana(input = '', options = {}, map) {
const { IMEMode, useObsoleteKana, customKanaMapping } = options;
if (!map) {
map = createRomajiToKanaMap(IMEMode, useObsoleteKana, customKanaMapping);
}
return applyMapping(input.toLowerCase(), map, !IMEMode);
const { IMEMode, useObsoleteKana, customKanaMapping } = options;
if (!map) {
map = createRomajiToKanaMap(IMEMode, useObsoleteKana, customKanaMapping);
}
return applyMapping(input.toLowerCase(), map, !IMEMode);
}

@@ -959,94 +887,74 @@

function makeOnInput(options) {
let prevInput;
// Enforce IMEMode if not already specified
const mergedConfig = Object.assign({}, mergeWithDefaultOptions(options), {
IMEMode: options.IMEMode || true,
});
const preConfiguredMap = createRomajiToKanaMap(
mergedConfig.IMEMode,
mergedConfig.useObsoleteKana,
mergedConfig.customKanaMapping
);
const triggers = [
...Object.keys(preConfiguredMap),
...Object.keys(preConfiguredMap).map((char) => char.toUpperCase()),
];
return function onInput({ target }) {
if (
target.value !== prevInput
&& target.dataset.ignoreComposition !== 'true'
) {
convertInput(target, mergedConfig, preConfiguredMap, triggers);
}
};
let prevInput;
// Enforce IMEMode if not already specified
const mergedConfig = Object.assign({}, mergeWithDefaultOptions(options), {
IMEMode: options.IMEMode || true,
});
const preConfiguredMap = createRomajiToKanaMap(mergedConfig.IMEMode, mergedConfig.useObsoleteKana, mergedConfig.customKanaMapping);
const triggers = [
...Object.keys(preConfiguredMap),
...Object.keys(preConfiguredMap).map((char) => char.toUpperCase()),
];
return function onInput({ target }) {
if (target.value !== prevInput
&& target.dataset.ignoreComposition !== 'true') {
convertInput(target, mergedConfig, preConfiguredMap, triggers);
}
};
}
function convertInput(target, options, map, triggers, prevInput) {
const [head, textToConvert, tail] = splitInput(
target.value,
target.selectionEnd,
triggers
);
const convertedText = toKana(textToConvert, options, map);
const changed = textToConvert !== convertedText;
if (changed) {
const newCursor = head.length + convertedText.length;
const newValue = head + convertedText + tail;
// eslint-disable-next-line no-param-reassign
target.value = newValue;
if (tail.length) {
// push later on event loop (otherwise mid-text insertion can be 1 char too far to the right)
setTimeout(() => target.setSelectionRange(newCursor, newCursor), 1);
} else {
target.setSelectionRange(newCursor, newCursor);
const [head, textToConvert, tail] = splitInput(target.value, target.selectionEnd, triggers);
const convertedText = toKana(textToConvert, options, map);
const changed = textToConvert !== convertedText;
if (changed) {
const newCursor = head.length + convertedText.length;
const newValue = head + convertedText + tail;
// eslint-disable-next-line no-param-reassign
target.value = newValue;
if (tail.length) {
// push later on event loop (otherwise mid-text insertion can be 1 char too far to the right)
setTimeout(() => target.setSelectionRange(newCursor, newCursor), 1);
}
else {
target.setSelectionRange(newCursor, newCursor);
}
}
}
else {
// eslint-disable-next-line no-param-reassign
target.value;
}
}
function onComposition({ type, target, data }) {
// navigator.platform is not 100% reliable for singling out all OS,
// but for determining desktop "Mac OS" it is effective enough.
const isMacOS = /Mac/.test(window.navigator && window.navigator.platform);
// We don't want to ignore on Android:
// https://github.com/WaniKani/WanaKana/issues/82
// But MacOS IME auto-closes if we don't ignore:
// https://github.com/WaniKani/WanaKana/issues/71
// Other platform Japanese IMEs pass through happily
if (isMacOS) {
if (type === 'compositionupdate' && isJapanese(data)) {
// eslint-disable-next-line no-param-reassign
target.dataset.ignoreComposition = 'true';
// navigator.platform is not 100% reliable for singling out all OS,
// but for determining desktop "Mac OS" it is effective enough.
const isMacOS = /Mac/.test(window.navigator && window.navigator.platform);
// We don't want to ignore on Android:
// https://github.com/WaniKani/WanaKana/issues/82
// But MacOS IME auto-closes if we don't ignore:
// https://github.com/WaniKani/WanaKana/issues/71
// Other platform Japanese IMEs pass through happily
if (isMacOS) {
if (type === 'compositionupdate' && isJapanese(data)) {
// eslint-disable-next-line no-param-reassign
target.dataset.ignoreComposition = 'true';
}
if (type === 'compositionend') {
// eslint-disable-next-line no-param-reassign
target.dataset.ignoreComposition = 'false';
}
}
if (type === 'compositionend') {
// eslint-disable-next-line no-param-reassign
target.dataset.ignoreComposition = 'false';
}
}
}
function trackListeners(id, inputHandler, compositionHandler) {
LISTENERS = LISTENERS.concat({
id,
inputHandler,
compositionHandler,
});
LISTENERS = LISTENERS.concat({
id,
inputHandler,
compositionHandler,
});
}
function untrackListeners({ id: targetId }) {
LISTENERS = LISTENERS.filter(({ id }) => id !== targetId);
LISTENERS = LISTENERS.filter(({ id }) => id !== targetId);
}
function findListeners(el) {
return (
el && LISTENERS.find(({ id }) => id === el.getAttribute('data-wanakana-id'))
);
return (el && LISTENERS.find(({ id }) => id === el.getAttribute('data-wanakana-id')));
}
// Handle non-terminal inserted input conversion:

@@ -1057,58 +965,43 @@ // | -> わ| -> わび| -> わ|び -> わs|び -> わsh|び -> わshi|び -> わし|び

function splitInput(text = '', cursor = 0, triggers = []) {
let head;
let toConvert;
let tail;
if (cursor === 0 && triggers.includes(text[0])) {
[head, toConvert, tail] = workFromStart(text, triggers);
} else if (cursor > 0) {
[head, toConvert, tail] = workBackwards(text, cursor);
} else {
[head, toConvert] = takeWhileAndSlice(
text,
(char) => !triggers.includes(char)
);
[toConvert, tail] = takeWhileAndSlice(
toConvert,
(char) => !isJapanese(char)
);
}
return [head, toConvert, tail];
let head;
let toConvert;
let tail;
if (cursor === 0 && triggers.includes(text[0])) {
[head, toConvert, tail] = workFromStart(text, triggers);
}
else if (cursor > 0) {
[head, toConvert, tail] = workBackwards(text, cursor);
}
else {
[head, toConvert] = takeWhileAndSlice(text, (char) => !triggers.includes(char));
[toConvert, tail] = takeWhileAndSlice(toConvert, (char) => !isJapanese(char));
}
return [head, toConvert, tail];
}
function workFromStart(text, catalystChars) {
return [
'',
...takeWhileAndSlice(
text,
(char) => catalystChars.includes(char) || !isJapanese(char, /[0-9]/)
),
];
return [
'',
...takeWhileAndSlice(text, (char) => catalystChars.includes(char) || !isJapanese(char, /[0-9]/)),
];
}
function workBackwards(text = '', startIndex = 0) {
const [toConvert, head] = takeWhileAndSlice(
[...text.slice(0, startIndex)].reverse(),
(char) => !isJapanese(char)
);
return [
head.reverse().join(''),
toConvert
.split('')
.reverse()
.join(''),
text.slice(startIndex),
];
const [toConvert, head] = takeWhileAndSlice([...text.slice(0, startIndex)].reverse(), (char) => !isJapanese(char));
return [
head.reverse().join(''),
toConvert
.split('')
.reverse()
.join(''),
text.slice(startIndex),
];
}
function takeWhileAndSlice(source = {}, predicate = (x) => !!x) {
const result = [];
const { length } = source;
let i = 0;
while (i < length && predicate(source[i], i)) {
result.push(source[i]);
i += 1;
}
return [result.join(''), source.slice(i)];
const result = [];
const { length } = source;
let i = 0;
while (i < length && predicate(source[i], i)) {
result.push(source[i]);
i += 1;
}
return [result.join(''), source.slice(i)];
}

@@ -1119,42 +1012,32 @@

const onCompositionStart = () => console.log('compositionstart');
const onCompositionUpdate = ({
target: { value, selectionStart, selectionEnd },
data,
}) => console.log('compositionupdate', {
data,
value,
selectionStart,
selectionEnd,
const onCompositionUpdate = ({ target: { value, selectionStart, selectionEnd }, data, }) => console.log('compositionupdate', {
data,
value,
selectionStart,
selectionEnd,
});
const onCompositionEnd = () => console.log('compositionend');
const events = {
input: onInput,
compositionstart: onCompositionStart,
compositionupdate: onCompositionUpdate,
compositionend: onCompositionEnd,
input: onInput,
compositionstart: onCompositionStart,
compositionupdate: onCompositionUpdate,
compositionend: onCompositionEnd,
};
const addDebugListeners = (input) => {
Object.entries(events).forEach(([event, handler]) => input.addEventListener(event, handler)
);
Object.entries(events).forEach(([event, handler]) => input.addEventListener(event, handler));
};
const removeDebugListeners = (input) => {
Object.entries(events).forEach(([event, handler]) => input.removeEventListener(event, handler)
);
Object.entries(events).forEach(([event, handler]) => input.removeEventListener(event, handler));
};
const ELEMENTS = ['TEXTAREA', 'INPUT'];
let idCounter = 0;
const newId = () => {
idCounter += 1;
return `${Date.now()}${idCounter}`;
idCounter += 1;
return `${Date.now()}${idCounter}`;
};
/**
* Binds eventListener for 'input' events to an input field to automagically replace values with kana
* Can pass `{ IMEMode: 'toHiragana' || 'toKatakana' }` to enforce kana conversion type
* @param {HTMLElement} element textarea, input[type="text"] etc
* @param {HTMLInputElement | HTMLTextAreaElement} element textarea, input[type="text"] etc
* @param {DefaultOptions} [options=defaultOptions] defaults to { IMEMode: true } using `toKana`

@@ -1165,35 +1048,31 @@ * @example

function bind(element = {}, options = {}, debug = false) {
if (!ELEMENTS.includes(element.nodeName)) {
throw new Error(
`Element provided to Wanakana bind() was not a valid input or textarea element.\n Received: (${JSON.stringify(
element
)})`
);
}
if (element.hasAttribute('data-wanakana-id')) {
return;
}
const onInput = makeOnInput(options);
const id = newId();
const attributes = [
{ name: 'data-wanakana-id', value: id },
{ name: 'lang', value: 'ja' },
{ name: 'autoCapitalize', value: 'none' },
{ name: 'autoCorrect', value: 'off' },
{ name: 'autoComplete', value: 'off' },
{ name: 'spellCheck', value: 'false' },
];
const previousAttributes = {};
attributes.forEach((attribute) => {
previousAttributes[attribute.name] = element.getAttribute(attribute.name);
element.setAttribute(attribute.name, attribute.value);
});
element.dataset.previousAttributes = JSON.stringify(previousAttributes);
element.addEventListener('input', onInput);
element.addEventListener('compositionupdate', onComposition);
element.addEventListener('compositionend', onComposition);
trackListeners(id, onInput, onComposition);
if (debug === true) {
addDebugListeners(element);
}
if (!ELEMENTS.includes(element.nodeName)) {
throw new Error(`Element provided to Wanakana bind() was not a valid input or textarea element.\n Received: (${JSON.stringify(element)})`);
}
if (element.hasAttribute('data-wanakana-id')) {
return;
}
const onInput = makeOnInput(options);
const id = newId();
const attributes = [
{ name: 'data-wanakana-id', value: id },
{ name: 'lang', value: 'ja' },
{ name: 'autoCapitalize', value: 'none' },
{ name: 'autoCorrect', value: 'off' },
{ name: 'autoComplete', value: 'off' },
{ name: 'spellCheck', value: 'false' },
];
const previousAttributes = {};
attributes.forEach((attribute) => {
previousAttributes[attribute.name] = element.getAttribute(attribute.name);
element.setAttribute(attribute.name, attribute.value);
});
element.dataset.previousAttributes = JSON.stringify(previousAttributes);
element.addEventListener('input', onInput);
element.addEventListener('compositionupdate', onComposition);
element.addEventListener('compositionend', onComposition);
trackListeners(id, onInput, onComposition);
if (debug === true) {
addDebugListeners(element);
}
}

@@ -1203,32 +1082,29 @@

* Unbinds eventListener from input field
* @param {HTMLElement} element textarea, input
* @param {HTMLInputElement | HTMLTextAreaElement} element textarea, input
*/
function unbind(element, debug = false) {
const listeners = findListeners(element);
if (listeners == null) {
throw new Error(
`Element provided to Wanakana unbind() had no listener registered.\n Received: ${JSON.stringify(
element
)}`
);
}
const { inputHandler, compositionHandler } = listeners;
const attributes = JSON.parse(element.dataset.previousAttributes);
Object.keys(attributes).forEach((key) => {
if (attributes[key]) {
element.setAttribute(key, attributes[key]);
} else {
element.removeAttribute(key);
const listeners = findListeners(element);
if (listeners == null) {
throw new Error(`Element provided to Wanakana unbind() had no listener registered.\n Received: ${JSON.stringify(element)}`);
}
});
element.removeAttribute('data-previous-attributes');
element.removeAttribute('data-ignore-composition');
element.removeEventListener('input', inputHandler);
element.removeEventListener('compositionstart', compositionHandler);
element.removeEventListener('compositionupdate', compositionHandler);
element.removeEventListener('compositionend', compositionHandler);
untrackListeners(listeners);
if (debug === true) {
removeDebugListeners(element);
}
const { inputHandler, compositionHandler } = listeners;
const attributes = JSON.parse(element.dataset.previousAttributes);
Object.keys(attributes).forEach((key) => {
if (attributes[key]) {
element.setAttribute(key, attributes[key]);
}
else {
element.removeAttribute(key);
}
});
element.removeAttribute('data-previous-attributes');
element.removeAttribute('data-ignore-composition');
element.removeEventListener('input', inputHandler);
element.removeEventListener('compositionstart', compositionHandler);
element.removeEventListener('compositionupdate', compositionHandler);
element.removeEventListener('compositionend', compositionHandler);
untrackListeners(listeners);
if (debug === true) {
removeDebugListeners(element);
}
}

@@ -1242,4 +1118,5 @@

function isCharRomaji(char = '') {
if (isEmpty(char)) return false;
return ROMAJI_RANGES.some(([start, end]) => isCharInRange(char, start, end));
if (isEmpty(char))
return false;
return ROMAJI_RANGES.some(([start, end]) => isCharInRange(char, start, end));
}

@@ -1250,3 +1127,3 @@

* @param {String} [input=''] text
* @param {Regexp} [allowed] additional test allowed to pass for each char
* @param {RegExp} [allowed] additional test allowed to pass for each char
* @return {Boolean} true if [Romaji](https://en.wikipedia.org/wiki/Romaji)

@@ -1268,9 +1145,9 @@ * @example

function isRomaji(input = '', allowed) {
const augmented = typeOf(allowed) === 'regexp';
return isEmpty(input)
? false
: [...input].every((char) => {
const isRoma = isCharRomaji(char);
return !augmented ? isRoma : isRoma || allowed.test(char);
});
const augmented = typeOf(allowed) === 'regexp';
return isEmpty(input)
? false
: [...input].every((char) => {
const isRoma = isCharRomaji(char);
return !augmented ? isRoma : isRoma || allowed.test(char);
});
}

@@ -1284,3 +1161,3 @@

function isCharKatakana(char = '') {
return isCharInRange(char, KATAKANA_START, KATAKANA_END);
return isCharInRange(char, KATAKANA_START, KATAKANA_END);
}

@@ -1294,4 +1171,5 @@

function isCharKana(char = '') {
if (isEmpty(char)) return false;
return isCharHiragana(char) || isCharKatakana(char);
if (isEmpty(char))
return false;
return isCharHiragana(char) || isCharKatakana(char);
}

@@ -1316,4 +1194,5 @@

function isKana(input = '') {
if (isEmpty(input)) return false;
return [...input].every(isCharKana);
if (isEmpty(input))
return false;
return [...input].every(isCharKana);
}

@@ -1334,4 +1213,5 @@

function isHiragana(input = '') {
if (isEmpty(input)) return false;
return [...input].every(isCharHiragana);
if (isEmpty(input))
return false;
return [...input].every(isCharHiragana);
}

@@ -1354,4 +1234,5 @@

function isKatakana(input = '') {
if (isEmpty(input)) return false;
return [...input].every(isCharKatakana);
if (isEmpty(input))
return false;
return [...input].every(isCharKatakana);
}

@@ -1365,4 +1246,5 @@

function isCharIterationMark(char = '') {
if (isEmpty(char)) return false;
return char.charCodeAt(0) === KANJI_ITERATION_MARK;
if (isEmpty(char))
return false;
return char.charCodeAt(0) === KANJI_ITERATION_MARK;
}

@@ -1376,3 +1258,3 @@

function isCharKanji(char = '') {
return isCharInRange(char, KANJI_START, KANJI_END) || isCharIterationMark(char);
return isCharInRange(char, KANJI_START, KANJI_END) || isCharIterationMark(char);
}

@@ -1397,4 +1279,5 @@

function isKanji(input = '') {
if (isEmpty(input)) return false;
return [...input].every(isCharKanji);
if (isEmpty(input))
return false;
return [...input].every(isCharKanji);
}

@@ -1405,3 +1288,3 @@

* @param {String} input text
* @param {Object} [options={ passKanji: true }] optional config to pass through kanji
* @param {{ passKanji: Boolean}} [options={ passKanji: true }] optional config to pass through kanji
* @return {Boolean} true if mixed

@@ -1421,8 +1304,8 @@ * @example

function isMixed(input = '', options = { passKanji: true }) {
const chars = [...input];
let hasKanji = false;
if (!options.passKanji) {
hasKanji = chars.some(isKanji);
}
return (chars.some(isHiragana) || chars.some(isKatakana)) && chars.some(isRomaji) && !hasKanji;
const chars = [...input];
let hasKanji = false;
if (!options.passKanji) {
hasKanji = chars.some(isKanji);
}
return (chars.some(isHiragana) || chars.some(isKatakana)) && chars.some(isRomaji) && !hasKanji;
}

@@ -1434,110 +1317,92 @@

const LONG_VOWELS = {
a: 'あ',
i: 'い',
u: 'う',
e: 'え',
o: 'う',
a: 'あ',
i: 'い',
u: 'う',
e: 'え',
o: 'う',
};
// inject toRomaji to avoid circular dependency between toRomaji <-> katakanaToHiragana
function katakanaToHiragana(
input = '',
toRomaji,
{ isDestinationRomaji, convertLongVowelMark } = {}
) {
let previousKana = '';
return input
.split('')
.reduce((hira, char, index) => {
// Short circuit to avoid incorrect codeshift for 'ー' and '・'
if (
isCharSlashDot(char)
|| isCharInitialLongDash(char, index)
|| isKanaAsSymbol(char)
) {
function katakanaToHiragana(input = '', toRomaji, { isDestinationRomaji, convertLongVowelMark } = {}) {
let previousKana = '';
return input
.split('')
.reduce((hira, char, index) => {
// Short circuit to avoid incorrect codeshift for 'ー' and '・'
if (isCharSlashDot(char)
|| isCharInitialLongDash(char, index)
|| isKanaAsSymbol(char)) {
return hira.concat(char);
}
// Transform long vowels: 'オー' to 'おう'
if (convertLongVowelMark
&& previousKana
&& isCharInnerLongDash(char, index)) {
// Transform previousKana back to romaji, and slice off the vowel
const romaji = toRomaji(previousKana).slice(-1);
// However, ensure 'オー' => 'おお' => 'oo' if this is a transform on the way to romaji
if (isCharKatakana(input[index - 1])
&& romaji === 'o'
&& isDestinationRomaji) {
return hira.concat('お');
}
return hira.concat(LONG_VOWELS[romaji]);
// Transform all other chars
}
if (!isCharLongDash(char) && isCharKatakana(char)) {
const code = char.charCodeAt(0) + (HIRAGANA_START - KATAKANA_START);
const hiraChar = String.fromCharCode(code);
previousKana = hiraChar;
return hira.concat(hiraChar);
}
// Pass non katakana chars through
previousKana = '';
return hira.concat(char);
}
// Transform long vowels: 'オー' to 'おう'
if (
convertLongVowelMark
&& previousKana
&& isCharInnerLongDash(char, index)
) {
// Transform previousKana back to romaji, and slice off the vowel
const romaji = toRomaji(previousKana).slice(-1);
// However, ensure 'オー' => 'おお' => 'oo' if this is a transform on the way to romaji
if (
isCharKatakana(input[index - 1])
&& romaji === 'o'
&& isDestinationRomaji
) {
return hira.concat('お');
}
return hira.concat(LONG_VOWELS[romaji]);
// Transform all other chars
}
if (!isCharLongDash(char) && isCharKatakana(char)) {
const code = char.charCodeAt(0) + (HIRAGANA_START - KATAKANA_START);
const hiraChar = String.fromCharCode(code);
previousKana = hiraChar;
return hira.concat(hiraChar);
}
// Pass non katakana chars through
previousKana = '';
return hira.concat(char);
}, [])
.join('');
.join('');
}
let kanaToHepburnMap = null;
/* eslint-disable */
// prettier-ignore
const BASIC_ROMAJI = {
あ:'a', い:'i', う:'u', え:'e', お:'o',
か:'ka', き:'ki', く:'ku', け:'ke', こ:'ko',
さ:'sa', し:'shi', す:'su', せ:'se', そ:'so',
た:'ta', ち:'chi', つ:'tsu', て:'te', と:'to',
な:'na', に:'ni', ぬ:'nu', ね:'ne', の:'no',
は:'ha', ひ:'hi', ふ:'fu', へ:'he', ほ:'ho',
ま:'ma', み:'mi', む:'mu', め:'me', も:'mo',
ら:'ra', り:'ri', る:'ru', れ:'re', ろ:'ro',
や:'ya', ゆ:'yu', よ:'yo',
わ:'wa', ゐ:'wi', ゑ:'we', を:'wo',
ん: 'n',
が:'ga', ぎ:'gi', ぐ:'gu', げ:'ge', ご:'go',
ざ:'za', じ:'ji', ず:'zu', ぜ:'ze', ぞ:'zo',
だ:'da', ぢ:'ji', づ:'zu', で:'de', ど:'do',
ば:'ba', び:'bi', ぶ:'bu', べ:'be', ぼ:'bo',
ぱ:'pa', ぴ:'pi', ぷ:'pu', ぺ:'pe', ぽ:'po',
ゔぁ:'va', ゔぃ:'vi', ゔ:'vu', ゔぇ:'ve', ゔぉ:'vo',
あ: 'a', い: 'i', う: 'u', え: 'e', お: 'o',
か: 'ka', き: 'ki', く: 'ku', け: 'ke', こ: 'ko',
さ: 'sa', し: 'shi', す: 'su', せ: 'se', そ: 'so',
た: 'ta', ち: 'chi', つ: 'tsu', て: 'te', と: 'to',
な: 'na', に: 'ni', ぬ: 'nu', ね: 'ne', の: 'no',
は: 'ha', ひ: 'hi', ふ: 'fu', へ: 'he', ほ: 'ho',
ま: 'ma', み: 'mi', む: 'mu', め: 'me', も: 'mo',
ら: 'ra', り: 'ri', る: 'ru', れ: 're', ろ: 'ro',
や: 'ya', ゆ: 'yu', よ: 'yo',
わ: 'wa', ゐ: 'wi', ゑ: 'we', を: 'wo',
ん: 'n',
が: 'ga', ぎ: 'gi', ぐ: 'gu', げ: 'ge', ご: 'go',
ざ: 'za', じ: 'ji', ず: 'zu', ぜ: 'ze', ぞ: 'zo',
だ: 'da', ぢ: 'ji', づ: 'zu', で: 'de', ど: 'do',
ば: 'ba', び: 'bi', ぶ: 'bu', べ: 'be', ぼ: 'bo',
ぱ: 'pa', ぴ: 'pi', ぷ: 'pu', ぺ: 'pe', ぽ: 'po',
ゔぁ: 'va', ゔぃ: 'vi', ゔ: 'vu', ゔぇ: 've', ゔぉ: 'vo',
};
/* eslint-enable */
const SPECIAL_SYMBOLS = {
'。': '.',
'、': ',',
':': ':',
'・': '/',
'!': '!',
'?': '?',
'〜': '~',
'ー': '-',
'「': '‘',
'」': '’',
'『': '“',
'』': '”',
'[': '[',
']': ']',
'(': '(',
')': ')',
'{': '{',
'}': '}',
' ': ' ',
'。': '.',
'、': ',',
':': ':',
'・': '/',
'!': '!',
'?': '?',
'〜': '~',
'ー': '-',
'「': '‘',
'」': '’',
'『': '“',
'』': '”',
'[': '[',
']': ']',
'(': '(',
')': ')',
'{': '{',
'}': '}',
' ': ' ',
};
// んい -> n'i

@@ -1548,174 +1413,152 @@ const AMBIGUOUS_VOWELS = ['あ', 'い', 'う', 'え', 'お', 'や', 'ゆ', 'よ'];

const SMALL_AIUEO = {
ぁ: 'a',
ぃ: 'i',
ぅ: 'u',
ぇ: 'e',
ぉ: 'o',
ぁ: 'a',
ぃ: 'i',
ぅ: 'u',
ぇ: 'e',
ぉ: 'o',
};
const YOON_KANA = [
'き',
'に',
'ひ',
'み',
'り',
'ぎ',
'び',
'ぴ',
'ゔ',
'く',
'ふ',
'き',
'に',
'ひ',
'み',
'り',
'ぎ',
'び',
'ぴ',
'ゔ',
'く',
'ふ',
];
const YOON_EXCEPTIONS = {
し: 'sh',
ち: 'ch',
じ: 'j',
ぢ: 'j',
し: 'sh',
ち: 'ch',
じ: 'j',
ぢ: 'j',
};
const SMALL_KANA = {
っ: '',
ゃ: 'ya',
ゅ: 'yu',
ょ: 'yo',
ぁ: 'a',
ぃ: 'i',
ぅ: 'u',
ぇ: 'e',
ぉ: 'o',
っ: '',
ゃ: 'ya',
ゅ: 'yu',
ょ: 'yo',
ぁ: 'a',
ぃ: 'i',
ぅ: 'u',
ぇ: 'e',
ぉ: 'o',
};
// going with the intuitive (yet incorrect) solution where っや -> yya and っぃ -> ii
// in other words, just assume the sokuon could have been applied to anything
const SOKUON_WHITELIST = {
b: 'b',
c: 't',
d: 'd',
f: 'f',
g: 'g',
h: 'h',
j: 'j',
k: 'k',
m: 'm',
p: 'p',
q: 'q',
r: 'r',
s: 's',
t: 't',
v: 'v',
w: 'w',
x: 'x',
z: 'z',
b: 'b',
c: 't',
d: 'd',
f: 'f',
g: 'g',
h: 'h',
j: 'j',
k: 'k',
m: 'm',
p: 'p',
q: 'q',
r: 'r',
s: 's',
t: 't',
v: 'v',
w: 'w',
x: 'x',
z: 'z',
};
function getKanaToHepburnTree() {
if (kanaToHepburnMap == null) {
kanaToHepburnMap = createKanaToHepburnMap();
}
return kanaToHepburnMap;
if (kanaToHepburnMap == null) {
kanaToHepburnMap = createKanaToHepburnMap();
}
return kanaToHepburnMap;
}
function getKanaToRomajiTree(romanization) {
switch (romanization) {
case ROMANIZATIONS.HEPBURN:
return getKanaToHepburnTree();
default:
return {};
}
switch (romanization) {
case ROMANIZATIONS.HEPBURN:
return getKanaToHepburnTree();
default:
return {};
}
}
function createKanaToHepburnMap() {
const romajiTree = transform(BASIC_ROMAJI);
const subtreeOf = (string) => getSubTreeOf(romajiTree, string);
const setTrans = (string, transliteration) => {
subtreeOf(string)[''] = transliteration;
};
Object.entries(SPECIAL_SYMBOLS).forEach(([jsymbol, symbol]) => {
subtreeOf(jsymbol)[''] = symbol;
});
[...Object.entries(SMALL_Y), ...Object.entries(SMALL_AIUEO)].forEach(
([roma, kana]) => {
setTrans(roma, kana);
}
);
// きゃ -> kya
YOON_KANA.forEach((kana) => {
const firstRomajiChar = subtreeOf(kana)[''][0];
Object.entries(SMALL_Y).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, firstRomajiChar + yRoma);
const romajiTree = transform(BASIC_ROMAJI);
const subtreeOf = (string) => getSubTreeOf(romajiTree, string);
const setTrans = (string, transliteration) => {
subtreeOf(string)[''] = transliteration;
};
Object.entries(SPECIAL_SYMBOLS).forEach(([jsymbol, symbol]) => {
subtreeOf(jsymbol)[''] = symbol;
});
// きぃ -> kyi
Object.entries(SMALL_Y_EXTRA).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, firstRomajiChar + yRoma);
[...Object.entries(SMALL_Y), ...Object.entries(SMALL_AIUEO)].forEach(([roma, kana]) => {
setTrans(roma, kana);
});
});
Object.entries(YOON_EXCEPTIONS).forEach(([kana, roma]) => {
// じゃ -> ja
Object.entries(SMALL_Y).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, roma + yRoma[1]);
// きゃ -> kya
YOON_KANA.forEach((kana) => {
const firstRomajiChar = subtreeOf(kana)[''][0];
Object.entries(SMALL_Y).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, firstRomajiChar + yRoma);
});
// きぃ -> kyi
Object.entries(SMALL_Y_EXTRA).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, firstRomajiChar + yRoma);
});
});
// じぃ -> jyi, じぇ -> je
setTrans(`${kana}ぃ`, `${roma}yi`);
setTrans(`${kana}ぇ`, `${roma}e`);
});
romajiTree['っ'] = resolveTsu(romajiTree);
Object.entries(SMALL_KANA).forEach(([kana, roma]) => {
setTrans(kana, roma);
});
AMBIGUOUS_VOWELS.forEach((kana) => {
setTrans(`ん${kana}`, `n'${subtreeOf(kana)['']}`);
});
// NOTE: could be re-enabled with an option?
// // んば -> mbo
// const LABIAL = [
// 'ば', 'び', 'ぶ', 'べ', 'ぼ',
// 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ',
// 'ま', 'み', 'む', 'め', 'も',
// ];
// LABIAL.forEach((kana) => {
// setTrans(`ん${kana}`, `m${subtreeOf(kana)['']}`);
// });
return Object.freeze(JSON.parse(JSON.stringify(romajiTree)));
Object.entries(YOON_EXCEPTIONS).forEach(([kana, roma]) => {
// じゃ -> ja
Object.entries(SMALL_Y).forEach(([yKana, yRoma]) => {
setTrans(kana + yKana, roma + yRoma[1]);
});
// じぃ -> jyi, じぇ -> je
setTrans(`${kana}ぃ`, `${roma}yi`);
setTrans(`${kana}ぇ`, `${roma}e`);
});
romajiTree['っ'] = resolveTsu(romajiTree);
Object.entries(SMALL_KANA).forEach(([kana, roma]) => {
setTrans(kana, roma);
});
AMBIGUOUS_VOWELS.forEach((kana) => {
setTrans(`ん${kana}`, `n'${subtreeOf(kana)['']}`);
});
// NOTE: could be re-enabled with an option?
// // んば -> mbo
// const LABIAL = [
// 'ば', 'び', 'ぶ', 'べ', 'ぼ',
// 'ぱ', 'ぴ', 'ぷ', 'ぺ', 'ぽ',
// 'ま', 'み', 'む', 'め', 'も',
// ];
// LABIAL.forEach((kana) => {
// setTrans(`ん${kana}`, `m${subtreeOf(kana)['']}`);
// });
return Object.freeze(JSON.parse(JSON.stringify(romajiTree)));
}
function resolveTsu(tree) {
return Object.entries(tree).reduce((tsuTree, [key, value]) => {
if (!key) {
// we have reached the bottom of this branch
const consonant = value.charAt(0);
// eslint-disable-next-line no-param-reassign
tsuTree[key] = Object.keys(SOKUON_WHITELIST).includes(consonant)
? SOKUON_WHITELIST[consonant] + value
: value;
} else {
// more subtrees
// eslint-disable-next-line no-param-reassign
tsuTree[key] = resolveTsu(value);
}
return tsuTree;
}, {});
return Object.entries(tree).reduce((tsuTree, [key, value]) => {
if (!key) {
// we have reached the bottom of this branch
const consonant = value.charAt(0);
// eslint-disable-next-line no-param-reassign
tsuTree[key] = Object.keys(SOKUON_WHITELIST).includes(consonant)
? SOKUON_WHITELIST[consonant] + value
: value;
}
else {
// more subtrees
// eslint-disable-next-line no-param-reassign
tsuTree[key] = resolveTsu(value);
}
return tsuTree;
}, {});
}
// memoize and deeply compare args so we only recreate when necessary
const createKanaToRomajiMap = memoizeOne(
(romanization, customRomajiMapping) => {
const createKanaToRomajiMap = memoizeOne((romanization, customRomajiMapping) => {
let map = getKanaToRomajiTree(romanization);
if (customRomajiMapping) {
map = mergeCustomMapping(map, customRomajiMapping);
map = mergeCustomMapping(map, customRomajiMapping);
}
return map;
},
dequal
);
}, dequal);
/**

@@ -1725,3 +1568,3 @@ * Convert kana to romaji

* @param {DefaultOptions} [options=defaultOptions]
* @param {Object} map custom mapping
* @param {Object.<string, string>} [map] custom mapping
* @return {String} converted text

@@ -1739,36 +1582,21 @@ * @example

function toRomaji(input = '', options = {}, map) {
const config = mergeWithDefaultOptions(options);
if (!map) {
map = createKanaToRomajiMap(
config.romanization,
config.customRomajiMapping
);
}
// just throw away the substring index information and simply concatenate all the kana
return splitIntoRomaji(input, config, map)
.map((romajiToken) => {
const [start, end, romaji] = romajiToken;
const makeUpperCase = config.upcaseKatakana && isKatakana(input.slice(start, end));
return makeUpperCase ? romaji.toUpperCase() : romaji;
const config = mergeWithDefaultOptions(options);
if (!map) {
map = createKanaToRomajiMap(config.romanization, config.customRomajiMapping);
}
// just throw away the substring index information and simply concatenate all the kana
return splitIntoRomaji(input, config, map)
.map((romajiToken) => {
const [start, end, romaji] = romajiToken;
const makeUpperCase = config.upcaseKatakana && isKatakana(input.slice(start, end));
return makeUpperCase ? romaji.toUpperCase() : romaji;
})
.join('');
.join('');
}
function splitIntoRomaji(input, options, map) {
if (!map) {
map = createKanaToRomajiMap(
options.romanization,
options.customRomajiMapping
);
}
const config = Object.assign({}, { isDestinationRomaji: true }, options);
return applyMapping(
katakanaToHiragana(input, toRomaji, config),
map,
!options.IMEMode
);
if (!map) {
map = createKanaToRomajiMap(options.romanization, options.customRomajiMapping);
}
const config = Object.assign({}, { isDestinationRomaji: true }, options);
return applyMapping(katakanaToHiragana(input, toRomaji, config), map, !options.IMEMode);
}

@@ -1782,4 +1610,5 @@

function isCharEnglishPunctuation(char = '') {
if (isEmpty(char)) return false;
return EN_PUNCTUATION_RANGES.some(([start, end]) => isCharInRange(char, start, end));
if (isEmpty(char))
return false;
return EN_PUNCTUATION_RANGES.some(([start, end]) => isCharInRange(char, start, end));
}

@@ -1803,17 +1632,14 @@

function toHiragana(input = '', options = {}) {
const config = mergeWithDefaultOptions(options);
if (config.passRomaji) {
const config = mergeWithDefaultOptions(options);
if (config.passRomaji) {
return katakanaToHiragana(input, toRomaji, config);
}
if (isMixed(input, { passKanji: true })) {
const convertedKatakana = katakanaToHiragana(input, toRomaji, config);
return toKana(convertedKatakana.toLowerCase(), config);
}
if (isRomaji(input) || isCharEnglishPunctuation(input)) {
return toKana(input.toLowerCase(), config);
}
return katakanaToHiragana(input, toRomaji, config);
}
if (isMixed(input, { passKanji: true })) {
const convertedKatakana = katakanaToHiragana(input, toRomaji, config);
return toKana(convertedKatakana.toLowerCase(), config);
}
if (isRomaji(input) || isCharEnglishPunctuation(input)) {
return toKana(input.toLowerCase(), config);
}
return katakanaToHiragana(input, toRomaji, config);
}

@@ -1837,13 +1663,11 @@

function toKatakana(input = '', options = {}) {
const mergedOptions = mergeWithDefaultOptions(options);
if (mergedOptions.passRomaji) {
const mergedOptions = mergeWithDefaultOptions(options);
if (mergedOptions.passRomaji) {
return hiraganaToKatakana(input);
}
if (isMixed(input) || isRomaji(input) || isCharEnglishPunctuation(input)) {
const hiragana = toKana(input.toLowerCase(), mergedOptions);
return hiraganaToKatakana(hiragana);
}
return hiraganaToKatakana(input);
}
if (isMixed(input) || isRomaji(input) || isCharEnglishPunctuation(input)) {
const hiragana = toKana(input.toLowerCase(), mergedOptions);
return hiraganaToKatakana(hiragana);
}
return hiraganaToKatakana(input);
}

@@ -1857,4 +1681,5 @@

function isCharJapanesePunctuation(char = '') {
if (isEmpty(char) || isCharIterationMark(char)) return false;
return JA_PUNCTUATION_RANGES.some(([start, end]) => isCharInRange(char, start, end));
if (isEmpty(char) || isCharIterationMark(char))
return false;
return JA_PUNCTUATION_RANGES.some(([start, end]) => isCharInRange(char, start, end));
}

@@ -1866,53 +1691,48 @@

const isCharEnNum = (x) => /[0-9]/.test(x);
const TOKEN_TYPES = {
EN: 'en',
JA: 'ja',
EN_NUM: 'englishNumeral',
JA_NUM: 'japaneseNumeral',
EN_PUNC: 'englishPunctuation',
JA_PUNC: 'japanesePunctuation',
KANJI: 'kanji',
HIRAGANA: 'hiragana',
KATAKANA: 'katakana',
SPACE: 'space',
OTHER: 'other',
EN: 'en',
JA: 'ja',
EN_NUM: 'englishNumeral',
JA_NUM: 'japaneseNumeral',
EN_PUNC: 'englishPunctuation',
JA_PUNC: 'japanesePunctuation',
KANJI: 'kanji',
HIRAGANA: 'hiragana',
KATAKANA: 'katakana',
SPACE: 'space',
OTHER: 'other',
};
// prettier-ignore
function getType(input, compact = false) {
const {
EN, JA, EN_NUM, JA_NUM, EN_PUNC, JA_PUNC, KANJI, HIRAGANA, KATAKANA, SPACE, OTHER,
} = TOKEN_TYPES;
if (compact) {
switch (true) {
case isCharJaNum(input): return OTHER;
case isCharEnNum(input): return OTHER;
case isCharEnSpace(input): return EN;
case isCharEnglishPunctuation(input): return OTHER;
case isCharJaSpace(input): return JA;
case isCharJapanesePunctuation(input): return OTHER;
case isCharJapanese(input): return JA;
case isCharRomaji(input): return EN;
default: return OTHER;
const { EN, JA, EN_NUM, JA_NUM, EN_PUNC, JA_PUNC, KANJI, HIRAGANA, KATAKANA, SPACE, OTHER, } = TOKEN_TYPES;
if (compact) {
switch (true) {
case isCharJaNum(input): return OTHER;
case isCharEnNum(input): return OTHER;
case isCharEnSpace(input): return EN;
case isCharEnglishPunctuation(input): return OTHER;
case isCharJaSpace(input): return JA;
case isCharJapanesePunctuation(input): return OTHER;
case isCharJapanese(input): return JA;
case isCharRomaji(input): return EN;
default: return OTHER;
}
}
} else {
switch (true) {
case isCharJaSpace(input): return SPACE;
case isCharEnSpace(input): return SPACE;
case isCharJaNum(input): return JA_NUM;
case isCharEnNum(input): return EN_NUM;
case isCharEnglishPunctuation(input): return EN_PUNC;
case isCharJapanesePunctuation(input): return JA_PUNC;
case isCharKanji(input): return KANJI;
case isCharHiragana(input): return HIRAGANA;
case isCharKatakana(input): return KATAKANA;
case isCharJapanese(input): return JA;
case isCharRomaji(input): return EN;
default: return OTHER;
else {
switch (true) {
case isCharJaSpace(input): return SPACE;
case isCharEnSpace(input): return SPACE;
case isCharJaNum(input): return JA_NUM;
case isCharEnNum(input): return EN_NUM;
case isCharEnglishPunctuation(input): return EN_PUNC;
case isCharJapanesePunctuation(input): return JA_PUNC;
case isCharKanji(input): return KANJI;
case isCharHiragana(input): return HIRAGANA;
case isCharKatakana(input): return KATAKANA;
case isCharJapanese(input): return JA;
case isCharRomaji(input): return EN;
default: return OTHER;
}
}
}
}
/**

@@ -1924,4 +1744,4 @@ * Splits input into array of strings separated by opinionated token types

* @param {String} input text
* @param {Object} [options={ compact: false, detailed: false}] options to modify output style
* @return {String|Object[]} text split into tokens containing values, or detailed object
* @param {{compact: Boolean | undefined, detailed: Boolean | undefined}} [options={ compact: false, detailed: false}] options to modify output style
* @return {(String[]|Array.<{type: String, value: String}>)} text split into tokens containing values, or detailed object
* @example

@@ -1983,28 +1803,22 @@ * tokenize('ふふフフ')

function tokenize(input, { compact = false, detailed = false } = {}) {
if (input == null || isEmpty(input)) {
return [];
}
const chars = [...input];
let initial = chars.shift();
let prevType = getType(initial, compact);
initial = detailed ? { type: prevType, value: initial } : initial;
const result = chars.reduce(
(tokens, char) => {
const currType = getType(char, compact);
const sameType = currType === prevType;
prevType = currType;
let newValue = char;
if (sameType) {
newValue = (detailed ? tokens.pop().value : tokens.pop()) + newValue;
}
return detailed
? tokens.concat({ type: currType, value: newValue })
: tokens.concat(newValue);
},
[initial]
);
return result;
if (input == null || isEmpty(input)) {
return [];
}
const chars = [...input];
let initial = chars.shift();
let prevType = getType(initial, compact);
initial = detailed ? { type: prevType, value: initial } : initial;
const result = chars.reduce((tokens, char) => {
const currType = getType(char, compact);
const sameType = currType === prevType;
prevType = currType;
let newValue = char;
if (sameType) {
newValue = (detailed ? tokens.pop().value : tokens.pop()) + newValue;
}
return detailed
? tokens.concat({ type: currType, value: newValue })
: tokens.concat(newValue);
}, [initial]);
return result;
}

@@ -2014,9 +1828,7 @@

const isTrailingWithoutFinalKana = (input, leading) => !leading && !isKana(input[input.length - 1]);
const isInvalidMatcher = (input, matchKanji) =>
(matchKanji && ![...matchKanji].some(isKanji)) || (!matchKanji && isKana(input));
const isInvalidMatcher = (input, matchKanji) => (matchKanji && ![...matchKanji].some(isKanji)) || (!matchKanji && isKana(input));
/**
* Strips [Okurigana](https://en.wikipedia.org/wiki/Okurigana)
* @param {String} input text
* @param {Object} [options={ leading: false, matchKanji: '' }] optional config
* @param {{ leading: Boolean | undefined, matchKanji: string | undefined }} [options={ leading: false, matchKanji: '' }] optional config
* @return {String} text with okurigana removed

@@ -2036,16 +1848,11 @@ * @example

function stripOkurigana(input = '', { leading = false, matchKanji = '' } = {}) {
if (
!isJapanese(input) ||
isLeadingWithoutInitialKana(input, leading) ||
isTrailingWithoutFinalKana(input, leading) ||
isInvalidMatcher(input, matchKanji)
) {
return input;
}
const chars = matchKanji || input;
const okuriganaRegex = new RegExp(
leading ? `^${tokenize(chars).shift()}` : `${tokenize(chars).pop()}$`
);
return input.replace(okuriganaRegex, '');
if (!isJapanese(input) ||
isLeadingWithoutInitialKana(input, leading) ||
isTrailingWithoutFinalKana(input, leading) ||
isInvalidMatcher(input, matchKanji)) {
return input;
}
const chars = matchKanji || input;
const okuriganaRegex = new RegExp(leading ? `^${tokenize(chars).shift()}` : `${tokenize(chars).pop()}$`);
return input.replace(okuriganaRegex, '');
}

@@ -2052,0 +1859,0 @@

{
"name": "wanakana",
"version": "5.2.0",
"version": "5.3.0",
"license": "MIT",

@@ -28,2 +28,3 @@ "homepage": "http://www.wanakana.com",

"main": "cjs/index.js",
"types": "cjs/index.d.ts",
"react-native": "cjs/index.js",

@@ -30,0 +31,0 @@ "module": "esm/index.js",

@@ -1,2 +0,2 @@

!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n="undefined"!=typeof globalThis?globalThis:n||self).wanakana={})}(this,(function(n){"use strict";function t(n){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n},t(n)}function e(n,t){return function(n){if(Array.isArray(n))return n}(n)||function(n,t){var e=null==n?null:"undefined"!=typeof Symbol&&n[Symbol.iterator]||n["@@iterator"];if(null==e)return;var r,o,i=[],a=!0,u=!1;try{for(e=e.call(n);!(a=(r=e.next()).done)&&(i.push(r.value),!t||i.length!==t);a=!0);}catch(n){u=!0,o=n}finally{try{a||null==e.return||e.return()}finally{if(u)throw o}}return i}(n,t)||o(n,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function r(n){return function(n){if(Array.isArray(n))return i(n)}(n)||function(n){if("undefined"!=typeof Symbol&&null!=n[Symbol.iterator]||null!=n["@@iterator"])return Array.from(n)}(n)||o(n)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function o(n,t){if(n){if("string"==typeof n)return i(n,t);var e=Object.prototype.toString.call(n).slice(8,-1);return"Object"===e&&n.constructor&&(e=n.constructor.name),"Map"===e||"Set"===e?Array.from(n):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?i(n,t):void 0}}function i(n,t){(null==t||t>n.length)&&(t=n.length);for(var e=0,r=new Array(t);e<t;e++)r[e]=n[e];return r}function a(n,t){var e="undefined"!=typeof Symbol&&n[Symbol.iterator]||n["@@iterator"];if(!e){if(Array.isArray(n)||(e=o(n))||t&&n&&"number"==typeof n.length){e&&(n=e);var r=0,i=function(){};return{s:i,n:function(){return r>=n.length?{done:!0}:{done:!1,value:n[r++]}},e:function(n){throw n},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,u=!0,c=!1;return{s:function(){e=e.call(n)},n:function(){var n=e.next();return u=n.done,n},e:function(n){c=!0,a=n},f:function(){try{u||null==e.return||e.return()}finally{if(c)throw a}}}}function u(n){return null===n?"null":n!==Object(n)?t(n):{}.toString.call(n).slice(8,-1).toLowerCase()}function c(n){return"string"!==u(n)||!n.length}function f(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1?arguments[1]:void 0,e=arguments.length>2?arguments[2]:void 0;if(c(n))return!1;var r=n.charCodeAt(0);return t<=r&&r<=e}var s={HIRAGANA:"toHiragana",KATAKANA:"toKatakana"},l={HEPBURN:"hepburn"},v={useObsoleteKana:!1,passRomaji:!1,upcaseKatakana:!1,IMEMode:!1,convertLongVowelMark:!0,romanization:l.HEPBURN},d=12353,h=12449,g=[65377,65381],p=[[12288,12351],g,[12539,12540],[65281,65295],[65306,65311],[65339,65343],[65371,65376],[65504,65518]],y=[].concat([[12352,12447],[12448,12543],g,[65382,65439]],p,[[65313,65338],[65345,65370],[65296,65305],[19968,40959],[13312,19903]]),m=[[0,127]].concat([[256,257],[274,275],[298,299],[332,333],[362,363]]),b=[[32,47],[58,63],[91,96],[123,126]].concat([[8216,8217],[8220,8221]]);function j(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return y.some((function(t){var r=e(t,2),o=r[0],i=r[1];return f(n,o,i)}))}function O(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1?arguments[1]:void 0,e="regexp"===u(t);return!c(n)&&r(n).every((function(n){var r=j(n);return e?r||t.test(n):r}))}var E=Number.isNaN||function(n){return"number"==typeof n&&n!=n};function w(n,t){if(n.length!==t.length)return!1;for(var e=0;e<n.length;e++)if(r=n[e],o=t[e],!(r===o||E(r)&&E(o)))return!1;var r,o;return!0}function A(n,t){void 0===t&&(t=w);var e=null;function r(){for(var r=[],o=0;o<arguments.length;o++)r[o]=arguments[o];if(e&&e.lastThis===this&&t(r,e.lastArgs))return e.lastResult;var i=n.apply(this,r);return e={lastResult:i,lastArgs:r,lastThis:this},i}return r.clear=function(){e=null},r}var S=Object.prototype.hasOwnProperty;function k(n,t,e){var r,o=a(n.keys());try{for(o.s();!(r=o.n()).done;)if(N(e=r.value,t))return e}catch(n){o.e(n)}finally{o.f()}}function N(n,e){var r,o,i;if(n===e)return!0;if(n&&e&&(r=n.constructor)===e.constructor){if(r===Date)return n.getTime()===e.getTime();if(r===RegExp)return n.toString()===e.toString();if(r===Array){if((o=n.length)===e.length)for(;o--&&N(n[o],e[o]););return-1===o}if(r===Set){if(n.size!==e.size)return!1;var u,c=a(n);try{for(c.s();!(u=c.n()).done;){if((i=o=u.value)&&"object"===t(i)&&!(i=k(e,i)))return!1;if(!e.has(i))return!1}}catch(n){c.e(n)}finally{c.f()}return!0}if(r===Map){if(n.size!==e.size)return!1;var f,s=a(n);try{for(s.s();!(f=s.n()).done;){if((i=(o=f.value)[0])&&"object"===t(i)&&!(i=k(e,i)))return!1;if(!N(o[1],e.get(i)))return!1}}catch(n){s.e(n)}finally{s.f()}return!0}if(r===ArrayBuffer)n=new Uint8Array(n),e=new Uint8Array(e);else if(r===DataView){if((o=n.byteLength)===e.byteLength)for(;o--&&n.getInt8(o)===e.getInt8(o););return-1===o}if(ArrayBuffer.isView(n)){if((o=n.byteLength)===e.byteLength)for(;o--&&n[o]===e[o];);return-1===o}if(!r||"object"===t(n)){for(r in o=0,n){if(S.call(n,r)&&++o&&!S.call(e,r))return!1;if(!(r in e)||!N(n[r],e[r]))return!1}return Object.keys(e).length===o}}return n!=n&&e!=e}var M=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return Object.assign({},v,n)};function C(n,t,e){var r=t;function o(n,t){var e=n.charAt(0);return i(Object.assign({"":e},r[e]),n.slice(1),t,t+1)}function i(n,t,r,a){if(!t)return e||1===Object.keys(n).length?n[""]?[[r,a,n[""]]]:[]:[[r,a,null]];if(1===Object.keys(n).length)return[[r,a,n[""]]].concat(o(t,a));var u=function(n,t){if(void 0!==n[t])return Object.assign({"":n[""]+t},n[t])}(n,t.charAt(0));return void 0===u?[[r,a,n[""]]].concat(o(t,a)):i(u,t.slice(1),r,a+1)}return o(n,0)}function R(n){return Object.entries(n).reduce((function(n,t){var r=e(t,2),o=r[0],i=r[1],a="string"===u(i);return n[o]=a?{"":i}:R(i),n}),{})}function I(n,t){return t.split("").reduce((function(n,t){return void 0===n[t]&&(n[t]={}),n[t]}),n)}function K(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t={};return"object"===u(n)&&Object.entries(n).forEach((function(n){var r=e(n,2),o=r[0],i=r[1],a=t;o.split("").forEach((function(n){void 0===a[n]&&(a[n]={}),a=a[n]})),a[""]=i})),function(n){return function n(t,r){return void 0===t||"string"===u(t)?r:Object.entries(r).reduce((function(r,o){var i=e(o,2),a=i[0],u=i[1];return r[a]=n(t[a],u),r}),t)}(JSON.parse(JSON.stringify(n)),t)}}function z(n,t){return t?"function"===u(t)?t(n):K(t)(n):n}var L={a:"あ",i:"い",u:"う",e:"え",o:"お",k:{a:"か",i:"き",u:"く",e:"け",o:"こ"},s:{a:"さ",i:"し",u:"す",e:"せ",o:"そ"},t:{a:"た",i:"ち",u:"つ",e:"て",o:"と"},n:{a:"な",i:"に",u:"ぬ",e:"ね",o:"の"},h:{a:"は",i:"ひ",u:"ふ",e:"へ",o:"ほ"},m:{a:"ま",i:"み",u:"む",e:"め",o:"も"},y:{a:"や",u:"ゆ",o:"よ"},r:{a:"ら",i:"り",u:"る",e:"れ",o:"ろ"},w:{a:"わ",i:"ゐ",e:"ゑ",o:"を"},g:{a:"が",i:"ぎ",u:"ぐ",e:"げ",o:"ご"},z:{a:"ざ",i:"じ",u:"ず",e:"ぜ",o:"ぞ"},d:{a:"だ",i:"ぢ",u:"づ",e:"で",o:"ど"},b:{a:"ば",i:"び",u:"ぶ",e:"べ",o:"ぼ"},p:{a:"ぱ",i:"ぴ",u:"ぷ",e:"ぺ",o:"ぽ"},v:{a:"ゔぁ",i:"ゔぃ",u:"ゔ",e:"ゔぇ",o:"ゔぉ"}},T={".":"。",",":"、",":":":","/":"・","!":"!","?":"?","~":"〜","-":"ー","‘":"「","’":"」","“":"『","”":"』","[":"[","]":"]","(":"(",")":")","{":"{","}":"}"},J={k:"き",s:"し",t:"ち",n:"に",h:"ひ",m:"み",r:"り",g:"ぎ",z:"じ",d:"ぢ",b:"び",p:"ぴ",v:"ゔ",q:"く",f:"ふ"},x={ya:"ゃ",yi:"ぃ",yu:"ゅ",ye:"ぇ",yo:"ょ"},H={a:"ぁ",i:"ぃ",u:"ぅ",e:"ぇ",o:"ぉ"},U={sh:"sy",ch:"ty",cy:"ty",chy:"ty",shy:"sy",j:"zy",jy:"zy",shi:"si",chi:"ti",tsu:"tu",ji:"zi",fu:"hu"},P=Object.assign({tu:"っ",wa:"ゎ",ka:"ヵ",ke:"ヶ"},H,x),D={yi:"い",wu:"う",ye:"いぇ",wi:"うぃ",we:"うぇ",kwa:"くぁ",whu:"う",tha:"てゃ",thu:"てゅ",tho:"てょ",dha:"でゃ",dhu:"でゅ",dho:"でょ"},q={wh:"う",kw:"く",qw:"く",q:"く",gw:"ぐ",sw:"す",ts:"つ",th:"て",tw:"と",dh:"で",dw:"ど",fw:"ふ",f:"ふ"};function B(){var n=R(L),t=function(t){return I(n,t)};function o(n){return Object.entries(n).reduce((function(n,t){var r=e(t,2),i=r[0],a=r[1];return n[i]=i?o(a):"っ".concat(a),n}),{})}return Object.entries(J).forEach((function(n){var r=e(n,2),o=r[0],i=r[1];Object.entries(x).forEach((function(n){var r=e(n,2),a=r[0],u=r[1];t(o+a)[""]=i+u}))})),Object.entries(T).forEach((function(n){var r=e(n,2),o=r[0],i=r[1];t(o)[""]=i})),Object.entries(q).forEach((function(n){var r=e(n,2),o=r[0],i=r[1];Object.entries(H).forEach((function(n){var r=e(n,2),a=r[0],u=r[1];t(o+a)[""]=i+u}))})),["n","n'","xn"].forEach((function(n){t(n)[""]="ん"})),n.c=JSON.parse(JSON.stringify(n.k)),Object.entries(U).forEach((function(n){var r=e(n,2),o=r[0],i=r[1],a=o.slice(0,o.length-1),u=o.charAt(o.length-1);t(a)[u]=JSON.parse(JSON.stringify(t(i)))})),Object.entries(P).forEach((function(n){var o,i=e(n,2),a=i[0],u=i[1],c=function(n){return n.charAt(n.length-1)},f=function(n){return n.slice(0,n.length-1)},s="x".concat(a),l=t(s);l[""]=u,t("l".concat(f(a)))[c(a)]=l,(o=a,[].concat(r(Object.entries(U)),[["c","k"]]).reduce((function(n,t){var r=e(t,2),i=r[0],a=r[1];return o.startsWith(a)?n.concat(o.replace(a,i)):n}),[])).forEach((function(n){["l","x"].forEach((function(e){t(e+f(n))[c(n)]=t(e+a)}))}))})),Object.entries(D).forEach((function(n){var r=e(n,2),o=r[0],i=r[1];t(o)[""]=i})),[].concat(r(Object.keys(J)),["c","y","w","j"]).forEach((function(t){var e=n[t];e[t]=o(e)})),delete n.n.n,Object.freeze(JSON.parse(JSON.stringify(n)))}var V=null;var W=K({wi:"ゐ",we:"ゑ"});function G(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&f(n,65,90)}function $(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&12540===n.charCodeAt(0)}function _(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&12539===n.charCodeAt(0)}function X(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&(!!$(n)||f(n,d,12438))}function Z(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=[];return n.split("").forEach((function(n){if($(n)||_(n))t.push(n);else if(X(n)){var e=n.charCodeAt(0)+96,r=String.fromCharCode(e);t.push(r)}else t.push(n)})),t.join("")}var F=A((function(n,t,e){var r=(null==V&&(V=B()),V);return r=n?function(n){var t=JSON.parse(JSON.stringify(n));return t.n.n={"":"ん"},t.n[" "]={"":"ん"},t}(r):r,r=t?W(r):r,e&&(r=z(r,e)),r}),N);function Q(){var n,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2?arguments[2]:void 0;return i?n=o:(n=M(o),i=F(n.IMEMode,n.useObsoleteKana,n.customKanaMapping)),Y(t,n,i).map((function(o){var i=e(o,3),a=i[0],u=i[1],c=i[2];if(null===c)return t.slice(a);var f=n.IMEMode===s.HIRAGANA,l=n.IMEMode===s.KATAKANA||r(t.slice(a,u)).every(G);return f||!l?c:Z(c)})).join("")}function Y(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=arguments.length>2?arguments[2]:void 0,r=t.IMEMode,o=t.useObsoleteKana,i=t.customKanaMapping;return e||(e=F(r,o,i)),C(n.toLowerCase(),e,!r)}var nn=[];function tn(n){var t=Object.assign({},M(n),{IMEMode:n.IMEMode||!0}),o=F(t.IMEMode,t.useObsoleteKana,t.customKanaMapping),i=[].concat(r(Object.keys(o)),r(Object.keys(o).map((function(n){return n.toUpperCase()}))));return function(n){var r=n.target;undefined!==r.value&&"true"!==r.dataset.ignoreComposition&&function(n,t,r,o,i){var a=e(function(){var n,t,r,o=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];if(0===i&&a.includes(o[0])){var u=e(un(o,a),3);n=u[0],t=u[1],r=u[2]}else if(i>0){var c=e(cn(o,i),3);n=c[0],t=c[1],r=c[2]}else{var f=e(fn(o,(function(n){return!a.includes(n)})),2);n=f[0];var s=e(fn(t=f[1],(function(n){return!O(n)})),2);t=s[0],r=s[1]}return[n,t,r]}(n.value,n.selectionEnd,o),3),u=a[0],c=a[1],f=a[2],s=Q(c,t,r);if(c!==s){var l=u.length+s.length,v=u+s+f;n.value=v,f.length?setTimeout((function(){return n.setSelectionRange(l,l)}),1):n.setSelectionRange(l,l)}}(r,t,o,i)}}function en(n){var t=n.type,e=n.target,r=n.data;/Mac/.test(window.navigator&&window.navigator.platform)&&("compositionupdate"===t&&O(r)&&(e.dataset.ignoreComposition="true"),"compositionend"===t&&(e.dataset.ignoreComposition="false"))}function rn(n,t,e){nn=nn.concat({id:n,inputHandler:t,compositionHandler:e})}function on(n){var t=n.id;nn=nn.filter((function(n){return n.id!==t}))}function an(n){return n&&nn.find((function(t){return t.id===n.getAttribute("data-wanakana-id")}))}function un(n,t){return[""].concat(r(fn(n,(function(n){return t.includes(n)||!O(n,/[0-9]/)}))))}function cn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,o=fn(r(n.slice(0,t)).reverse(),(function(n){return!O(n)})),i=e(o,2),a=i[0],u=i[1];return[u.reverse().join(""),a.split("").reverse().join(""),n.slice(t)]}function fn(){for(var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:function(n){return!!n},e=[],r=n.length,o=0;o<r&&t(n[o],o);)e.push(n[o]),o+=1;return[e.join(""),n.slice(o)]}var sn={input:function(n){var t=n.target,e=t.value,r=t.selectionStart,o=t.selectionEnd;return console.log("input:",{value:e,selectionStart:r,selectionEnd:o})},compositionstart:function(){return console.log("compositionstart")},compositionupdate:function(n){var t=n.target,e=t.value,r=t.selectionStart,o=t.selectionEnd,i=n.data;return console.log("compositionupdate",{data:i,value:e,selectionStart:r,selectionEnd:o})},compositionend:function(){return console.log("compositionend")}},ln=function(n){Object.entries(sn).forEach((function(t){var r=e(t,2),o=r[0],i=r[1];return n.addEventListener(o,i)}))},vn=function(n){Object.entries(sn).forEach((function(t){var r=e(t,2),o=r[0],i=r[1];return n.removeEventListener(o,i)}))},dn=["TEXTAREA","INPUT"],hn=0,gn=function(){return hn+=1,"".concat(Date.now()).concat(hn)};function pn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&m.some((function(t){var r=e(t,2),o=r[0],i=r[1];return f(n,o,i)}))}function yn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1?arguments[1]:void 0,e="regexp"===u(t);return!c(n)&&r(n).every((function(n){var r=pn(n);return e?r||t.test(n):r}))}function mn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return f(n,h,12540)}function bn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&(X(n)||mn(n))}function jn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&r(n).every(bn)}function On(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&r(n).every(X)}function En(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&r(n).every(mn)}function wn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&12293===n.charCodeAt(0)}function An(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return f(n,19968,40879)||wn(n)}function Sn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&r(n).every(An)}function kn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{passKanji:!0},e=r(n),o=!1;return t.passKanji||(o=e.some(Sn)),(e.some(On)||e.some(En))&&e.some(yn)&&!o}var Nn=function(n,t){return $(n)&&t<1},Mn=function(n,t){return $(n)&&t>0},Cn=function(n){return["ヶ","ヵ"].includes(n)},Rn={a:"あ",i:"い",u:"う",e:"え",o:"う"};function In(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1?arguments[1]:void 0,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=e.isDestinationRomaji,o=e.convertLongVowelMark,i="";return n.split("").reduce((function(e,a,u){if(_(a)||Nn(a,u)||Cn(a))return e.concat(a);if(o&&i&&Mn(a,u)){var c=t(i).slice(-1);return mn(n[u-1])&&"o"===c&&r?e.concat("お"):e.concat(Rn[c])}if(!$(a)&&mn(a)){var f=a.charCodeAt(0)+-96,s=String.fromCharCode(f);return i=s,e.concat(s)}return i="",e.concat(a)}),[]).join("")}var Kn=null,zn={"あ":"a","い":"i","う":"u","え":"e","お":"o","か":"ka","き":"ki","く":"ku","け":"ke","こ":"ko","さ":"sa","し":"shi","す":"su","せ":"se","そ":"so","た":"ta","ち":"chi","つ":"tsu","て":"te","と":"to","な":"na","に":"ni","ぬ":"nu","ね":"ne","の":"no","は":"ha","ひ":"hi","ふ":"fu","へ":"he","ほ":"ho","ま":"ma","み":"mi","む":"mu","め":"me","も":"mo","ら":"ra","り":"ri","る":"ru","れ":"re","ろ":"ro","や":"ya","ゆ":"yu","よ":"yo","わ":"wa","ゐ":"wi","ゑ":"we","を":"wo","ん":"n","が":"ga","ぎ":"gi","ぐ":"gu","げ":"ge","ご":"go","ざ":"za","じ":"ji","ず":"zu","ぜ":"ze","ぞ":"zo","だ":"da","ぢ":"ji","づ":"zu","で":"de","ど":"do","ば":"ba","び":"bi","ぶ":"bu","べ":"be","ぼ":"bo","ぱ":"pa","ぴ":"pi","ぷ":"pu","ぺ":"pe","ぽ":"po","ゔぁ":"va","ゔぃ":"vi","ゔ":"vu","ゔぇ":"ve","ゔぉ":"vo"},Ln={"。":".","、":",",":":":","・":"/","!":"!","?":"?","〜":"~","ー":"-","「":"‘","」":"’","『":"“","』":"”","[":"[","]":"]","(":"(",")":")","{":"{","}":"}"," ":" "},Tn=["あ","い","う","え","お","や","ゆ","よ"],Jn={"ゃ":"ya","ゅ":"yu","ょ":"yo"},xn={"ぃ":"yi","ぇ":"ye"},Hn={"ぁ":"a","ぃ":"i","ぅ":"u","ぇ":"e","ぉ":"o"},Un=["き","に","ひ","み","り","ぎ","び","ぴ","ゔ","く","ふ"],Pn={"し":"sh","ち":"ch","じ":"j","ぢ":"j"},Dn={"っ":"","ゃ":"ya","ゅ":"yu","ょ":"yo","ぁ":"a","ぃ":"i","ぅ":"u","ぇ":"e","ぉ":"o"},qn={b:"b",c:"t",d:"d",f:"f",g:"g",h:"h",j:"j",k:"k",m:"m",p:"p",q:"q",r:"r",s:"s",t:"t",v:"v",w:"w",x:"x",z:"z"};function Bn(){var n,t,o;return null==Kn&&(n=R(zn),t=function(t){return I(n,t)},o=function(n,e){t(n)[""]=e},Object.entries(Ln).forEach((function(n){var r=e(n,2),o=r[0],i=r[1];t(o)[""]=i})),[].concat(r(Object.entries(Jn)),r(Object.entries(Hn))).forEach((function(n){var t=e(n,2),r=t[0],i=t[1];o(r,i)})),Un.forEach((function(n){var r=t(n)[""][0];Object.entries(Jn).forEach((function(t){var i=e(t,2),a=i[0],u=i[1];o(n+a,r+u)})),Object.entries(xn).forEach((function(t){var i=e(t,2),a=i[0],u=i[1];o(n+a,r+u)}))})),Object.entries(Pn).forEach((function(n){var t=e(n,2),r=t[0],i=t[1];Object.entries(Jn).forEach((function(n){var t=e(n,2),a=t[0],u=t[1];o(r+a,i+u[1])})),o("".concat(r,"ぃ"),"".concat(i,"yi")),o("".concat(r,"ぇ"),"".concat(i,"e"))})),n["っ"]=Vn(n),Object.entries(Dn).forEach((function(n){var t=e(n,2),r=t[0],i=t[1];o(r,i)})),Tn.forEach((function(n){o("ん".concat(n),"n'".concat(t(n)[""]))})),Kn=Object.freeze(JSON.parse(JSON.stringify(n)))),Kn}function Vn(n){return Object.entries(n).reduce((function(n,t){var r=e(t,2),o=r[0],i=r[1];if(o)n[o]=Vn(i);else{var a=i.charAt(0);n[o]=Object.keys(qn).includes(a)?qn[a]+i:i}return n}),{})}var Wn=A((function(n,t){var e=function(n){return n===l.HEPBURN?Bn():{}}(n);return t&&(e=z(e,t)),e}),N);function Gn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2?arguments[2]:void 0,o=M(t);return r||(r=Wn(o.romanization,o.customRomajiMapping)),$n(n,o,r).map((function(t){var r=e(t,3),i=r[0],a=r[1],u=r[2];return o.upcaseKatakana&&En(n.slice(i,a))?u.toUpperCase():u})).join("")}function $n(n,t,e){return e||(e=Wn(t.romanization,t.customRomajiMapping)),C(In(n,Gn,Object.assign({},{isDestinationRomaji:!0},t)),e,!t.IMEMode)}function _n(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&b.some((function(t){var r=e(t,2),o=r[0],i=r[1];return f(n,o,i)}))}function Xn(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!c(n)&&!wn(n)&&p.some((function(t){var r=e(t,2),o=r[0],i=r[1];return f(n,o,i)}))}var Zn=function(n){return" "===n},Fn=function(n){return" "===n},Qn=function(n){return/[0-9]/.test(n)},Yn=function(n){return/[0-9]/.test(n)},nt="en",tt="ja",et="englishNumeral",rt="japaneseNumeral",ot="englishPunctuation",it="japanesePunctuation",at="kanji",ut="hiragana",ct="katakana",ft="space",st="other";function lt(n){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],e=nt,r=tt,o=et,i=rt,a=ot,u=it,c=at,f=ut,s=ct,l=ft,v=st;if(t)switch(!0){case Qn(n):case Yn(n):return v;case Zn(n):return e;case _n(n):return v;case Fn(n):return r;case Xn(n):return v;case j(n):return r;case pn(n):return e;default:return v}else switch(!0){case Fn(n):case Zn(n):return l;case Qn(n):return i;case Yn(n):return o;case _n(n):return a;case Xn(n):return u;case An(n):return c;case X(n):return f;case mn(n):return s;case j(n):return r;case pn(n):return e;default:return v}}function vt(n){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=t.compact,o=void 0!==e&&e,i=t.detailed,a=void 0!==i&&i;if(null==n||c(n))return[];var u=r(n),f=u.shift(),s=lt(f,o);f=a?{type:s,value:f}:f;var l=u.reduce((function(n,t){var e=lt(t,o),r=e===s;s=e;var i=t;return r&&(i=(a?n.pop().value:n.pop())+i),a?n.concat({type:e,value:i}):n.concat(i)}),[f]);return l}var dt=function(n,t){return t&&!jn(n[0])},ht=function(n,t){return!t&&!jn(n[n.length-1])},gt=function(n,t){return t&&!r(t).some(Sn)||!t&&jn(n)};n.ROMANIZATIONS=l,n.TO_KANA_METHODS=s,n.VERSION="5.2.0",n.bind=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(!dn.includes(n.nodeName))throw new Error("Element provided to Wanakana bind() was not a valid input or textarea element.\n Received: (".concat(JSON.stringify(n),")"));if(!n.hasAttribute("data-wanakana-id")){var r=tn(t),o=gn(),i=[{name:"data-wanakana-id",value:o},{name:"lang",value:"ja"},{name:"autoCapitalize",value:"none"},{name:"autoCorrect",value:"off"},{name:"autoComplete",value:"off"},{name:"spellCheck",value:"false"}],a={};i.forEach((function(t){a[t.name]=n.getAttribute(t.name),n.setAttribute(t.name,t.value)})),n.dataset.previousAttributes=JSON.stringify(a),n.addEventListener("input",r),n.addEventListener("compositionupdate",en),n.addEventListener("compositionend",en),rn(o,r,en),!0===e&&ln(n)}},n.isHiragana=On,n.isJapanese=O,n.isKana=jn,n.isKanji=Sn,n.isKatakana=En,n.isMixed=kn,n.isRomaji=yn,n.stripOkurigana=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=t.leading,r=void 0!==e&&e,o=t.matchKanji,i=void 0===o?"":o;if(!O(n)||dt(n,r)||ht(n,r)||gt(n,i))return n;var a=i||n,u=new RegExp(r?"^".concat(vt(a).shift()):"".concat(vt(a).pop(),"$"));return n.replace(u,"")},n.toHiragana=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=M(t);if(e.passRomaji)return In(n,Gn,e);if(kn(n,{passKanji:!0})){var r=In(n,Gn,e);return Q(r.toLowerCase(),e)}return yn(n)||_n(n)?Q(n.toLowerCase(),e):In(n,Gn,e)},n.toKana=Q,n.toKatakana=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=M(t);if(e.passRomaji)return Z(n);if(kn(n)||yn(n)||_n(n)){var r=Q(n.toLowerCase(),e);return Z(r)}return Z(n)},n.toRomaji=Gn,n.tokenize=vt,n.unbind=function(n){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],e=an(n);if(null==e)throw new Error("Element provided to Wanakana unbind() had no listener registered.\n Received: ".concat(JSON.stringify(n)));var r=e.inputHandler,o=e.compositionHandler,i=JSON.parse(n.dataset.previousAttributes);Object.keys(i).forEach((function(t){i[t]?n.setAttribute(t,i[t]):n.removeAttribute(t)})),n.removeAttribute("data-previous-attributes"),n.removeAttribute("data-ignore-composition"),n.removeEventListener("input",r),n.removeEventListener("compositionstart",o),n.removeEventListener("compositionupdate",o),n.removeEventListener("compositionend",o),on(e),!0===t&&vn(n)}}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).wanakana={})}(this,(function(e){"use strict";function t(e){return null===e?"null":e!==Object(e)?typeof e:{}.toString.call(e).slice(8,-1).toLowerCase()}function n(e){return"string"!==t(e)||!e.length}function r(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1?arguments[1]:void 0,r=arguments.length>2?arguments[2]:void 0;if(n(e))return!1;const o=e.charCodeAt(0);return t<=o&&o<=r}const o={HIRAGANA:"toHiragana",KATAKANA:"toKatakana"},i={HEPBURN:"hepburn"},a={useObsoleteKana:!1,passRomaji:!1,convertLongVowelMark:!0,upcaseKatakana:!1,IMEMode:!1,romanization:i.HEPBURN},u=65,s=90,c=12353,l=12438,f=12449,d=12540,h=19968,g=40879,v=12293,p=12540,m=12539,b=[65313,65338],j=[65345,65370],y=[65377,65381],E=[[12288,12351],y,[12539,12540],[65281,65295],[65306,65311],[65339,65343],[65371,65376],[65504,65518]],A=[...[[12352,12447],[12448,12543],y,[65382,65439]],...E,b,j,[65296,65305],[19968,40959],[13312,19903]],O=[[0,127],[256,257],[274,275],[298,299],[332,333],[362,363]],N=[[32,47],[58,63],[91,96],[123,126],[8216,8217],[8220,8221]];function w(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return A.some((t=>{let[n,o]=t;return r(e,n,o)}))}function k(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",r=arguments.length>1?arguments[1]:void 0;const o="regexp"===t(r);return!n(e)&&[...e].every((e=>{const t=w(e);return o?t||r.test(e):t}))}var M=Number.isNaN||function(e){return"number"==typeof e&&e!=e};function S(e,t){if(e.length!==t.length)return!1;for(var n=0;n<e.length;n++)if(r=e[n],o=t[n],!(r===o||M(r)&&M(o)))return!1;var r,o;return!0}function C(e,t){void 0===t&&(t=S);var n=null;function r(){for(var r=[],o=0;o<arguments.length;o++)r[o]=arguments[o];if(n&&n.lastThis===this&&t(r,n.lastArgs))return n.lastResult;var i=e.apply(this,r);return n={lastResult:i,lastArgs:r,lastThis:this},i}return r.clear=function(){n=null},r}var K=Object.prototype.hasOwnProperty;function R(e,t,n){for(n of e.keys())if(J(n,t))return n}function J(e,t){var n,r,o;if(e===t)return!0;if(e&&t&&(n=e.constructor)===t.constructor){if(n===Date)return e.getTime()===t.getTime();if(n===RegExp)return e.toString()===t.toString();if(n===Array){if((r=e.length)===t.length)for(;r--&&J(e[r],t[r]););return-1===r}if(n===Set){if(e.size!==t.size)return!1;for(r of e){if((o=r)&&"object"==typeof o&&!(o=R(t,o)))return!1;if(!t.has(o))return!1}return!0}if(n===Map){if(e.size!==t.size)return!1;for(r of e){if((o=r[0])&&"object"==typeof o&&!(o=R(t,o)))return!1;if(!J(r[1],t.get(o)))return!1}return!0}if(n===ArrayBuffer)e=new Uint8Array(e),t=new Uint8Array(t);else if(n===DataView){if((r=e.byteLength)===t.byteLength)for(;r--&&e.getInt8(r)===t.getInt8(r););return-1===r}if(ArrayBuffer.isView(e)){if((r=e.byteLength)===t.byteLength)for(;r--&&e[r]===t[r];);return-1===r}if(!n||"object"==typeof e){for(n in r=0,e){if(K.call(e,n)&&++r&&!K.call(t,n))return!1;if(!(n in t)||!J(e[n],t[n]))return!1}return Object.keys(t).length===r}}return e!=e&&t!=t}const z=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return Object.assign({},a,e)};function I(e,t,n){const r=t;function o(e,t){const n=e.charAt(0);return i(Object.assign({"":n},r[n]),e.slice(1),t,t+1)}function i(e,t,r,a){if(!t)return n||1===Object.keys(e).length?e[""]?[[r,a,e[""]]]:[]:[[r,a,null]];if(1===Object.keys(e).length)return[[r,a,e[""]]].concat(o(t,a));const u=function(e,t){if(void 0!==e[t])return Object.assign({"":e[""]+t},e[t])}(e,t.charAt(0));return void 0===u?[[r,a,e[""]]].concat(o(t,a)):i(u,t.slice(1),r,a+1)}return o(e,0)}function L(e){return Object.entries(e).reduce(((e,n)=>{let[r,o]=n;const i="string"===t(o);return e[r]=i?{"":o}:L(o),e}),{})}function T(e,t){return t.split("").reduce(((e,t)=>(void 0===e[t]&&(e[t]={}),e[t])),e)}function H(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const n={};return"object"===t(e)&&Object.entries(e).forEach((e=>{let[t,r]=e,o=n;t.split("").forEach((e=>{void 0===o[e]&&(o[e]={}),o=o[e]})),o[""]=r})),function(e){return function e(n,r){return void 0===n||"string"===t(n)?r:Object.entries(r).reduce(((t,r)=>{let[o,i]=r;return t[o]=e(n[o],i),t}),n)}(JSON.parse(JSON.stringify(e)),n)}}function U(e,n){return n?"function"===t(n)?n(e):H(n)(e):e}const $={a:"あ",i:"い",u:"う",e:"え",o:"お",k:{a:"か",i:"き",u:"く",e:"け",o:"こ"},s:{a:"さ",i:"し",u:"す",e:"せ",o:"そ"},t:{a:"た",i:"ち",u:"つ",e:"て",o:"と"},n:{a:"な",i:"に",u:"ぬ",e:"ね",o:"の"},h:{a:"は",i:"ひ",u:"ふ",e:"へ",o:"ほ"},m:{a:"ま",i:"み",u:"む",e:"め",o:"も"},y:{a:"や",u:"ゆ",o:"よ"},r:{a:"ら",i:"り",u:"る",e:"れ",o:"ろ"},w:{a:"わ",i:"ゐ",e:"ゑ",o:"を"},g:{a:"が",i:"ぎ",u:"ぐ",e:"げ",o:"ご"},z:{a:"ざ",i:"じ",u:"ず",e:"ぜ",o:"ぞ"},d:{a:"だ",i:"ぢ",u:"づ",e:"で",o:"ど"},b:{a:"ば",i:"び",u:"ぶ",e:"べ",o:"ぼ"},p:{a:"ぱ",i:"ぴ",u:"ぷ",e:"ぺ",o:"ぽ"},v:{a:"ゔぁ",i:"ゔぃ",u:"ゔ",e:"ゔぇ",o:"ゔぉ"}},x={".":"。",",":"、",":":":","/":"・","!":"!","?":"?","~":"〜","-":"ー","‘":"「","’":"」","“":"『","”":"』","[":"[","]":"]","(":"(",")":")","{":"{","}":"}"},P={k:"き",s:"し",t:"ち",n:"に",h:"ひ",m:"み",r:"り",g:"ぎ",z:"じ",d:"ぢ",b:"び",p:"ぴ",v:"ゔ",q:"く",f:"ふ"},_={ya:"ゃ",yi:"ぃ",yu:"ゅ",ye:"ぇ",yo:"ょ"},D={a:"ぁ",i:"ぃ",u:"ぅ",e:"ぇ",o:"ぉ"},q={sh:"sy",ch:"ty",cy:"ty",chy:"ty",shy:"sy",j:"zy",jy:"zy",shi:"si",chi:"ti",tsu:"tu",ji:"zi",fu:"hu"},B=Object.assign({tu:"っ",wa:"ゎ",ka:"ヵ",ke:"ヶ"},D,_),V={yi:"い",wu:"う",ye:"いぇ",wi:"うぃ",we:"うぇ",kwa:"くぁ",whu:"う",tha:"てゃ",thu:"てゅ",tho:"てょ",dha:"でゃ",dhu:"でゅ",dho:"でょ"},G={wh:"う",kw:"く",qw:"く",q:"く",gw:"ぐ",sw:"す",ts:"つ",th:"て",tw:"と",dh:"で",dw:"ど",fw:"ふ",f:"ふ"};function W(){const e=L($),t=t=>T(e,t);function n(e){return Object.entries(e).reduce(((e,t)=>{let[r,o]=t;return e[r]=r?n(o):`っ${o}`,e}),{})}return Object.entries(P).forEach((e=>{let[n,r]=e;Object.entries(_).forEach((e=>{let[o,i]=e;t(n+o)[""]=r+i}))})),Object.entries(x).forEach((e=>{let[n,r]=e;t(n)[""]=r})),Object.entries(G).forEach((e=>{let[n,r]=e;Object.entries(D).forEach((e=>{let[o,i]=e;t(n+o)[""]=r+i}))})),["n","n'","xn"].forEach((e=>{t(e)[""]="ん"})),e.c=JSON.parse(JSON.stringify(e.k)),Object.entries(q).forEach((e=>{let[n,r]=e;const o=n.slice(0,n.length-1),i=n.charAt(n.length-1);t(o)[i]=JSON.parse(JSON.stringify(t(r)))})),Object.entries(B).forEach((e=>{let[n,r]=e;const o=e=>e.charAt(e.length-1),i=e=>e.slice(0,e.length-1),a=t(`x${n}`);a[""]=r;var u;t(`l${i(n)}`)[o(n)]=a,(u=n,[...Object.entries(q),["c","k"]].reduce(((e,t)=>{let[n,r]=t;return u.startsWith(r)?e.concat(u.replace(r,n)):e}),[])).forEach((e=>{["l","x"].forEach((r=>{t(r+i(e))[o(e)]=t(r+n)}))}))})),Object.entries(V).forEach((e=>{let[n,r]=e;t(n)[""]=r})),[...Object.keys(P),"c","y","w","j"].forEach((t=>{const r=e[t];r[t]=n(r)})),delete e.n.n,Object.freeze(JSON.parse(JSON.stringify(e)))}let X=null;const Z=H({wi:"ゐ",we:"ゑ"});function F(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&r(e,u,s)}function Q(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&e.charCodeAt(0)===p}function Y(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&e.charCodeAt(0)===m}function ee(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&(!!Q(e)||r(e,c,l))}function te(){const e=[];return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"").split("").forEach((t=>{if(Q(t)||Y(t))e.push(t);else if(ee(t)){const n=t.charCodeAt(0)+(f-c),r=String.fromCharCode(n);e.push(r)}else e.push(t)})),e.join("")}const ne=C(((e,t,n)=>{let r=(null==X&&(X=W()),X);return r=e?function(e){const t=JSON.parse(JSON.stringify(e));return t.n.n={"":"ん"},t.n[" "]={"":"ん"},t}(r):r,r=t?Z(r):r,n&&(r=U(r,n)),r}),J);function re(){let e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2?arguments[2]:void 0;return r?e=n:(e=z(n),r=ne(e.IMEMode,e.useObsoleteKana,e.customKanaMapping)),function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;const{IMEMode:r,useObsoleteKana:o,customKanaMapping:i}=t;n||(n=ne(r,o,i));return I(e.toLowerCase(),n,!r)}(t,e,r).map((n=>{const[r,i,a]=n;if(null===a)return t.slice(r);const u=e.IMEMode===o.HIRAGANA,s=e.IMEMode===o.KATAKANA||[...t.slice(r,i)].every(F);return u||!s?a:te(a)})).join("")}let oe=[];function ie(e){const t=Object.assign({},z(e),{IMEMode:e.IMEMode||!0}),n=ne(t.IMEMode,t.useObsoleteKana,t.customKanaMapping),r=[...Object.keys(n),...Object.keys(n).map((e=>e.toUpperCase()))];return function(e){let{target:o}=e;undefined!==o.value&&"true"!==o.dataset.ignoreComposition&&function(e,t,n,r,o){const[i,a,u]=function(){let e,t,n,r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];0===o&&i.includes(r[0])?[e,t,n]=function(e,t){return["",...ue(e,(e=>t.includes(e)||!k(e,/[0-9]/)))]}(r,i):o>0?[e,t,n]=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;const[n,r]=ue([...e.slice(0,t)].reverse(),(e=>!k(e)));return[r.reverse().join(""),n.split("").reverse().join(""),e.slice(t)]}(r,o):([e,t]=ue(r,(e=>!i.includes(e))),[t,n]=ue(t,(e=>!k(e))));return[e,t,n]}(e.value,e.selectionEnd,r),s=re(a,t,n);if(a!==s){const t=i.length+s.length,n=i+s+u;e.value=n,u.length?setTimeout((()=>e.setSelectionRange(t,t)),1):e.setSelectionRange(t,t)}else e.value}(o,t,n,r)}}function ae(e){let{type:t,target:n,data:r}=e;/Mac/.test(window.navigator&&window.navigator.platform)&&("compositionupdate"===t&&k(r)&&(n.dataset.ignoreComposition="true"),"compositionend"===t&&(n.dataset.ignoreComposition="false"))}function ue(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:e=>!!e;const n=[],{length:r}=e;let o=0;for(;o<r&&t(e[o],o);)n.push(e[o]),o+=1;return[n.join(""),e.slice(o)]}const se={input:e=>{let{target:{value:t,selectionStart:n,selectionEnd:r}}=e;return console.log("input:",{value:t,selectionStart:n,selectionEnd:r})},compositionstart:()=>console.log("compositionstart"),compositionupdate:e=>{let{target:{value:t,selectionStart:n,selectionEnd:r},data:o}=e;return console.log("compositionupdate",{data:o,value:t,selectionStart:n,selectionEnd:r})},compositionend:()=>console.log("compositionend")},ce=["TEXTAREA","INPUT"];let le=0;function fe(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&O.some((t=>{let[n,o]=t;return r(e,n,o)}))}function de(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",r=arguments.length>1?arguments[1]:void 0;const o="regexp"===t(r);return!n(e)&&[...e].every((e=>{const t=fe(e);return o?t||r.test(e):t}))}function he(){return r(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",f,d)}function ge(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&(ee(e)||he(e))}function ve(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&[...e].every(ge)}function pe(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&[...e].every(ee)}function me(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&[...e].every(he)}function be(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&e.charCodeAt(0)===v}function je(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return r(e,h,g)||be(e)}function ye(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&[...e].every(je)}function Ee(){const e=[...arguments.length>0&&void 0!==arguments[0]?arguments[0]:""];let t=!1;return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{passKanji:!0}).passKanji||(t=e.some(ye)),(e.some(pe)||e.some(me))&&e.some(de)&&!t}const Ae=(e,t)=>Q(e)&&t<1,Oe=(e,t)=>Q(e)&&t>0,Ne=e=>["ヶ","ヵ"].includes(e),we={a:"あ",i:"い",u:"う",e:"え",o:"う"};function ke(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1?arguments[1]:void 0,{isDestinationRomaji:n,convertLongVowelMark:r}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o="";return e.split("").reduce(((i,a,u)=>{if(Y(a)||Ae(a,u)||Ne(a))return i.concat(a);if(r&&o&&Oe(a,u)){const r=t(o).slice(-1);return he(e[u-1])&&"o"===r&&n?i.concat("お"):i.concat(we[r])}if(!Q(a)&&he(a)){const e=a.charCodeAt(0)+(c-f),t=String.fromCharCode(e);return o=t,i.concat(t)}return o="",i.concat(a)}),[]).join("")}let Me=null;const Se={"あ":"a","い":"i","う":"u","え":"e","お":"o","か":"ka","き":"ki","く":"ku","け":"ke","こ":"ko","さ":"sa","し":"shi","す":"su","せ":"se","そ":"so","た":"ta","ち":"chi","つ":"tsu","て":"te","と":"to","な":"na","に":"ni","ぬ":"nu","ね":"ne","の":"no","は":"ha","ひ":"hi","ふ":"fu","へ":"he","ほ":"ho","ま":"ma","み":"mi","む":"mu","め":"me","も":"mo","ら":"ra","り":"ri","る":"ru","れ":"re","ろ":"ro","や":"ya","ゆ":"yu","よ":"yo","わ":"wa","ゐ":"wi","ゑ":"we","を":"wo","ん":"n","が":"ga","ぎ":"gi","ぐ":"gu","げ":"ge","ご":"go","ざ":"za","じ":"ji","ず":"zu","ぜ":"ze","ぞ":"zo","だ":"da","ぢ":"ji","づ":"zu","で":"de","ど":"do","ば":"ba","び":"bi","ぶ":"bu","べ":"be","ぼ":"bo","ぱ":"pa","ぴ":"pi","ぷ":"pu","ぺ":"pe","ぽ":"po","ゔぁ":"va","ゔぃ":"vi","ゔ":"vu","ゔぇ":"ve","ゔぉ":"vo"},Ce={"。":".","、":",",":":":","・":"/","!":"!","?":"?","〜":"~","ー":"-","「":"‘","」":"’","『":"“","』":"”","[":"[","]":"]","(":"(",")":")","{":"{","}":"}"," ":" "},Ke=["あ","い","う","え","お","や","ゆ","よ"],Re={"ゃ":"ya","ゅ":"yu","ょ":"yo"},Je={"ぃ":"yi","ぇ":"ye"},ze={"ぁ":"a","ぃ":"i","ぅ":"u","ぇ":"e","ぉ":"o"},Ie=["き","に","ひ","み","り","ぎ","び","ぴ","ゔ","く","ふ"],Le={"し":"sh","ち":"ch","じ":"j","ぢ":"j"},Te={"っ":"","ゃ":"ya","ゅ":"yu","ょ":"yo","ぁ":"a","ぃ":"i","ぅ":"u","ぇ":"e","ぉ":"o"},He={b:"b",c:"t",d:"d",f:"f",g:"g",h:"h",j:"j",k:"k",m:"m",p:"p",q:"q",r:"r",s:"s",t:"t",v:"v",w:"w",x:"x",z:"z"};function Ue(){return null==Me&&(Me=function(){const e=L(Se),t=t=>T(e,t),n=(e,n)=>{t(e)[""]=n};return Object.entries(Ce).forEach((e=>{let[n,r]=e;t(n)[""]=r})),[...Object.entries(Re),...Object.entries(ze)].forEach((e=>{let[t,r]=e;n(t,r)})),Ie.forEach((e=>{const r=t(e)[""][0];Object.entries(Re).forEach((t=>{let[o,i]=t;n(e+o,r+i)})),Object.entries(Je).forEach((t=>{let[o,i]=t;n(e+o,r+i)}))})),Object.entries(Le).forEach((e=>{let[t,r]=e;Object.entries(Re).forEach((e=>{let[o,i]=e;n(t+o,r+i[1])})),n(`${t}ぃ`,`${r}yi`),n(`${t}ぇ`,`${r}e`)})),e["っ"]=$e(e),Object.entries(Te).forEach((e=>{let[t,r]=e;n(t,r)})),Ke.forEach((e=>{n(`ん${e}`,`n'${t(e)[""]}`)})),Object.freeze(JSON.parse(JSON.stringify(e)))}()),Me}function $e(e){return Object.entries(e).reduce(((e,t)=>{let[n,r]=t;if(n)e[n]=$e(r);else{const t=r.charAt(0);e[n]=Object.keys(He).includes(t)?He[t]+r:r}return e}),{})}const xe=C(((e,t)=>{let n=function(e){return e===i.HEPBURN?Ue():{}}(e);return t&&(n=U(n,t)),n}),J);function Pe(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>2?arguments[2]:void 0;const n=z(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{});return t||(t=xe(n.romanization,n.customRomajiMapping)),function(e,t,n){n||(n=xe(t.romanization,t.customRomajiMapping));const r=Object.assign({},{isDestinationRomaji:!0},t);return I(ke(e,Pe,r),n,!t.IMEMode)}(e,n,t).map((t=>{const[r,o,i]=t;return n.upcaseKatakana&&me(e.slice(r,o))?i.toUpperCase():i})).join("")}function _e(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&N.some((t=>{let[n,o]=t;return r(e,n,o)}))}function De(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return!n(e)&&!be(e)&&E.some((t=>{let[n,o]=t;return r(e,n,o)}))}const qe=e=>" "===e,Be=e=>" "===e,Ve=e=>/[0-9]/.test(e),Ge=e=>/[0-9]/.test(e),We={EN:"en",JA:"ja",EN_NUM:"englishNumeral",JA_NUM:"japaneseNumeral",EN_PUNC:"englishPunctuation",JA_PUNC:"japanesePunctuation",KANJI:"kanji",HIRAGANA:"hiragana",KATAKANA:"katakana",SPACE:"space",OTHER:"other"};function Xe(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const{EN:n,JA:r,EN_NUM:o,JA_NUM:i,EN_PUNC:a,JA_PUNC:u,KANJI:s,HIRAGANA:c,KATAKANA:l,SPACE:f,OTHER:d}=We;if(t)switch(!0){case Ve(e):case Ge(e):return d;case qe(e):return n;case _e(e):return d;case Be(e):return r;case De(e):return d;case w(e):return r;case fe(e):return n;default:return d}else switch(!0){case Be(e):case qe(e):return f;case Ve(e):return i;case Ge(e):return o;case _e(e):return a;case De(e):return u;case je(e):return s;case ee(e):return c;case he(e):return l;case w(e):return r;case fe(e):return n;default:return d}}function Ze(e){let{compact:t=!1,detailed:r=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(null==e||n(e))return[];const o=[...e];let i=o.shift(),a=Xe(i,t);i=r?{type:a,value:i}:i;return o.reduce(((e,n)=>{const o=Xe(n,t),i=o===a;a=o;let u=n;return i&&(u=(r?e.pop().value:e.pop())+u),r?e.concat({type:o,value:u}):e.concat(u)}),[i])}e.ROMANIZATIONS=i,e.TO_KANA_METHODS=o,e.VERSION="5.2.0",e.bind=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(!ce.includes(e.nodeName))throw new Error(`Element provided to Wanakana bind() was not a valid input or textarea element.\n Received: (${JSON.stringify(e)})`);if(e.hasAttribute("data-wanakana-id"))return;const r=ie(t),o=(le+=1,`${Date.now()}${le}`),i={};var a;[{name:"data-wanakana-id",value:o},{name:"lang",value:"ja"},{name:"autoCapitalize",value:"none"},{name:"autoCorrect",value:"off"},{name:"autoComplete",value:"off"},{name:"spellCheck",value:"false"}].forEach((t=>{i[t.name]=e.getAttribute(t.name),e.setAttribute(t.name,t.value)})),e.dataset.previousAttributes=JSON.stringify(i),e.addEventListener("input",r),e.addEventListener("compositionupdate",ae),e.addEventListener("compositionend",ae),function(e,t,n){oe=oe.concat({id:e,inputHandler:t,compositionHandler:n})}(o,r,ae),!0===n&&(a=e,Object.entries(se).forEach((e=>{let[t,n]=e;return a.addEventListener(t,n)})))},e.isHiragana=pe,e.isJapanese=k,e.isKana=ve,e.isKanji=ye,e.isKatakana=me,e.isMixed=Ee,e.isRomaji=de,e.stripOkurigana=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",{leading:t=!1,matchKanji:n=""}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!k(e)||((e,t)=>t&&!ve(e[0]))(e,t)||((e,t)=>!t&&!ve(e[e.length-1]))(e,t)||((e,t)=>t&&![...t].some(ye)||!t&&ve(e))(e,n))return e;const r=n||e,o=new RegExp(t?`^${Ze(r).shift()}`:`${Ze(r).pop()}$`);return e.replace(o,"")},e.toHiragana=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";const t=z(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{});if(t.passRomaji)return ke(e,Pe,t);if(Ee(e,{passKanji:!0})){return re(ke(e,Pe,t).toLowerCase(),t)}return de(e)||_e(e)?re(e.toLowerCase(),t):ke(e,Pe,t)},e.toKana=re,e.toKatakana=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";const t=z(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{});if(t.passRomaji)return te(e);if(Ee(e)||de(e)||_e(e)){return te(re(e.toLowerCase(),t))}return te(e)},e.toRomaji=Pe,e.tokenize=Ze,e.unbind=function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const n=(r=e)&&oe.find((e=>{let{id:t}=e;return t===r.getAttribute("data-wanakana-id")}));var r;if(null==n)throw new Error(`Element provided to Wanakana unbind() had no listener registered.\n Received: ${JSON.stringify(e)}`);const{inputHandler:o,compositionHandler:i}=n,a=JSON.parse(e.dataset.previousAttributes);var u;Object.keys(a).forEach((t=>{a[t]?e.setAttribute(t,a[t]):e.removeAttribute(t)})),e.removeAttribute("data-previous-attributes"),e.removeAttribute("data-ignore-composition"),e.removeEventListener("input",o),e.removeEventListener("compositionstart",i),e.removeEventListener("compositionupdate",i),e.removeEventListener("compositionend",i),function(e){let{id:t}=e;oe=oe.filter((e=>{let{id:n}=e;return n!==t}))}(n),!0===t&&(u=e,Object.entries(se).forEach((e=>{let[t,n]=e;return u.removeEventListener(t,n)})))}}));
//# sourceMappingURL=wanakana.min.js.map

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc