text-input-guard 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/text-input-guard.cjs +4504 -52
- package/dist/cjs/text-input-guard.min.cjs +1 -1
- package/dist/esm/text-input-guard.js +4498 -53
- package/dist/esm/text-input-guard.min.js +1 -1
- package/dist/types/text-input-guard.d.ts +544 -16
- package/dist/umd/text-input-guard.js +4504 -52
- package/dist/umd/text-input-guard.min.js +1 -1
- package/package.json +1 -1
|
@@ -332,10 +332,25 @@ class SwapState {
|
|
|
332
332
|
* @property {boolean} warn - warnログを出すかどうか
|
|
333
333
|
* @property {string} invalidClass - エラー時に付与するclass名
|
|
334
334
|
* @property {boolean} composing - IME変換中かどうか
|
|
335
|
+
* @property {string|null} inputType - 直前の入力操作種別(insertText / insertFromPaste / insertCompositionText 等)
|
|
336
|
+
* @property {string} beforeText - 挿入前の全文字列(置換範囲は除去済み)
|
|
337
|
+
* @property {number} replaceStart - 挿入位置/置換開始位置(selectionStart)
|
|
338
|
+
* @property {number} replaceEnd - 置換終了位置(selectionEnd)
|
|
339
|
+
* @property {string} insertedText - 挿入された文字列
|
|
340
|
+
* @property {string} afterText - 挿入後の全文字列(後で代入する)
|
|
335
341
|
* @property {(e: TigError) => void} pushError - エラーを登録する関数
|
|
336
342
|
* @property {(req: RevertRequest) => void} requestRevert - 入力を直前の受理値へ巻き戻す要求
|
|
337
343
|
*/
|
|
338
344
|
|
|
345
|
+
/**
|
|
346
|
+
* beforeinput で採取する「入力直前スナップショット」
|
|
347
|
+
* - 入力反映前の状態を保持し、差分判定や挿入位置特定に利用する
|
|
348
|
+
* @typedef {Object} BeforeInputSnapshot
|
|
349
|
+
* @property {SelectionState} selection - 入力反映前の選択範囲(挿入/置換位置の判定に使用)
|
|
350
|
+
* @property {string|null} inputType - 入力種別(insertText / insertFromPaste / deleteContentBackward 等)
|
|
351
|
+
* @property {string|null} insertedText - 挿入された文字列(通常入力時に取得できる場合あり)
|
|
352
|
+
*/
|
|
353
|
+
|
|
339
354
|
/**
|
|
340
355
|
* 1つの入力制御ルール定義
|
|
341
356
|
* - 各フェーズの処理を必要に応じて実装する
|
|
@@ -592,6 +607,11 @@ class InputGuard {
|
|
|
592
607
|
*/
|
|
593
608
|
this.onInput = this.onInput.bind(this);
|
|
594
609
|
|
|
610
|
+
/**
|
|
611
|
+
* beforeinputイベントハンドラ(this固定)
|
|
612
|
+
*/
|
|
613
|
+
this.onBeforeInput = this.onBeforeInput.bind(this);
|
|
614
|
+
|
|
595
615
|
/**
|
|
596
616
|
* blurイベントハンドラ(this固定)
|
|
597
617
|
*/
|
|
@@ -621,17 +641,24 @@ class InputGuard {
|
|
|
621
641
|
this.pendingCompositionCommit = false;
|
|
622
642
|
|
|
623
643
|
/**
|
|
624
|
-
*
|
|
644
|
+
* 直前に受理した表示値、正しい情報のスナップショットのような情報(block時の戻し先)
|
|
625
645
|
* @type {string}
|
|
626
646
|
*/
|
|
627
647
|
this.lastAcceptedValue = "";
|
|
628
648
|
|
|
629
649
|
/**
|
|
630
|
-
* 直前に受理したselection
|
|
650
|
+
* 直前に受理したselection、正しい情報のスナップショットのような情報(block時の戻し先)
|
|
631
651
|
* @type {SelectionState}
|
|
632
652
|
*/
|
|
633
653
|
this.lastAcceptedSelection = { start: null, end: null, direction: null };
|
|
634
654
|
|
|
655
|
+
/**
|
|
656
|
+
* 入力直前スナップショット(beforeinputで更新)
|
|
657
|
+
* length等の「挿入位置優先」ロジックで使用する
|
|
658
|
+
* @type {BeforeInputSnapshot|null}
|
|
659
|
+
*/
|
|
660
|
+
this.beforeInputSnapshot = null;
|
|
661
|
+
|
|
635
662
|
/**
|
|
636
663
|
* ルールからのrevert要求
|
|
637
664
|
* @type {RevertRequest|null}
|
|
@@ -693,7 +720,7 @@ class InputGuard {
|
|
|
693
720
|
applySeparateValue() {
|
|
694
721
|
const userMode = this.options.separateValue?.mode ?? "auto";
|
|
695
722
|
|
|
696
|
-
// autoの場合:format系ルールがあるときだけswap
|
|
723
|
+
// autoの場合:format系ルールがあるときだけswap (つまり input を作成する)
|
|
697
724
|
const mode =
|
|
698
725
|
userMode === "auto"
|
|
699
726
|
? (this.formatRules.length > 0 ? "swap" : "off")
|
|
@@ -832,6 +859,7 @@ class InputGuard {
|
|
|
832
859
|
this.displayElement.addEventListener("compositionstart", this.onCompositionStart);
|
|
833
860
|
this.displayElement.addEventListener("compositionend", this.onCompositionEnd);
|
|
834
861
|
this.displayElement.addEventListener("input", this.onInput);
|
|
862
|
+
this.displayElement.addEventListener("beforeinput", this.onBeforeInput);
|
|
835
863
|
this.displayElement.addEventListener("blur", this.onBlur);
|
|
836
864
|
|
|
837
865
|
// フォーカスで編集用に戻す
|
|
@@ -852,6 +880,7 @@ class InputGuard {
|
|
|
852
880
|
this.displayElement.removeEventListener("compositionstart", this.onCompositionStart);
|
|
853
881
|
this.displayElement.removeEventListener("compositionend", this.onCompositionEnd);
|
|
854
882
|
this.displayElement.removeEventListener("input", this.onInput);
|
|
883
|
+
this.displayElement.removeEventListener("beforeinput", this.onBeforeInput);
|
|
855
884
|
this.displayElement.removeEventListener("blur", this.onBlur);
|
|
856
885
|
this.displayElement.removeEventListener("focus", this.onFocus);
|
|
857
886
|
this.displayElement.removeEventListener("keyup", this.onSelectionChange);
|
|
@@ -893,6 +922,70 @@ class InputGuard {
|
|
|
893
922
|
* @returns {GuardContext}
|
|
894
923
|
*/
|
|
895
924
|
createCtx() {
|
|
925
|
+
const snap = this.beforeInputSnapshot;
|
|
926
|
+
const inputType = snap?.inputType ?? "";
|
|
927
|
+
const insertedText = snap?.insertedText ?? "";
|
|
928
|
+
|
|
929
|
+
// 受理済み(正規化済み)の全文を「今回の編集の基準」として使う
|
|
930
|
+
// display.value はブラウザ側の編集結果が混ざるので、差分再構成の基準にはしない
|
|
931
|
+
const beforeText = this.lastAcceptedValue ?? "";
|
|
932
|
+
|
|
933
|
+
// selection は2系統ある:
|
|
934
|
+
// - snapSel: beforeinput 時点で取得した selection(今回の編集の基準点になり得る)
|
|
935
|
+
// - lastSel: ユーザー操作(keyup/mouseup/select 等)で追跡している selection(常にUIの見た目に近い)
|
|
936
|
+
//
|
|
937
|
+
// 基本は snapSel を優先するが、IME が絡むと snapSel が「変換中の範囲(composition range)」を指して
|
|
938
|
+
// “本当のキャレット位置” と一致しないことがあるため、その場合は lastSel を採用する。
|
|
939
|
+
const snapSel = snap?.selection ?? null;
|
|
940
|
+
const lastSel = this.lastAcceptedSelection;
|
|
941
|
+
|
|
942
|
+
// 通常は beforeinput の selection(snapSel)を使うのが一番正確
|
|
943
|
+
let baseSel = snapSel ?? lastSel;
|
|
944
|
+
|
|
945
|
+
// IME由来の入力(変換中の確定/更新など)は、beforeinput の selection が
|
|
946
|
+
// 「IMEが管理する範囲」になってしまうことがある。
|
|
947
|
+
// この場合 snapSel を使うと “勝手に上書き” が起きやすいので lastSel に寄せる。
|
|
948
|
+
const isCompositionInput =
|
|
949
|
+
this.composing ||
|
|
950
|
+
inputType === "insertCompositionText" ||
|
|
951
|
+
inputType === "deleteCompositionText" ||
|
|
952
|
+
inputType === "insertFromComposition";
|
|
953
|
+
|
|
954
|
+
// もう一つの検知:snapSel が「範囲選択」なのに lastSel が「キャレットのみ」なら、
|
|
955
|
+
// その範囲はユーザーが選択したのではなく、IMEが作っている範囲である可能性が高い。
|
|
956
|
+
// (例:12|34 に 5 を入れたいのに、IME範囲を置換して 1254 になる、など)
|
|
957
|
+
const looksLikeImeRange =
|
|
958
|
+
snapSel &&
|
|
959
|
+
(snapSel.start !== snapSel.end) &&
|
|
960
|
+
(lastSel.start === lastSel.end) &&
|
|
961
|
+
(inputType === "insertText" || inputType === "insertCompositionText");
|
|
962
|
+
|
|
963
|
+
if (isCompositionInput || looksLikeImeRange) {
|
|
964
|
+
baseSel = lastSel;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
let replaceStart = baseSel.start ?? 0;
|
|
968
|
+
let replaceEnd = baseSel.end ?? 0;
|
|
969
|
+
|
|
970
|
+
// Backspace / Delete は「挿入文字がない(dataがnull)」ことが多い。
|
|
971
|
+
// そのままだと差分再構成で “何も変わらない” 扱いになって削除が効かなくなるため、
|
|
972
|
+
// 選択範囲が無い場合は「削除される1文字ぶん」の置換範囲をここで作る。
|
|
973
|
+
//
|
|
974
|
+
// ※ 選択範囲がある削除は replaceStart!=replaceEnd なので補正不要(その範囲を消すだけでよい)
|
|
975
|
+
if (replaceStart === replaceEnd) {
|
|
976
|
+
if (inputType === "deleteContentBackward") {
|
|
977
|
+
// Backspace: キャレットの左側1文字を削除
|
|
978
|
+
replaceStart = Math.max(0, replaceStart - 1);
|
|
979
|
+
replaceEnd = snapSel.start ?? replaceEnd;
|
|
980
|
+
} else if (inputType === "deleteContentForward") {
|
|
981
|
+
// Delete: キャレットの右側1文字を削除
|
|
982
|
+
replaceStart = snapSel.start ?? replaceStart;
|
|
983
|
+
replaceEnd = Math.min(beforeText.length, replaceEnd + 1);
|
|
984
|
+
}
|
|
985
|
+
// 追加で拾うならここ:
|
|
986
|
+
// deleteWordBackward / deleteWordForward / deleteByCut / deleteSoftLineBackward ... etc
|
|
987
|
+
}
|
|
988
|
+
|
|
896
989
|
return {
|
|
897
990
|
hostElement: this.hostElement,
|
|
898
991
|
displayElement: this.displayElement,
|
|
@@ -901,6 +994,12 @@ class InputGuard {
|
|
|
901
994
|
warn: this.warn,
|
|
902
995
|
invalidClass: this.invalidClass,
|
|
903
996
|
composing: this.composing,
|
|
997
|
+
inputType,
|
|
998
|
+
beforeText,
|
|
999
|
+
replaceStart,
|
|
1000
|
+
replaceEnd,
|
|
1001
|
+
insertedText,
|
|
1002
|
+
afterText: null, // 後で代入する
|
|
904
1003
|
pushError: (e) => this.errors.push(e),
|
|
905
1004
|
requestRevert: (req) => {
|
|
906
1005
|
// 1回でもrevert要求が出たら採用(最初の理由を保持)
|
|
@@ -1065,6 +1164,23 @@ class InputGuard {
|
|
|
1065
1164
|
this.evaluateInput();
|
|
1066
1165
|
}
|
|
1067
1166
|
|
|
1167
|
+
/**
|
|
1168
|
+
* beforeinput:入力が反映される直前に呼ばれる
|
|
1169
|
+
* - ここでの value/selection が「今回の編集の基準点」になる
|
|
1170
|
+
* @param {InputEvent} e
|
|
1171
|
+
* @returns {void}
|
|
1172
|
+
*/
|
|
1173
|
+
onBeforeInput(e) {
|
|
1174
|
+
const el = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
|
|
1175
|
+
// 現時点(反映前)の選択範囲
|
|
1176
|
+
const selection = this.readSelection(el);
|
|
1177
|
+
/** @type {string|null} */
|
|
1178
|
+
const inputType = typeof e.inputType === "string" ? e.inputType : null;
|
|
1179
|
+
/** @type {string|null} */
|
|
1180
|
+
const insertedText = typeof e.data === "string" ? e.data : null;
|
|
1181
|
+
this.beforeInputSnapshot = { selection, inputType, insertedText };
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1068
1184
|
/**
|
|
1069
1185
|
* blurイベント:確定時評価(normalize → validate → fix → format、同期、class更新)
|
|
1070
1186
|
* @returns {void}
|
|
@@ -1075,7 +1191,7 @@ class InputGuard {
|
|
|
1075
1191
|
}
|
|
1076
1192
|
|
|
1077
1193
|
/**
|
|
1078
|
-
* focus
|
|
1194
|
+
* focusイベント:表示整形を剥がして編集しやすい状態にする
|
|
1079
1195
|
* - validate は走らせない(触っただけで赤くしたくないため)
|
|
1080
1196
|
* @returns {void}
|
|
1081
1197
|
*/
|
|
@@ -1086,9 +1202,10 @@ class InputGuard {
|
|
|
1086
1202
|
const current = display.value;
|
|
1087
1203
|
|
|
1088
1204
|
const ctx = this.createCtx();
|
|
1205
|
+
ctx.afterText = current;
|
|
1089
1206
|
|
|
1090
1207
|
let v = current;
|
|
1091
|
-
v = this.runNormalizeChar(v, ctx);
|
|
1208
|
+
v = this.runNormalizeChar(v, ctx);
|
|
1092
1209
|
v = this.runNormalizeStructure(v, ctx);
|
|
1093
1210
|
|
|
1094
1211
|
if (v !== current) {
|
|
@@ -1155,6 +1272,83 @@ class InputGuard {
|
|
|
1155
1272
|
}
|
|
1156
1273
|
}
|
|
1157
1274
|
|
|
1275
|
+
/**
|
|
1276
|
+
* evaluateInput専用createCtx(ルール実行に渡すコンテキストを作り、正規箇所も実行する)
|
|
1277
|
+
*
|
|
1278
|
+
* - CTX を作成する中で、文字の正規化と構造の正規化を行い、CTXのその情報に合わせる
|
|
1279
|
+
* - runNormalizeChar に対して入力した文字のみを入れることで、処理の高速化とキャレットズレが起きないように制御する
|
|
1280
|
+
* - runNormalizeStructure は全体の文字を入れるため、ここでの処理はキャレットズレが起きる可能性がある
|
|
1281
|
+
* @returns {GuardContext}
|
|
1282
|
+
*/
|
|
1283
|
+
createCtxAndNormalize() {
|
|
1284
|
+
const display = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
|
|
1285
|
+
const current = display.value;
|
|
1286
|
+
const ctx = this.createCtx();
|
|
1287
|
+
ctx.afterText = current;
|
|
1288
|
+
|
|
1289
|
+
// 元のテキスト
|
|
1290
|
+
const beforeText = ctx.beforeText;
|
|
1291
|
+
|
|
1292
|
+
// 追加入力したテキスト
|
|
1293
|
+
let insertedText = ctx.insertedText;
|
|
1294
|
+
|
|
1295
|
+
// 左端の挿入箇所
|
|
1296
|
+
const replaceStart = ctx.replaceStart;
|
|
1297
|
+
|
|
1298
|
+
// 現状のテキスト
|
|
1299
|
+
const tempText = current;
|
|
1300
|
+
|
|
1301
|
+
// 作成する全体のテキスト
|
|
1302
|
+
let newText = beforeText;
|
|
1303
|
+
|
|
1304
|
+
if (ctx.replaceStart !== ctx.replaceEnd) {
|
|
1305
|
+
// 選択範囲の前までと、選択範囲の後ろを結合して、間(選択部分)を削除する
|
|
1306
|
+
newText = beforeText.slice(0, ctx.replaceStart) + beforeText.slice(ctx.replaceEnd);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// CTX の情報を最新の情報へ更新する
|
|
1310
|
+
ctx.beforeText = newText;
|
|
1311
|
+
|
|
1312
|
+
// 挿入するテキストのみ文字チェックを行う
|
|
1313
|
+
const normalizeCharText = this.runNormalizeChar(insertedText, ctx);
|
|
1314
|
+
insertedText = normalizeCharText;
|
|
1315
|
+
|
|
1316
|
+
// 作成したテキストを挿入する
|
|
1317
|
+
newText = newText.slice(0, replaceStart) + insertedText + newText.slice(replaceStart);
|
|
1318
|
+
|
|
1319
|
+
// 挿入したテキストの右側にカーソル位置をずらす
|
|
1320
|
+
// insertedText は UTF-16 code unit 長なので、
|
|
1321
|
+
// Selection は JS の index(UTF-16)前提で計算
|
|
1322
|
+
/**
|
|
1323
|
+
* @type {SelectionState}
|
|
1324
|
+
*/
|
|
1325
|
+
let newSelection = { start: replaceStart + insertedText.length, end: replaceStart + insertedText.length, direction: "forward" };
|
|
1326
|
+
|
|
1327
|
+
// 挿入後文章全体に構造チェックを行う
|
|
1328
|
+
const normalizeStructureText = this.runNormalizeStructure(newText, ctx);
|
|
1329
|
+
|
|
1330
|
+
// 構成した文章がずれていた場合、カーソル位置の見直しを行う
|
|
1331
|
+
if (newText !== normalizeStructureText) {
|
|
1332
|
+
newText = normalizeStructureText;
|
|
1333
|
+
// 入力した実際のテキスト(tempText)から、現在位置から左側のみ切り出して、左側のみ再チェックする
|
|
1334
|
+
// 文章の長さに依存した変更があった場合は厳しいが、それ以外は以下の方法で切り抜けられる可能性が高い
|
|
1335
|
+
let leftText = tempText.slice(0, replaceStart);
|
|
1336
|
+
leftText = this.runNormalizeChar(leftText, ctx);
|
|
1337
|
+
leftText = this.runNormalizeStructure(leftText, ctx);
|
|
1338
|
+
const newPos = Math.min(leftText.length, newText.length);
|
|
1339
|
+
newSelection = { start: newPos, end: newPos, direction: "forward" };
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// 画面を更新
|
|
1343
|
+
this.syncDisplay(newText);
|
|
1344
|
+
this.writeSelection(display, newSelection);
|
|
1345
|
+
|
|
1346
|
+
// CTX の情報を最新の情報へ更新する
|
|
1347
|
+
ctx.afterText = newText;
|
|
1348
|
+
|
|
1349
|
+
return ctx;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1158
1352
|
/**
|
|
1159
1353
|
* 入力中の評価(IME中は何もしない)
|
|
1160
1354
|
* - 固定順:normalize.char → normalize.structure → validate
|
|
@@ -1170,26 +1364,15 @@ class InputGuard {
|
|
|
1170
1364
|
this.revertRequest = null;
|
|
1171
1365
|
|
|
1172
1366
|
const display = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
|
|
1173
|
-
const current = display.value;
|
|
1174
1367
|
|
|
1175
|
-
const ctx = this.
|
|
1176
|
-
|
|
1177
|
-
// raw候補(入力中は表示値=rawとして扱う)
|
|
1178
|
-
let raw = current;
|
|
1179
|
-
|
|
1180
|
-
raw = this.runNormalizeChar(raw, ctx);
|
|
1181
|
-
raw = this.runNormalizeStructure(raw, ctx);
|
|
1182
|
-
|
|
1183
|
-
// normalizeで変わったら反映(selection補正)
|
|
1184
|
-
if (raw !== current) {
|
|
1185
|
-
this.setDisplayValuePreserveCaret(display, raw, ctx);
|
|
1186
|
-
}
|
|
1368
|
+
const ctx = this.createCtxAndNormalize();
|
|
1369
|
+
const raw = ctx.afterText;
|
|
1187
1370
|
|
|
1188
1371
|
// validate(入力中:エラー出すだけ)
|
|
1189
1372
|
this.runValidate(raw, ctx);
|
|
1190
1373
|
|
|
1191
|
-
// revert要求が出たら巻き戻して終了
|
|
1192
1374
|
if (this.revertRequest) {
|
|
1375
|
+
// revert要求が出たら巻き戻して終了
|
|
1193
1376
|
this.revertDisplay(this.revertRequest);
|
|
1194
1377
|
return;
|
|
1195
1378
|
}
|
|
@@ -1223,6 +1406,7 @@ class InputGuard {
|
|
|
1223
1406
|
|
|
1224
1407
|
// 1) raw候補(displayから取得)
|
|
1225
1408
|
let raw = display.value;
|
|
1409
|
+
ctx.afterText = raw;
|
|
1226
1410
|
|
|
1227
1411
|
// 2) 正規化(rawとして扱う形に揃える)
|
|
1228
1412
|
raw = this.runNormalizeChar(raw, ctx);
|
|
@@ -1356,59 +1540,122 @@ class InputGuard {
|
|
|
1356
1540
|
}
|
|
1357
1541
|
|
|
1358
1542
|
/**
|
|
1359
|
-
*
|
|
1543
|
+
* dataset/option の boolean 値を解釈する
|
|
1544
|
+
* - 未指定(null/undefined)の場合は defaultValue を返す
|
|
1545
|
+
* - 空文字 "" は常に true(HTML属性文化)
|
|
1546
|
+
* - 指定があるが解釈できない場合は undefined
|
|
1360
1547
|
*
|
|
1361
|
-
*
|
|
1362
|
-
*
|
|
1548
|
+
* true : true / 1 / "true" / "1" / "yes" / "on" / ""
|
|
1549
|
+
* false : false / 0 / "false" / "0" / "no" / "off"
|
|
1363
1550
|
*
|
|
1364
|
-
*
|
|
1365
|
-
*
|
|
1366
|
-
*/
|
|
1367
|
-
|
|
1368
|
-
/**
|
|
1369
|
-
* datasetのboolean値を解釈する
|
|
1370
|
-
* - 未指定なら undefined
|
|
1371
|
-
* - "" / "true" / "1" / "yes" / "on" は true
|
|
1372
|
-
* - "false" / "0" / "no" / "off" は false
|
|
1373
|
-
* @param {string|undefined} v
|
|
1551
|
+
* @param {string|number|boolean|undefined|null} v
|
|
1552
|
+
* @param {boolean} [defaultValue]
|
|
1374
1553
|
* @returns {boolean|undefined}
|
|
1375
1554
|
*/
|
|
1376
|
-
function parseDatasetBool(v) {
|
|
1377
|
-
if (v
|
|
1555
|
+
function parseDatasetBool(v, defaultValue) {
|
|
1556
|
+
if (v === null || v === undefined) { return defaultValue; }
|
|
1557
|
+
|
|
1558
|
+
if (typeof v === "boolean") { return v; }
|
|
1559
|
+
|
|
1560
|
+
if (typeof v === "number") {
|
|
1561
|
+
if (v === 1) { return true; }
|
|
1562
|
+
if (v === 0) { return false; }
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1378
1566
|
const s = String(v).trim().toLowerCase();
|
|
1379
|
-
|
|
1567
|
+
|
|
1568
|
+
// dataset の属性存在を true とみなす(例: data-xxx="")
|
|
1569
|
+
if (s === "") { return true; }
|
|
1570
|
+
|
|
1571
|
+
if (s === "true" || s === "1" || s === "yes" || s === "on") { return true; }
|
|
1380
1572
|
if (s === "false" || s === "0" || s === "no" || s === "off") { return false; }
|
|
1573
|
+
|
|
1381
1574
|
return;
|
|
1382
1575
|
}
|
|
1383
1576
|
|
|
1384
1577
|
/**
|
|
1385
|
-
* datasetのnumber
|
|
1386
|
-
* -
|
|
1578
|
+
* dataset/option の number 値を解釈する
|
|
1579
|
+
* - 未指定(null/undefined/空文字)の場合は defaultValue を返す
|
|
1387
1580
|
* - 数値でなければ undefined
|
|
1388
|
-
* @param {string|undefined} v
|
|
1581
|
+
* @param {string|number|undefined|null} v
|
|
1582
|
+
* @param {number} [defaultValue]
|
|
1389
1583
|
* @returns {number|undefined}
|
|
1390
1584
|
*/
|
|
1391
|
-
function parseDatasetNumber(v) {
|
|
1392
|
-
if (v
|
|
1585
|
+
function parseDatasetNumber(v, defaultValue) {
|
|
1586
|
+
if (v === null || v === undefined) { return defaultValue; }
|
|
1587
|
+
|
|
1588
|
+
if (typeof v === "number") {
|
|
1589
|
+
return Number.isFinite(v) ? v : undefined;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1393
1592
|
const s = String(v).trim();
|
|
1394
|
-
if (s === "") { return; }
|
|
1593
|
+
if (s === "") { return defaultValue; }
|
|
1594
|
+
|
|
1395
1595
|
const n = Number(s);
|
|
1396
1596
|
return Number.isFinite(n) ? n : undefined;
|
|
1397
1597
|
}
|
|
1398
1598
|
|
|
1399
1599
|
/**
|
|
1400
|
-
* enum
|
|
1600
|
+
* enumを解釈する
|
|
1601
|
+
* - 未指定(null/undefined/空文字)の場合は defaultValue を返す
|
|
1602
|
+
* - 値が指定されているが allowed に含まれない場合は undefined を返す
|
|
1603
|
+
*
|
|
1401
1604
|
* @template {string} T
|
|
1402
|
-
* @param {string|undefined} v
|
|
1605
|
+
* @param {string|undefined|null} v
|
|
1403
1606
|
* @param {readonly T[]} allowed
|
|
1607
|
+
* @param {T} [defaultValue]
|
|
1404
1608
|
* @returns {T|undefined}
|
|
1405
1609
|
*/
|
|
1406
|
-
function parseDatasetEnum(v, allowed) {
|
|
1407
|
-
if (v
|
|
1610
|
+
function parseDatasetEnum(v, allowed, defaultValue) {
|
|
1611
|
+
if (v === null || v === undefined) { return defaultValue; }
|
|
1612
|
+
|
|
1613
|
+
const s = String(v).trim();
|
|
1614
|
+
if (s === "") { return defaultValue; }
|
|
1615
|
+
|
|
1616
|
+
return /** @type {T|undefined} */ (
|
|
1617
|
+
allowed.includes(/** @type {any} */ (s)) ? s : undefined
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* enum のカンマ区切り複数指定を解釈する
|
|
1623
|
+
* - 未指定(null/undefined/空文字)の場合は defaultValue を返す
|
|
1624
|
+
* - 空要素は無視
|
|
1625
|
+
* - allowed に含まれないものは除外
|
|
1626
|
+
*
|
|
1627
|
+
* @template {string} T
|
|
1628
|
+
* @param {string|T[]|undefined|null} v
|
|
1629
|
+
* @param {readonly T[]} allowed
|
|
1630
|
+
* @param {T[]} [defaultValue]
|
|
1631
|
+
* @returns {T[]|undefined}
|
|
1632
|
+
*/
|
|
1633
|
+
function parseDatasetEnumList(v, allowed, defaultValue) {
|
|
1634
|
+
if (v === null || v === undefined) { return defaultValue; }
|
|
1635
|
+
|
|
1636
|
+
// JSオプションで配列直渡しも許可
|
|
1637
|
+
if (Array.isArray(v)) {
|
|
1638
|
+
const result = v.filter(
|
|
1639
|
+
/** @returns {x is T} */
|
|
1640
|
+
(x) => allowed.includes(/** @type {any} */ (x))
|
|
1641
|
+
);
|
|
1642
|
+
return result;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1408
1645
|
const s = String(v).trim();
|
|
1409
|
-
if (s === "") { return; }
|
|
1410
|
-
|
|
1411
|
-
|
|
1646
|
+
if (s === "") { return defaultValue; }
|
|
1647
|
+
|
|
1648
|
+
const list = s
|
|
1649
|
+
.split(",")
|
|
1650
|
+
.map((x) => x.trim())
|
|
1651
|
+
.filter(Boolean);
|
|
1652
|
+
|
|
1653
|
+
const result = list.filter(
|
|
1654
|
+
/** @returns {x is T} */
|
|
1655
|
+
(x) => allowed.includes(/** @type {any} */ (x))
|
|
1656
|
+
);
|
|
1657
|
+
|
|
1658
|
+
return /** @type {T[]} */ (result);
|
|
1412
1659
|
}
|
|
1413
1660
|
|
|
1414
1661
|
/**
|
|
@@ -1615,6 +1862,7 @@ class InputGuardAutoAttach {
|
|
|
1615
1862
|
* @returns {Rule}
|
|
1616
1863
|
*/
|
|
1617
1864
|
function numeric(options = {}) {
|
|
1865
|
+
/** @type {NumericRuleOptions} */
|
|
1618
1866
|
const opt = {
|
|
1619
1867
|
allowFullWidth: options.allowFullWidth ?? true,
|
|
1620
1868
|
allowMinus: options.allowMinus ?? false,
|
|
@@ -2053,6 +2301,7 @@ function roundFraction(intPart, fracPart, fracLimit) {
|
|
|
2053
2301
|
* @returns {Rule}
|
|
2054
2302
|
*/
|
|
2055
2303
|
function digits(options = {}) {
|
|
2304
|
+
/** @type {DigitsRuleOptions} */
|
|
2056
2305
|
const opt = {
|
|
2057
2306
|
int: typeof options.int === "number" ? options.int : undefined,
|
|
2058
2307
|
frac: typeof options.frac === "number" ? options.frac : undefined,
|
|
@@ -2378,6 +2627,4188 @@ comma.fromDataset = function fromDataset(dataset, _el) {
|
|
|
2378
2627
|
return comma();
|
|
2379
2628
|
};
|
|
2380
2629
|
|
|
2630
|
+
/**
|
|
2631
|
+
* The script is part of Mojix for TextInputGuard.
|
|
2632
|
+
*
|
|
2633
|
+
* AUTHOR:
|
|
2634
|
+
* natade-jp (https://github.com/natade-jp)
|
|
2635
|
+
*
|
|
2636
|
+
* LICENSE:
|
|
2637
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
2638
|
+
*/
|
|
2639
|
+
|
|
2640
|
+
/**
|
|
2641
|
+
* 制御文字マップ
|
|
2642
|
+
* @type {Record<number, string>}
|
|
2643
|
+
* @ignore
|
|
2644
|
+
*/
|
|
2645
|
+
let control_charcter_map = null;
|
|
2646
|
+
|
|
2647
|
+
/**
|
|
2648
|
+
* コードポイントからUnicodeのブロック名に変換する
|
|
2649
|
+
* @type {(codepoint: number) => (string)}
|
|
2650
|
+
* @ignore
|
|
2651
|
+
*/
|
|
2652
|
+
let toBlockNameFromUnicode = null;
|
|
2653
|
+
|
|
2654
|
+
/**
|
|
2655
|
+
* コードポイントから異体字セレクタの判定をする
|
|
2656
|
+
* @type {(codepoint: number, annotate?: boolean) => (string|null)}
|
|
2657
|
+
* @ignore
|
|
2658
|
+
*/
|
|
2659
|
+
let getVariationSelectorsnumberFromCodePoint = null;
|
|
2660
|
+
|
|
2661
|
+
/**
|
|
2662
|
+
* コードポイントからタグ文字の判定をする
|
|
2663
|
+
* @type {(codepoint: number) => (string|null)}
|
|
2664
|
+
* @ignore
|
|
2665
|
+
*/
|
|
2666
|
+
let getTagCharacterFromCodePoint = null;
|
|
2667
|
+
|
|
2668
|
+
/**
|
|
2669
|
+
* Unicode を扱うクラス
|
|
2670
|
+
* @ignore
|
|
2671
|
+
*/
|
|
2672
|
+
class Unicode {
|
|
2673
|
+
/**
|
|
2674
|
+
* 初期化
|
|
2675
|
+
*/
|
|
2676
|
+
static init() {
|
|
2677
|
+
if (Unicode.is_initmap) {
|
|
2678
|
+
return;
|
|
2679
|
+
}
|
|
2680
|
+
Unicode.is_initmap = true;
|
|
2681
|
+
|
|
2682
|
+
/**
|
|
2683
|
+
* 制御文字、VS、タグ文字は多いため含めていない
|
|
2684
|
+
*/
|
|
2685
|
+
// prettier-ignore
|
|
2686
|
+
control_charcter_map = {
|
|
2687
|
+
// --- C0 control characters (ASCII 0x00–0x1F) ---
|
|
2688
|
+
0: "NUL", // Null
|
|
2689
|
+
1: "SOH", // Start of Heading
|
|
2690
|
+
2: "STX", // Start of Text
|
|
2691
|
+
3: "ETX", // End of Text
|
|
2692
|
+
4: "EOT", // End of Transmission
|
|
2693
|
+
5: "ENQ", // Enquiry
|
|
2694
|
+
6: "ACK", // Acknowledge
|
|
2695
|
+
7: "BEL", // Bell (beep)
|
|
2696
|
+
|
|
2697
|
+
8: "BS", // Backspace
|
|
2698
|
+
9: "HT", // Horizontal Tab
|
|
2699
|
+
10: "LF", // Line Feed
|
|
2700
|
+
11: "VT", // Vertical Tab
|
|
2701
|
+
12: "FF", // Form Feed
|
|
2702
|
+
13: "CR", // Carriage Return
|
|
2703
|
+
14: "SO", // Shift Out
|
|
2704
|
+
15: "SI", // Shift In
|
|
2705
|
+
|
|
2706
|
+
16: "DLE", // Data Link Escape
|
|
2707
|
+
17: "DC1", // Device Control 1 (XON)
|
|
2708
|
+
18: "DC2", // Device Control 2
|
|
2709
|
+
19: "DC3", // Device Control 3 (XOFF)
|
|
2710
|
+
20: "DC4", // Device Control 4
|
|
2711
|
+
21: "NAK", // Negative Acknowledge
|
|
2712
|
+
22: "SYN", // Synchronous Idle
|
|
2713
|
+
23: "ETB", // End of Transmission Block
|
|
2714
|
+
|
|
2715
|
+
24: "CAN", // Cancel
|
|
2716
|
+
25: "EM", // End of Medium
|
|
2717
|
+
26: "SUB", // Substitute
|
|
2718
|
+
27: "ESC", // Escape
|
|
2719
|
+
28: "FS", // File Separator
|
|
2720
|
+
29: "GS", // Group Separator
|
|
2721
|
+
30: "RS", // Record Separator
|
|
2722
|
+
31: "US", // Unit Separator
|
|
2723
|
+
|
|
2724
|
+
// --- DEL ---
|
|
2725
|
+
127: "DEL", // Delete
|
|
2726
|
+
|
|
2727
|
+
// --- C1 control characters (ISO/IEC 6429, 0x80–0x9F) ---
|
|
2728
|
+
128: "PAD", // Padding Character
|
|
2729
|
+
129: "HOP", // High Octet Preset
|
|
2730
|
+
130: "BPH", // Break Permitted Here
|
|
2731
|
+
131: "NBH", // No Break Here
|
|
2732
|
+
132: "IND", // Index
|
|
2733
|
+
133: "NEL", // Next Line
|
|
2734
|
+
134: "SSA", // Start of Selected Area
|
|
2735
|
+
135: "ESA", // End of Selected Area
|
|
2736
|
+
136: "HTS", // Horizontal Tab Set
|
|
2737
|
+
137: "HTJ", // Horizontal Tab with Justification
|
|
2738
|
+
138: "VTS", // Vertical Tab Set
|
|
2739
|
+
139: "PLD", // Partial Line Down
|
|
2740
|
+
140: "PLU", // Partial Line Up
|
|
2741
|
+
141: "RI", // Reverse Index
|
|
2742
|
+
142: "SS2", // Single Shift 2
|
|
2743
|
+
143: "SS3", // Single Shift 3
|
|
2744
|
+
144: "DCS", // Device Control String
|
|
2745
|
+
145: "PU1", // Private Use 1
|
|
2746
|
+
146: "PU2", // Private Use 2
|
|
2747
|
+
147: "STS", // Set Transmit State
|
|
2748
|
+
148: "CCH", // Cancel Character
|
|
2749
|
+
149: "MW", // Message Waiting
|
|
2750
|
+
150: "SPA", // Start of Protected Area
|
|
2751
|
+
151: "EPA", // End of Protected Area
|
|
2752
|
+
152: "SOS", // Start of String
|
|
2753
|
+
153: "SGCI", // Single Graphic Character Introducer
|
|
2754
|
+
154: "SCI", // Single Character Introducer
|
|
2755
|
+
155: "CSI", // Control Sequence Introducer
|
|
2756
|
+
156: "ST", // String Terminator
|
|
2757
|
+
157: "OSC", // Operating System Command
|
|
2758
|
+
158: "PM", // Privacy Message
|
|
2759
|
+
159: "APC", // Application Program Command
|
|
2760
|
+
|
|
2761
|
+
// --- Unicode but制御的に扱われる文字 ---
|
|
2762
|
+
160: "NBSP", // No-Break Space(表示は空白だが改行不可)
|
|
2763
|
+
173: "SHY", // Soft Hyphen(通常は表示されない)
|
|
2764
|
+
|
|
2765
|
+
// --- Unicode Interlinear Annotation ---
|
|
2766
|
+
65529: "IAA", // Interlinear Annotation Anchor
|
|
2767
|
+
65530: "IAS", // Interlinear Annotation Separator
|
|
2768
|
+
65531: "IAT", // Interlinear Annotation Terminator
|
|
2769
|
+
|
|
2770
|
+
// Zero Width / Joiner 系(Cf)
|
|
2771
|
+
0x200B: "ZWSP", // ZERO WIDTH SPACE ゼロ幅スペース
|
|
2772
|
+
0x200C: "ZWNJ", // ZERO WIDTH NON-JOINER ゼロ幅非接合子
|
|
2773
|
+
0x200D: "ZWJ", // ZERO WIDTH JOINER ゼロ幅接合子
|
|
2774
|
+
0x2060: "WJ", // WORD JOINER 単語結合子
|
|
2775
|
+
0xFEFF: "BOM", // BYTE ORDER MARK / ZERO WIDTH NO-BREAK SPACE
|
|
2776
|
+
|
|
2777
|
+
// 双方向(BiDi)制御文字
|
|
2778
|
+
0x202A: "LRE", // LEFT-TO-RIGHT EMBEDDING
|
|
2779
|
+
0x202B: "RLE", // RIGHT-TO-LEFT EMBEDDING
|
|
2780
|
+
0x202C: "PDF", // POP DIRECTIONAL FORMATTING
|
|
2781
|
+
0x202D: "LRO", // LEFT-TO-RIGHT OVERRIDE
|
|
2782
|
+
0x202E: "RLO", // RIGHT-TO-LEFT OVERRIDE
|
|
2783
|
+
|
|
2784
|
+
0x2066: "LRI", // LEFT-TO-RIGHT ISOLATE
|
|
2785
|
+
0x2067: "RLI", // RIGHT-TO-LEFT ISOLATE
|
|
2786
|
+
0x2068: "FSI", // FIRST STRONG ISOLATE
|
|
2787
|
+
0x2069: "PDI", // POP DIRECTIONAL ISOLATE
|
|
2788
|
+
|
|
2789
|
+
// Unicode Noncharacter(検証・防御用途)
|
|
2790
|
+
0xFFFE: "NONCHAR_FFFE",
|
|
2791
|
+
0xFFFF: "NONCHAR_FFFF"
|
|
2792
|
+
};
|
|
2793
|
+
|
|
2794
|
+
// prettier-ignore
|
|
2795
|
+
const unicode_blockname_array = [
|
|
2796
|
+
"Basic Latin", "Latin-1 Supplement", "Latin Extended-A", "Latin Extended-B", "IPA Extensions", "Spacing Modifier Letters", "Combining Diacritical Marks", "Greek and Coptic",
|
|
2797
|
+
"Cyrillic", "Cyrillic Supplement", "Armenian", "Hebrew", "Arabic", "Syriac", "Arabic Supplement", "Thaana",
|
|
2798
|
+
"NKo", "Samaritan", "Mandaic", "Syriac Supplement", "Arabic Extended-B", "Arabic Extended-A", "Devanagari", "Bengali",
|
|
2799
|
+
"Gurmukhi", "Gujarati", "Oriya", "Tamil", "Telugu", "Kannada", "Malayalam", "Sinhala",
|
|
2800
|
+
"Thai", "Lao", "Tibetan", "Myanmar", "Georgian", "Hangul Jamo", "Ethiopic", "Ethiopic Supplement",
|
|
2801
|
+
"Cherokee", "Unified Canadian Aboriginal Syllabics", "Ogham", "Runic", "Tagalog", "Hanunoo", "Buhid", "Tagbanwa",
|
|
2802
|
+
"Khmer", "Mongolian", "Unified Canadian Aboriginal Syllabics Extended", "Limbu", "Tai Le", "New Tai Lue", "Khmer Symbols", "Buginese",
|
|
2803
|
+
"Tai Tham", "Combining Diacritical Marks Extended", "Balinese", "Sundanese", "Batak", "Lepcha", "Ol Chiki", "Cyrillic Extended-C",
|
|
2804
|
+
"Georgian Extended", "Sundanese Supplement", "Vedic Extensions", "Phonetic Extensions", "Phonetic Extensions Supplement", "Combining Diacritical Marks Supplement", "Latin Extended Additional", "Greek Extended",
|
|
2805
|
+
"General Punctuation", "Superscripts and Subscripts", "Currency Symbols", "Combining Diacritical Marks for Symbols", "Letterlike Symbols", "Number Forms", "Arrows", "Mathematical Operators",
|
|
2806
|
+
"Miscellaneous Technical", "Control Pictures", "Optical Character Recognition", "Enclosed Alphanumerics", "Box Drawing", "Block Elements", "Geometric Shapes", "Miscellaneous Symbols",
|
|
2807
|
+
"Dingbats", "Miscellaneous Mathematical Symbols-A", "Supplemental Arrows-A", "Braille Patterns", "Supplemental Arrows-B", "Miscellaneous Mathematical Symbols-B", "Supplemental Mathematical Operators", "Miscellaneous Symbols and Arrows",
|
|
2808
|
+
"Glagolitic", "Latin Extended-C", "Coptic", "Georgian Supplement", "Tifinagh", "Ethiopic Extended", "Cyrillic Extended-A", "Supplemental Punctuation",
|
|
2809
|
+
"CJK Radicals Supplement", "Kangxi Radicals", "Ideographic Description Characters", "CJK Symbols and Punctuation", "Hiragana", "Katakana", "Bopomofo", "Hangul Compatibility Jamo",
|
|
2810
|
+
"Kanbun", "Bopomofo Extended", "CJK Strokes", "Katakana Phonetic Extensions", "Enclosed CJK Letters and Months", "CJK Compatibility", "CJK Unified Ideographs Extension A", "Yijing Hexagram Symbols",
|
|
2811
|
+
"CJK Unified Ideographs", "Yi Syllables", "Yi Radicals", "Lisu", "Vai", "Cyrillic Extended-B", "Bamum", "Modifier Tone Letters",
|
|
2812
|
+
"Latin Extended-D", "Syloti Nagri", "Common Indic Number Forms", "Phags-pa", "Saurashtra", "Devanagari Extended", "Kayah Li", "Rejang",
|
|
2813
|
+
"Hangul Jamo Extended-A", "Javanese", "Myanmar Extended-B", "Cham", "Myanmar Extended-A", "Tai Viet", "Meetei Mayek Extensions", "Ethiopic Extended-A",
|
|
2814
|
+
"Latin Extended-E", "Cherokee Supplement", "Meetei Mayek", "Hangul Syllables", "Hangul Jamo Extended-B", "High Surrogates", "High Private Use Surrogates", "Low Surrogates",
|
|
2815
|
+
"Private Use Area", "CJK Compatibility Ideographs", "Alphabetic Presentation Forms", "Arabic Presentation Forms-A", "Variation Selectors", "Vertical Forms", "Combining Half Marks", "CJK Compatibility Forms",
|
|
2816
|
+
"Small Form Variants", "Arabic Presentation Forms-B", "Halfwidth and Fullwidth Forms", "Specials", "Linear B Syllabary", "Linear B Ideograms", "Aegean Numbers", "Ancient Greek Numbers",
|
|
2817
|
+
"Ancient Symbols", "Phaistos Disc", "Lycian", "Carian", "Coptic Epact Numbers", "Old Italic", "Gothic", "Old Permic",
|
|
2818
|
+
"Ugaritic", "Old Persian", "Deseret", "Shavian", "Osmanya", "Osage", "Elbasan", "Caucasian Albanian",
|
|
2819
|
+
"Vithkuqi", "Linear A", "Latin Extended-F", "Cypriot Syllabary", "Imperial Aramaic", "Palmyrene", "Nabataean", "Hatran",
|
|
2820
|
+
"Phoenician", "Lydian", "Meroitic Hieroglyphs", "Meroitic Cursive", "Kharoshthi", "Old South Arabian", "Old North Arabian", "Manichaean",
|
|
2821
|
+
"Avestan", "Inscriptional Parthian", "Inscriptional Pahlavi", "Psalter Pahlavi", "Old Turkic", "Old Hungarian", "Hanifi Rohingya", "Rumi Numeral Symbols",
|
|
2822
|
+
"Yezidi", "Arabic Extended-C", "Old Sogdian", "Sogdian", "Old Uyghur", "Chorasmian", "Elymaic", "Brahmi",
|
|
2823
|
+
"Kaithi", "Sora Sompeng", "Chakma", "Mahajani", "Sharada", "Sinhala Archaic Numbers", "Khojki", "Multani",
|
|
2824
|
+
"Khudawadi", "Grantha", "Newa", "Tirhuta", "Siddham", "Modi", "Mongolian Supplement", "Takri",
|
|
2825
|
+
"Ahom", "Dogra", "Warang Citi", "Dives Akuru", "Nandinagari", "Zanabazar Square", "Soyombo", "Unified Canadian Aboriginal Syllabics Extended-A",
|
|
2826
|
+
"Pau Cin Hau", "Devanagari Extended-A", "Bhaiksuki", "Marchen", "Masaram Gondi", "Gunjala Gondi", "Makasar", "Kawi",
|
|
2827
|
+
"Lisu Supplement", "Tamil Supplement", "Cuneiform", "Cuneiform Numbers and Punctuation", "Early Dynastic Cuneiform", "Cypro-Minoan", "Egyptian Hieroglyphs", "Egyptian Hieroglyph Format Controls",
|
|
2828
|
+
"Anatolian Hieroglyphs", "Bamum Supplement", "Mro", "Tangsa", "Bassa Vah", "Pahawh Hmong", "Medefaidrin", "Miao",
|
|
2829
|
+
"Ideographic Symbols and Punctuation", "Tangut", "Tangut Components", "Khitan Small Script", "Tangut Supplement", "Kana Extended-B", "Kana Supplement", "Kana Extended-A",
|
|
2830
|
+
"Small Kana Extension", "Nushu", "Duployan", "Shorthand Format Controls", "Znamenny Musical Notation", "Byzantine Musical Symbols", "Musical Symbols", "Ancient Greek Musical Notation",
|
|
2831
|
+
"Kaktovik Numerals", "Mayan Numerals", "Tai Xuan Jing Symbols", "Counting Rod Numerals", "Mathematical Alphanumeric Symbols", "Sutton SignWriting", "Latin Extended-G", "Glagolitic Supplement",
|
|
2832
|
+
"Cyrillic Extended-D", "Nyiakeng Puachue Hmong", "Toto", "Wancho", "Nag Mundari", "Ethiopic Extended-B", "Mende Kikakui", "Adlam",
|
|
2833
|
+
"Indic Siyaq Numbers", "Ottoman Siyaq Numbers", "Arabic Mathematical Alphabetic Symbols", "Mahjong Tiles", "Domino Tiles", "Playing Cards", "Enclosed Alphanumeric Supplement", "Enclosed Ideographic Supplement",
|
|
2834
|
+
"Miscellaneous Symbols and Pictographs", "Emoticons", "Ornamental Dingbats", "Transport and Map Symbols", "Alchemical Symbols", "Geometric Shapes Extended", "Supplemental Arrows-C", "Supplemental Symbols and Pictographs",
|
|
2835
|
+
"Chess Symbols", "Symbols and Pictographs Extended-A", "Symbols for Legacy Computing", "CJK Unified Ideographs Extension B", "CJK Unified Ideographs Extension C", "CJK Unified Ideographs Extension D", "CJK Unified Ideographs Extension E", "CJK Unified Ideographs Extension F", "CJK Unified Ideographs Extension I",
|
|
2836
|
+
"CJK Compatibility Ideographs Supplement", "CJK Unified Ideographs Extension G", "CJK Unified Ideographs Extension H", "CJK Unified Ideographs Extension J", "Tags", "Variation Selectors Supplement", "Supplementary Private Use Area-A", "Supplementary Private Use Area-B"
|
|
2837
|
+
];
|
|
2838
|
+
|
|
2839
|
+
/* eslint-disable max-len */
|
|
2840
|
+
// prettier-ignore
|
|
2841
|
+
const unicode_blockaddress_array = [
|
|
2842
|
+
0x007F, 0x00FF, 0x017F, 0x024F, 0x02AF, 0x02FF, 0x036F, 0x03FF, 0x04FF, 0x052F, 0x058F, 0x05FF, 0x06FF, 0x074F, 0x077F, 0x07BF,
|
|
2843
|
+
0x07FF, 0x083F, 0x085F, 0x086F, 0x089F, 0x08FF, 0x097F, 0x09FF, 0x0A7F, 0x0AFF, 0x0B7F, 0x0BFF, 0x0C7F, 0x0CFF, 0x0D7F, 0x0DFF,
|
|
2844
|
+
0x0E7F, 0x0EFF, 0x0FFF, 0x109F, 0x10FF, 0x11FF, 0x137F, 0x139F, 0x13FF, 0x167F, 0x169F, 0x16FF, 0x171F, 0x173F, 0x175F, 0x177F,
|
|
2845
|
+
0x17FF, 0x18AF, 0x18FF, 0x194F, 0x197F, 0x19DF, 0x19FF, 0x1A1F, 0x1AAF, 0x1AFF, 0x1B7F, 0x1BBF, 0x1BFF, 0x1C4F, 0x1C7F, 0x1C8F,
|
|
2846
|
+
0x1CBF, 0x1CCF, 0x1CFF, 0x1D7F, 0x1DBF, 0x1DFF, 0x1EFF, 0x1FFF, 0x206F, 0x209F, 0x20CF, 0x20FF, 0x214F, 0x218F, 0x21FF, 0x22FF,
|
|
2847
|
+
0x23FF, 0x243F, 0x245F, 0x24FF, 0x257F, 0x259F, 0x25FF, 0x26FF, 0x27BF, 0x27EF, 0x27FF, 0x28FF, 0x297F, 0x29FF, 0x2AFF, 0x2BFF,
|
|
2848
|
+
0x2C5F, 0x2C7F, 0x2CFF, 0x2D2F, 0x2D7F, 0x2DDF, 0x2DFF, 0x2E7F, 0x2EFF, 0x2FDF, 0x2FFF, 0x303F, 0x309F, 0x30FF, 0x312F, 0x318F,
|
|
2849
|
+
0x319F, 0x31BF, 0x31EF, 0x31FF, 0x32FF, 0x33FF, 0x4DBF, 0x4DFF, 0x9FFF, 0xA48F, 0xA4CF, 0xA4FF, 0xA63F, 0xA69F, 0xA6FF, 0xA71F,
|
|
2850
|
+
0xA7FF, 0xA82F, 0xA83F, 0xA87F, 0xA8DF, 0xA8FF, 0xA92F, 0xA95F, 0xA97F, 0xA9DF, 0xA9FF, 0xAA5F, 0xAA7F, 0xAADF, 0xAAFF, 0xAB2F,
|
|
2851
|
+
0xAB6F, 0xABBF, 0xABFF, 0xD7AF, 0xD7FF, 0xDB7F, 0xDBFF, 0xDFFF, 0xF8FF, 0xFAFF, 0xFB4F, 0xFDFF, 0xFE0F, 0xFE1F, 0xFE2F, 0xFE4F,
|
|
2852
|
+
0xFE6F, 0xFEFF, 0xFFEF, 0xFFFF, 0x1007F, 0x100FF, 0x1013F, 0x1018F, 0x101CF, 0x101FF, 0x1029F, 0x102DF, 0x102FF, 0x1032F, 0x1034F, 0x1037F,
|
|
2853
|
+
0x1039F, 0x103DF, 0x1044F, 0x1047F, 0x104AF, 0x104FF, 0x1052F, 0x1056F, 0x105BF, 0x1077F, 0x107BF, 0x1083F, 0x1085F, 0x1087F, 0x108AF, 0x108FF,
|
|
2854
|
+
0x1091F, 0x1093F, 0x1099F, 0x109FF, 0x10A5F, 0x10A7F, 0x10A9F, 0x10AFF, 0x10B3F, 0x10B5F, 0x10B7F, 0x10BAF, 0x10C4F, 0x10CFF, 0x10D3F, 0x10E7F,
|
|
2855
|
+
0x10EBF, 0x10EFF, 0x10F2F, 0x10F6F, 0x10FAF, 0x10FDF, 0x10FFF, 0x1107F, 0x110CF, 0x110FF, 0x1114F, 0x1117F, 0x111DF, 0x111FF, 0x1124F, 0x112AF,
|
|
2856
|
+
0x112FF, 0x1137F, 0x1147F, 0x114DF, 0x115FF, 0x1165F, 0x1167F, 0x116CF, 0x1174F, 0x1184F, 0x118FF, 0x1195F, 0x119FF, 0x11A4F, 0x11AAF, 0x11ABF,
|
|
2857
|
+
0x11AFF, 0x11B5F, 0x11C6F, 0x11CBF, 0x11D5F, 0x11DAF, 0x11EFF, 0x11F5F, 0x11FBF, 0x11FFF, 0x123FF, 0x1247F, 0x1254F, 0x12FFF, 0x1342F, 0x1345F,
|
|
2858
|
+
0x1467F, 0x16A3F, 0x16A6F, 0x16ACF, 0x16AFF, 0x16B8F, 0x16E9F, 0x16F9F, 0x16FFF, 0x187FF, 0x18AFF, 0x18CFF, 0x18D7F, 0x1AFFF, 0x1B0FF, 0x1B12F,
|
|
2859
|
+
0x1B16F, 0x1B2FF, 0x1BC9F, 0x1BCAF, 0x1CFCF, 0x1D0FF, 0x1D1FF, 0x1D24F, 0x1D2DF, 0x1D2FF, 0x1D35F, 0x1D37F, 0x1D7FF, 0x1DAAF, 0x1DFFF, 0x1E02F,
|
|
2860
|
+
0x1E08F, 0x1E14F, 0x1E2BF, 0x1E2FF, 0x1E4FF, 0x1E7FF, 0x1E8DF, 0x1E95F, 0x1ECBF, 0x1ED4F, 0x1EEFF, 0x1F02F, 0x1F09F, 0x1F0FF, 0x1F1FF, 0x1F2FF,
|
|
2861
|
+
0x1F5FF, 0x1F64F, 0x1F67F, 0x1F6FF, 0x1F77F, 0x1F7FF, 0x1F8FF, 0x1F9FF, 0x1FA6F, 0x1FAFF, 0x1FBFF, 0x2A6DF, 0x2B73F, 0x2B81F, 0x2CEAF, 0x2EBEF, 0x2EE5F,
|
|
2862
|
+
0x2FA1F, 0x3134F, 0x323AF, 0x3347F, 0xE007F, 0xE01EF, 0xFFFFF, 0x10FFFF
|
|
2863
|
+
];
|
|
2864
|
+
/* eslint-enable max-len */
|
|
2865
|
+
|
|
2866
|
+
/**
|
|
2867
|
+
* コードポイントからUnicodeのブロック名に変換する
|
|
2868
|
+
* 変換できない場合は "-" を返す
|
|
2869
|
+
* @param {number} codepoint - コードポイント
|
|
2870
|
+
* @returns {string}
|
|
2871
|
+
*/
|
|
2872
|
+
toBlockNameFromUnicode = function (codepoint) {
|
|
2873
|
+
for (let i = 0; i < unicode_blockname_array.length; i++) {
|
|
2874
|
+
if (codepoint <= unicode_blockaddress_array[i]) {
|
|
2875
|
+
return unicode_blockname_array[i];
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
return "-";
|
|
2879
|
+
};
|
|
2880
|
+
|
|
2881
|
+
/**
|
|
2882
|
+
* コードポイントから異体字セレクタの判定
|
|
2883
|
+
* @param {number} codepoint - コードポイント
|
|
2884
|
+
* @param {boolean} [annotate = false] - 注釈をつけるか否か
|
|
2885
|
+
* @returns {string|null} 確認結果(異体字セレクタではない場合はNULLを返す)
|
|
2886
|
+
*/
|
|
2887
|
+
getVariationSelectorsnumberFromCodePoint = function (codepoint, annotate) {
|
|
2888
|
+
// prettier-ignore
|
|
2889
|
+
if (0x180B <= codepoint && codepoint <= 0x180D) {
|
|
2890
|
+
// モンゴル自由字形選択子 U+180B〜U+180D (3個)
|
|
2891
|
+
// prettier-ignore
|
|
2892
|
+
return "FVS" + (codepoint - 0x180B + 1);
|
|
2893
|
+
}
|
|
2894
|
+
// prettier-ignore
|
|
2895
|
+
if (0xFE00 <= codepoint && codepoint <= 0xFE0F) {
|
|
2896
|
+
// SVSで利用される異体字セレクタ U+FE00〜U+FE0F (VS1~VS16) (16個)
|
|
2897
|
+
// prettier-ignore
|
|
2898
|
+
const n = codepoint - 0xFE00 + 1;
|
|
2899
|
+
if (!annotate) { return "VS" + n; }
|
|
2900
|
+
// prettier-ignore
|
|
2901
|
+
if (codepoint === 0xFE0E) { return "VS15 (text)"; }
|
|
2902
|
+
// prettier-ignore
|
|
2903
|
+
if (codepoint === 0xFE0F) { return "VS16 (emoji)"; }
|
|
2904
|
+
return "VS" + n;
|
|
2905
|
+
// prettier-ignore
|
|
2906
|
+
} else if (0xE0100 <= codepoint && codepoint <= 0xE01EF) {
|
|
2907
|
+
// IVSで利用される異体字セレクタ U+E0100〜U+E01EF (VS17~VS256) (240個)
|
|
2908
|
+
// prettier-ignore
|
|
2909
|
+
return "VS" + (codepoint - 0xE0100 + 17);
|
|
2910
|
+
}
|
|
2911
|
+
return null;
|
|
2912
|
+
};
|
|
2913
|
+
|
|
2914
|
+
/**
|
|
2915
|
+
* コードポイントからタグ文字の判定
|
|
2916
|
+
* @param {number} codepoint - コードポイント
|
|
2917
|
+
* @returns {string|null} 確認結果(タグ文字ではない場合はNULLを返す)
|
|
2918
|
+
*/
|
|
2919
|
+
getTagCharacterFromCodePoint = function (codepoint) {
|
|
2920
|
+
// TAG characters U+E0020..U+E007F
|
|
2921
|
+
// prettier-ignore
|
|
2922
|
+
if (0xE0020 <= codepoint && codepoint <= 0xE007F) {
|
|
2923
|
+
// CANCEL TAG
|
|
2924
|
+
// prettier-ignore
|
|
2925
|
+
if (codepoint === 0xE007F) {
|
|
2926
|
+
return "CANCEL_TAG";
|
|
2927
|
+
}
|
|
2928
|
+
// TAG_20..TAG_7E のように返す
|
|
2929
|
+
// prettier-ignore
|
|
2930
|
+
const ascii = codepoint - 0xE0000; // 0x20..0x7E
|
|
2931
|
+
return "TAG_" + ascii.toString(16).toUpperCase().padStart(2, "0");
|
|
2932
|
+
}
|
|
2933
|
+
return null;
|
|
2934
|
+
};
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
/**
|
|
2938
|
+
* 上位のサロゲートペアの判定
|
|
2939
|
+
* @param {string} text - 対象テキスト
|
|
2940
|
+
* @param {number} index - インデックス
|
|
2941
|
+
* @returns {boolean} 確認結果
|
|
2942
|
+
*/
|
|
2943
|
+
static isHighSurrogateAt(text, index) {
|
|
2944
|
+
const ch = text.charCodeAt(index);
|
|
2945
|
+
// prettier-ignore
|
|
2946
|
+
return 0xD800 <= ch && ch <= 0xDBFF;
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
/**
|
|
2950
|
+
* 下位のサロゲートペアの判定
|
|
2951
|
+
* @param {string} text - 対象テキスト
|
|
2952
|
+
* @param {number} index - インデックス
|
|
2953
|
+
* @returns {boolean} 確認結果
|
|
2954
|
+
*/
|
|
2955
|
+
static isLowSurrogateAt(text, index) {
|
|
2956
|
+
const ch = text.charCodeAt(index);
|
|
2957
|
+
// prettier-ignore
|
|
2958
|
+
return 0xDC00 <= ch && ch <= 0xDFFF;
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
/**
|
|
2962
|
+
* サロゲートペアの判定
|
|
2963
|
+
* @param {string} text - 対象テキスト
|
|
2964
|
+
* @param {number} index - インデックス
|
|
2965
|
+
* @returns {boolean} 確認結果
|
|
2966
|
+
*/
|
|
2967
|
+
static isSurrogatePairAt(text, index) {
|
|
2968
|
+
const ch = text.charCodeAt(index);
|
|
2969
|
+
// prettier-ignore
|
|
2970
|
+
return 0xD800 <= ch && ch <= 0xDFFF;
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
/**
|
|
2974
|
+
* サロゲートペア対応のコードポイント取得
|
|
2975
|
+
* @param {string} text - 対象テキスト
|
|
2976
|
+
* @param {number} [index = 0] - インデックス
|
|
2977
|
+
* @returns {number} コードポイント
|
|
2978
|
+
*/
|
|
2979
|
+
static codePointAt(text, index) {
|
|
2980
|
+
const index_ = index !== undefined ? index : 0;
|
|
2981
|
+
if (Unicode.isHighSurrogateAt(text, index_)) {
|
|
2982
|
+
const high = text.charCodeAt(index_);
|
|
2983
|
+
const low = text.charCodeAt(index_ + 1);
|
|
2984
|
+
// prettier-ignore
|
|
2985
|
+
return (((high - 0xD800) << 10) | (low - 0xDC00)) + 0x10000;
|
|
2986
|
+
} else {
|
|
2987
|
+
return text.charCodeAt(index_);
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
/**
|
|
2992
|
+
* インデックスの前にあるコードポイント
|
|
2993
|
+
* @param {string} text - 対象テキスト
|
|
2994
|
+
* @param {number} index - インデックス
|
|
2995
|
+
* @returns {number} コードポイント
|
|
2996
|
+
*/
|
|
2997
|
+
static codePointBefore(text, index) {
|
|
2998
|
+
if (!Unicode.isLowSurrogateAt(text, index - 1)) {
|
|
2999
|
+
return text.charCodeAt(index - 1);
|
|
3000
|
+
} else {
|
|
3001
|
+
return text.codePointAt(index - 2);
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
|
|
3005
|
+
/**
|
|
3006
|
+
* コードポイント換算で文字列数をカウント
|
|
3007
|
+
* @param {string} text - 対象テキスト
|
|
3008
|
+
* @param {number} [beginIndex=0] - 最初のインデックス(省略可)
|
|
3009
|
+
* @param {number} [endIndex] - 最後のインデックス(ここは含めない)(省略可)
|
|
3010
|
+
* @returns {number} 文字数
|
|
3011
|
+
*/
|
|
3012
|
+
static codePointCount(text, beginIndex, endIndex) {
|
|
3013
|
+
if (beginIndex === undefined) {
|
|
3014
|
+
beginIndex = 0;
|
|
3015
|
+
}
|
|
3016
|
+
if (endIndex === undefined) {
|
|
3017
|
+
endIndex = text.length;
|
|
3018
|
+
}
|
|
3019
|
+
let count = 0;
|
|
3020
|
+
for (; beginIndex < endIndex; beginIndex++) {
|
|
3021
|
+
count++;
|
|
3022
|
+
if (Unicode.isSurrogatePairAt(text, beginIndex)) {
|
|
3023
|
+
beginIndex++;
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
return count;
|
|
3027
|
+
}
|
|
3028
|
+
|
|
3029
|
+
/**
|
|
3030
|
+
* コードポイント換算で文字列配列の位置を計算
|
|
3031
|
+
* @param {string} text - 対象テキスト
|
|
3032
|
+
* @param {number} index - オフセット
|
|
3033
|
+
* @param {number} codePointOffset - ずらすコードポイント数
|
|
3034
|
+
* @returns {number} ずらしたインデックス
|
|
3035
|
+
*/
|
|
3036
|
+
static offsetByCodePoints(text, index, codePointOffset) {
|
|
3037
|
+
let count = 0;
|
|
3038
|
+
if (codePointOffset === 0) {
|
|
3039
|
+
return index;
|
|
3040
|
+
}
|
|
3041
|
+
if (codePointOffset > 0) {
|
|
3042
|
+
for (; index < text.length; index++) {
|
|
3043
|
+
count++;
|
|
3044
|
+
if (Unicode.isHighSurrogateAt(text, index)) {
|
|
3045
|
+
index++;
|
|
3046
|
+
}
|
|
3047
|
+
if (count === codePointOffset) {
|
|
3048
|
+
return index + 1;
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
} else {
|
|
3052
|
+
codePointOffset = -codePointOffset;
|
|
3053
|
+
for (; index >= 0; index--) {
|
|
3054
|
+
count++;
|
|
3055
|
+
if (Unicode.isLowSurrogateAt(text, index - 1)) {
|
|
3056
|
+
index--;
|
|
3057
|
+
}
|
|
3058
|
+
if (count === codePointOffset) {
|
|
3059
|
+
return index - 1;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
throw "error offsetByCodePoints";
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
/**
|
|
3067
|
+
* コードポイントの数値データをUTF16の配列に変換
|
|
3068
|
+
* @param {...(number|number[])} codepoint - 変換したいUTF-32の配列、又はコードポイントを並べた可変引数
|
|
3069
|
+
* @returns {number[]} 変換後のテキスト
|
|
3070
|
+
*/
|
|
3071
|
+
static toUTF16ArrayFromCodePoint() {
|
|
3072
|
+
/**
|
|
3073
|
+
* @type {number[]}
|
|
3074
|
+
*/
|
|
3075
|
+
const utf16_array = [];
|
|
3076
|
+
/**
|
|
3077
|
+
* @type {number[]}
|
|
3078
|
+
*/
|
|
3079
|
+
let codepoint_array = [];
|
|
3080
|
+
if (arguments[0].length) {
|
|
3081
|
+
codepoint_array = arguments[0];
|
|
3082
|
+
} else {
|
|
3083
|
+
for (let i = 0; i < arguments.length; i++) {
|
|
3084
|
+
codepoint_array[i] = arguments[i];
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
for (let i = 0; i < codepoint_array.length; i++) {
|
|
3088
|
+
const codepoint = codepoint_array[i];
|
|
3089
|
+
if (0x10000 <= codepoint) {
|
|
3090
|
+
// prettier-ignore
|
|
3091
|
+
const high = ((codepoint - 0x10000) >> 10) + 0xD800;
|
|
3092
|
+
// prettier-ignore
|
|
3093
|
+
const low = (codepoint & 0x3FF) + 0xDC00;
|
|
3094
|
+
utf16_array.push(high);
|
|
3095
|
+
utf16_array.push(low);
|
|
3096
|
+
} else {
|
|
3097
|
+
utf16_array.push(codepoint);
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
return utf16_array;
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
/**
|
|
3104
|
+
* コードポイントの数値データを文字列に変換
|
|
3105
|
+
* @param {...(number|number[])} codepoint - 変換したいコードポイントの数値配列、又は数値を並べた可変引数
|
|
3106
|
+
* @returns {string} 変換後のテキスト
|
|
3107
|
+
*/
|
|
3108
|
+
static fromCodePoint(codepoint) {
|
|
3109
|
+
/** @type {number[]} */
|
|
3110
|
+
let utf16_array;
|
|
3111
|
+
if (Array.isArray(codepoint)) {
|
|
3112
|
+
utf16_array = Unicode.toUTF16ArrayFromCodePoint(codepoint);
|
|
3113
|
+
} else {
|
|
3114
|
+
const codepoint_array = [];
|
|
3115
|
+
for (let i = 0; i < arguments.length; i++) {
|
|
3116
|
+
codepoint_array[i] = arguments[i];
|
|
3117
|
+
}
|
|
3118
|
+
utf16_array = Unicode.toUTF16ArrayFromCodePoint(codepoint_array);
|
|
3119
|
+
}
|
|
3120
|
+
const text = [];
|
|
3121
|
+
for (let i = 0; i < utf16_array.length; i++) {
|
|
3122
|
+
text[text.length] = String.fromCharCode(utf16_array[i]);
|
|
3123
|
+
}
|
|
3124
|
+
return text.join("");
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
/**
|
|
3128
|
+
* 文字列をUTF32(コードポイント)の配列に変換
|
|
3129
|
+
* @param {string} text - 変換したいテキスト
|
|
3130
|
+
* @returns {number[]} UTF32(コードポイント)のデータが入った配列
|
|
3131
|
+
*/
|
|
3132
|
+
static toUTF32Array(text) {
|
|
3133
|
+
const utf32 = [];
|
|
3134
|
+
for (let i = 0; i < text.length; i = Unicode.offsetByCodePoints(text, i, 1)) {
|
|
3135
|
+
utf32.push(Unicode.codePointAt(text, i));
|
|
3136
|
+
}
|
|
3137
|
+
return utf32;
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
/**
|
|
3141
|
+
* UTF32の配列から文字列に変換
|
|
3142
|
+
* @param {number[]} utf32 - 変換したいテキスト
|
|
3143
|
+
* @returns {string} 変換後のテキスト
|
|
3144
|
+
*/
|
|
3145
|
+
static fromUTF32Array(utf32) {
|
|
3146
|
+
return Unicode.fromCodePoint(utf32);
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
/**
|
|
3150
|
+
* 文字列をUTF16の配列に変換
|
|
3151
|
+
* @param {string} text - 変換したいテキスト
|
|
3152
|
+
* @returns {number[]} UTF16のデータが入った配列
|
|
3153
|
+
*/
|
|
3154
|
+
static toUTF16Array(text) {
|
|
3155
|
+
const utf16 = [];
|
|
3156
|
+
for (let i = 0; i < text.length; i++) {
|
|
3157
|
+
utf16[i] = text.charCodeAt(i);
|
|
3158
|
+
}
|
|
3159
|
+
return utf16;
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
/**
|
|
3163
|
+
* UTF16の配列から文字列に変換
|
|
3164
|
+
* @param {number[]} utf16 - 変換したいテキスト
|
|
3165
|
+
* @returns {string} 変換後のテキスト
|
|
3166
|
+
*/
|
|
3167
|
+
static fromUTF16Array(utf16) {
|
|
3168
|
+
const text = [];
|
|
3169
|
+
for (let i = 0; i < utf16.length; i++) {
|
|
3170
|
+
text[i] = String.fromCharCode(utf16[i]);
|
|
3171
|
+
}
|
|
3172
|
+
return text.join("");
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
/**
|
|
3176
|
+
* 文字列をUTF8の配列に変換
|
|
3177
|
+
* @param {string} text - 変換したいテキスト
|
|
3178
|
+
* @returns {number[]} UTF8のデータが入った配列
|
|
3179
|
+
*/
|
|
3180
|
+
static toUTF8Array(text) {
|
|
3181
|
+
return Unicode.toUTFBinaryFromCodePoint(Unicode.toUTF32Array(text), "utf-8", false);
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
/**
|
|
3185
|
+
* UTF8の配列から文字列に変換
|
|
3186
|
+
* @param {number[]} utf8 - 変換したいテキスト
|
|
3187
|
+
* @returns {string} 変換後のテキスト
|
|
3188
|
+
*/
|
|
3189
|
+
static fromUTF8Array(utf8) {
|
|
3190
|
+
return Unicode.fromCodePoint(Unicode.toCodePointFromUTFBinary(utf8, "utf-8"));
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
/**
|
|
3194
|
+
* 指定したテキストを切り出す
|
|
3195
|
+
* - 単位は文字数
|
|
3196
|
+
* @param {string} text - 切り出したいテキスト
|
|
3197
|
+
* @param {number} offset - 切り出し位置
|
|
3198
|
+
* @param {number} size - 切り出す長さ
|
|
3199
|
+
* @returns {string} 切り出したテキスト
|
|
3200
|
+
*/
|
|
3201
|
+
static cutTextForCodePoint(text, offset, size) {
|
|
3202
|
+
const utf32 = Unicode.toUTF32Array(text);
|
|
3203
|
+
const cut = [];
|
|
3204
|
+
for (let i = 0, point = offset; i < size && point < utf32.length; i++, point++) {
|
|
3205
|
+
cut.push(utf32[point]);
|
|
3206
|
+
}
|
|
3207
|
+
return Unicode.fromUTF32Array(cut);
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
/**
|
|
3211
|
+
* UTFのバイナリ配列からバイトオーダーマーク(BOM)を調査する
|
|
3212
|
+
* @param {number[]} utfbinary - 調査するバイナリ配列
|
|
3213
|
+
* @returns {string} 符号化形式(不明時はnull)
|
|
3214
|
+
*/
|
|
3215
|
+
static getCharsetFromBOM(utfbinary) {
|
|
3216
|
+
if (utfbinary.length >= 4) {
|
|
3217
|
+
// prettier-ignore
|
|
3218
|
+
if (utfbinary[0] === 0x00 && utfbinary[1] === 0x00 && utfbinary[2] === 0xFE && utfbinary[3] === 0xFF) {
|
|
3219
|
+
return "UTF-32BE";
|
|
3220
|
+
}
|
|
3221
|
+
// prettier-ignore
|
|
3222
|
+
if (utfbinary[0] === 0xFF && utfbinary[1] === 0xFE && utfbinary[2] === 0x00 && utfbinary[3] === 0x00) {
|
|
3223
|
+
return "UTF-32LE";
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
if (utfbinary.length >= 3) {
|
|
3227
|
+
// prettier-ignore
|
|
3228
|
+
if (utfbinary[0] === 0xEF && utfbinary[1] === 0xBB && utfbinary[2] === 0xBF) {
|
|
3229
|
+
return "UTF-8";
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
if (utfbinary.length >= 2) {
|
|
3233
|
+
// prettier-ignore
|
|
3234
|
+
if (utfbinary[0] === 0xFE && utfbinary[1] === 0xFF) {
|
|
3235
|
+
return "UTF-16BE";
|
|
3236
|
+
}
|
|
3237
|
+
// prettier-ignore
|
|
3238
|
+
if (utfbinary[0] === 0xFF && utfbinary[1] === 0xFE) {
|
|
3239
|
+
return "UTF-16LE";
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
return null;
|
|
3243
|
+
}
|
|
3244
|
+
|
|
3245
|
+
/**
|
|
3246
|
+
* UTFのバイナリ配列からコードポイントに変換
|
|
3247
|
+
* @param {number[]} binary - 変換したいバイナリ配列
|
|
3248
|
+
* @param {string} [charset] - UTFの種類(省略した場合はBOM付きを期待する)
|
|
3249
|
+
* @returns {number[]} コードポイントの配列(失敗時はnull)
|
|
3250
|
+
*/
|
|
3251
|
+
static toCodePointFromUTFBinary(binary, charset) {
|
|
3252
|
+
const utf32_array = [];
|
|
3253
|
+
let check_charset = charset;
|
|
3254
|
+
let offset = 0;
|
|
3255
|
+
// バイトオーダーマーク(BOM)がある場合は BOM を優先
|
|
3256
|
+
const charset_for_bom = Unicode.getCharsetFromBOM(binary);
|
|
3257
|
+
if (charset_for_bom) {
|
|
3258
|
+
check_charset = charset_for_bom;
|
|
3259
|
+
if (/utf-?8/i.test(charset_for_bom)) {
|
|
3260
|
+
offset = 3;
|
|
3261
|
+
} else if (/utf-?16/i.test(charset_for_bom)) {
|
|
3262
|
+
offset = 2;
|
|
3263
|
+
} else if (/utf-?32/i.test(charset_for_bom)) {
|
|
3264
|
+
offset = 4;
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
// BOM付きではない+指定もしていないので変換失敗
|
|
3268
|
+
if (!charset_for_bom && !charset) {
|
|
3269
|
+
return null;
|
|
3270
|
+
}
|
|
3271
|
+
if (/utf-?8n?/i.test(check_charset)) {
|
|
3272
|
+
// UTF-8
|
|
3273
|
+
let size = 0;
|
|
3274
|
+
let write = 0;
|
|
3275
|
+
for (let i = offset; i < binary.length; i++) {
|
|
3276
|
+
const bin = binary[i];
|
|
3277
|
+
if (size === 0) {
|
|
3278
|
+
if (bin < 0x80) {
|
|
3279
|
+
utf32_array.push(bin);
|
|
3280
|
+
// prettier-ignore
|
|
3281
|
+
} else if (bin < 0xE0) {
|
|
3282
|
+
size = 1;
|
|
3283
|
+
// prettier-ignore
|
|
3284
|
+
write = bin & 0x1F; // 0001 1111
|
|
3285
|
+
// prettier-ignore
|
|
3286
|
+
} else if (bin < 0xF0) {
|
|
3287
|
+
size = 2;
|
|
3288
|
+
// prettier-ignore
|
|
3289
|
+
write = bin & 0xF; // 0000 1111
|
|
3290
|
+
} else {
|
|
3291
|
+
size = 3;
|
|
3292
|
+
// prettier-ignore
|
|
3293
|
+
write = bin & 0x7; // 0000 0111
|
|
3294
|
+
}
|
|
3295
|
+
} else {
|
|
3296
|
+
write <<= 6;
|
|
3297
|
+
// prettier-ignore
|
|
3298
|
+
write |= bin & 0x3F; // 0011 1111
|
|
3299
|
+
size--;
|
|
3300
|
+
if (size === 0) {
|
|
3301
|
+
utf32_array.push(write);
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
return utf32_array;
|
|
3306
|
+
} else if (/utf-?16/i.test(check_charset)) {
|
|
3307
|
+
// UTF-16
|
|
3308
|
+
// UTF-16 につめる
|
|
3309
|
+
const utf16 = [];
|
|
3310
|
+
if (/utf-?16(be)/i.test(check_charset)) {
|
|
3311
|
+
// UTF-16BE
|
|
3312
|
+
for (let i = offset; i < binary.length; i += 2) {
|
|
3313
|
+
utf16.push((binary[i] << 8) | binary[i + 1]);
|
|
3314
|
+
}
|
|
3315
|
+
} else if (/utf-?16(le)?/i.test(check_charset)) {
|
|
3316
|
+
// UTF-16LE
|
|
3317
|
+
for (let i = offset; i < binary.length; i += 2) {
|
|
3318
|
+
utf16.push(binary[i] | (binary[i + 1] << 8));
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
// UTF-32 につめる
|
|
3322
|
+
for (let i = 0; i < utf16.length; i++) {
|
|
3323
|
+
// prettier-ignore
|
|
3324
|
+
if (0xD800 <= utf16[i] && utf16[i] <= 0xDBFF) {
|
|
3325
|
+
if (i + 2 <= utf16.length) {
|
|
3326
|
+
const high = utf16[i];
|
|
3327
|
+
const low = utf16[i + 1];
|
|
3328
|
+
// prettier-ignore
|
|
3329
|
+
utf32_array.push((((high - 0xD800) << 10) | (low - 0xDC00)) + 0x10000);
|
|
3330
|
+
}
|
|
3331
|
+
i++;
|
|
3332
|
+
} else {
|
|
3333
|
+
utf32_array.push(utf16[i]);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
return utf32_array;
|
|
3337
|
+
} else {
|
|
3338
|
+
// UTF-32
|
|
3339
|
+
if (/utf-?32(be)/i.test(check_charset)) {
|
|
3340
|
+
// UTF-32BE
|
|
3341
|
+
for (let i = offset; i < binary.length; i += 4) {
|
|
3342
|
+
utf32_array.push((binary[i] << 24) | (binary[i + 1] << 16) | (binary[i + 2] << 8) | binary[i + 3]);
|
|
3343
|
+
}
|
|
3344
|
+
return utf32_array;
|
|
3345
|
+
} else if (/utf-?32(le)?/i.test(check_charset)) {
|
|
3346
|
+
// UTF-32LE
|
|
3347
|
+
for (let i = offset; i < binary.length; i += 4) {
|
|
3348
|
+
utf32_array.push(binary[i] | (binary[i + 1] << 8) | (binary[i + 2] << 16) | (binary[i + 3] << 24));
|
|
3349
|
+
}
|
|
3350
|
+
return utf32_array;
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
return null;
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
/**
|
|
3357
|
+
* UTF32配列からバイナリ配列に変換
|
|
3358
|
+
* @param {number[]} utf32_array - 変換したいUTF-32配列
|
|
3359
|
+
* @param {string} charset - UTFの種類
|
|
3360
|
+
* @param {boolean} [is_with_bom=true] - BOMをつけるかどうか
|
|
3361
|
+
* @returns {number[]} バイナリ配列(失敗時はnull)
|
|
3362
|
+
*/
|
|
3363
|
+
static toUTFBinaryFromCodePoint(utf32_array, charset, is_with_bom) {
|
|
3364
|
+
let is_with_bom_ = is_with_bom !== undefined ? is_with_bom : true;
|
|
3365
|
+
// charset に" with BOM" が入っている場合はBOM付きとする
|
|
3366
|
+
if (/\s+with\s+bom$/i.test(charset)) {
|
|
3367
|
+
is_with_bom_ = true;
|
|
3368
|
+
}
|
|
3369
|
+
/**
|
|
3370
|
+
* @type {number[]}
|
|
3371
|
+
*/
|
|
3372
|
+
const binary = [];
|
|
3373
|
+
// UTF-8
|
|
3374
|
+
if (/utf-?8n?/i.test(charset)) {
|
|
3375
|
+
// bom をつける
|
|
3376
|
+
if (is_with_bom_) {
|
|
3377
|
+
// prettier-ignore
|
|
3378
|
+
binary.push(0xEF);
|
|
3379
|
+
// prettier-ignore
|
|
3380
|
+
binary.push(0xBB);
|
|
3381
|
+
// prettier-ignore
|
|
3382
|
+
binary.push(0xBF);
|
|
3383
|
+
}
|
|
3384
|
+
for (let i = 0; i < utf32_array.length; i++) {
|
|
3385
|
+
let codepoint = utf32_array[i];
|
|
3386
|
+
// 1バイト文字
|
|
3387
|
+
if (codepoint <= 0x7F) {
|
|
3388
|
+
binary.push(codepoint);
|
|
3389
|
+
continue;
|
|
3390
|
+
}
|
|
3391
|
+
const buffer = [];
|
|
3392
|
+
/** @type {number} */
|
|
3393
|
+
let size;
|
|
3394
|
+
// 2バイト以上
|
|
3395
|
+
if (codepoint < 0x800) {
|
|
3396
|
+
size = 2;
|
|
3397
|
+
} else if (codepoint < 0x10000) {
|
|
3398
|
+
size = 3;
|
|
3399
|
+
} else {
|
|
3400
|
+
size = 4;
|
|
3401
|
+
}
|
|
3402
|
+
for (let j = 0; j < size; j++) {
|
|
3403
|
+
let write = codepoint & ((1 << 6) - 1);
|
|
3404
|
+
if (j === size - 1) {
|
|
3405
|
+
if (size === 2) {
|
|
3406
|
+
// prettier-ignore
|
|
3407
|
+
write |= 0xC0; // 1100 0000
|
|
3408
|
+
} else if (size === 3) {
|
|
3409
|
+
// prettier-ignore
|
|
3410
|
+
write |= 0xE0; // 1110 0000
|
|
3411
|
+
} else {
|
|
3412
|
+
// prettier-ignore
|
|
3413
|
+
write |= 0xF0; // 1111 0000
|
|
3414
|
+
}
|
|
3415
|
+
buffer.push(write);
|
|
3416
|
+
break;
|
|
3417
|
+
}
|
|
3418
|
+
buffer.push(write | 0x80); // 1000 0000
|
|
3419
|
+
codepoint = codepoint >> 6;
|
|
3420
|
+
}
|
|
3421
|
+
// 反転
|
|
3422
|
+
for (let j = buffer.length - 1; j >= 0; j--) {
|
|
3423
|
+
binary.push(buffer[j]);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
return binary;
|
|
3427
|
+
} else if (/utf-?16/i.test(charset)) {
|
|
3428
|
+
// UTF-16
|
|
3429
|
+
// UTF-16 に詰め替える
|
|
3430
|
+
const utf16_array = Unicode.toUTF16ArrayFromCodePoint(utf32_array);
|
|
3431
|
+
if (/utf-?16(be)/i.test(charset)) {
|
|
3432
|
+
// UTF-16BE
|
|
3433
|
+
// bom をつける
|
|
3434
|
+
if (is_with_bom_) {
|
|
3435
|
+
binary.push(0xFE);
|
|
3436
|
+
binary.push(0xFF);
|
|
3437
|
+
}
|
|
3438
|
+
for (let i = 0; i < utf16_array.length; i++) {
|
|
3439
|
+
binary.push(utf16_array[i] >> 8);
|
|
3440
|
+
binary.push(utf16_array[i] & 0xFF);
|
|
3441
|
+
}
|
|
3442
|
+
} else if (/utf-?16(le)?/i.test(charset)) {
|
|
3443
|
+
// UTF-16LE
|
|
3444
|
+
// bom をつける
|
|
3445
|
+
if (is_with_bom_) {
|
|
3446
|
+
binary.push(0xFF);
|
|
3447
|
+
binary.push(0xFE);
|
|
3448
|
+
}
|
|
3449
|
+
for (let i = 0; i < utf16_array.length; i++) {
|
|
3450
|
+
binary.push(utf16_array[i] & 0xFF);
|
|
3451
|
+
binary.push(utf16_array[i] >> 8);
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
return binary;
|
|
3455
|
+
} else if (/utf-?32/i.test(charset)) {
|
|
3456
|
+
// UTF-32
|
|
3457
|
+
if (/utf-?32(be)/i.test(charset)) {
|
|
3458
|
+
// UTF-32BE
|
|
3459
|
+
// bom をつける
|
|
3460
|
+
if (is_with_bom_) {
|
|
3461
|
+
binary.push(0x00);
|
|
3462
|
+
binary.push(0x00);
|
|
3463
|
+
binary.push(0xFE);
|
|
3464
|
+
binary.push(0xFF);
|
|
3465
|
+
}
|
|
3466
|
+
for (let i = 0; i < utf32_array.length; i++) {
|
|
3467
|
+
binary.push((utf32_array[i] >> 24) & 0xFF);
|
|
3468
|
+
binary.push((utf32_array[i] >> 16) & 0xFF);
|
|
3469
|
+
binary.push((utf32_array[i] >> 8) & 0xFF);
|
|
3470
|
+
binary.push(utf32_array[i] & 0xFF);
|
|
3471
|
+
}
|
|
3472
|
+
} else if (/utf-?32(le)?/i.test(charset)) {
|
|
3473
|
+
// UTF-32LE
|
|
3474
|
+
// bom をつける
|
|
3475
|
+
if (is_with_bom_) {
|
|
3476
|
+
binary.push(0xFF);
|
|
3477
|
+
binary.push(0xFE);
|
|
3478
|
+
binary.push(0x00);
|
|
3479
|
+
binary.push(0x00);
|
|
3480
|
+
}
|
|
3481
|
+
for (let i = 0; i < utf32_array.length; i++) {
|
|
3482
|
+
binary.push(utf32_array[i] & 0xFF);
|
|
3483
|
+
binary.push((utf32_array[i] >> 8) & 0xFF);
|
|
3484
|
+
binary.push((utf32_array[i] >> 16) & 0xFF);
|
|
3485
|
+
binary.push((utf32_array[i] >> 24) & 0xFF);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
return binary;
|
|
3489
|
+
}
|
|
3490
|
+
return null;
|
|
3491
|
+
}
|
|
3492
|
+
|
|
3493
|
+
/**
|
|
3494
|
+
* コードポイントからUnicodeのブロック名に変換する
|
|
3495
|
+
* 変換できない場合は "-" を返す
|
|
3496
|
+
* @param {number} codepoint - コードポイント
|
|
3497
|
+
* @returns {string}
|
|
3498
|
+
*/
|
|
3499
|
+
static toBlockNameFromUnicode(codepoint) {
|
|
3500
|
+
Unicode.init();
|
|
3501
|
+
return toBlockNameFromUnicode(codepoint);
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3504
|
+
/**
|
|
3505
|
+
* コードポイントから制御文字名に変換する
|
|
3506
|
+
* 変換できない場合は null を返す
|
|
3507
|
+
* @param {number} codepoint - コードポイント
|
|
3508
|
+
* @returns {string|null}
|
|
3509
|
+
*/
|
|
3510
|
+
static toControlCharcterName(codepoint) {
|
|
3511
|
+
Unicode.init();
|
|
3512
|
+
|
|
3513
|
+
// 異体字セレクタの確認を行い、異体字セレクタ用の制御文字(FVS, VSx)を返す
|
|
3514
|
+
const info_variation_selectors_number = getVariationSelectorsnumberFromCodePoint(codepoint);
|
|
3515
|
+
if (info_variation_selectors_number !== null) {
|
|
3516
|
+
return info_variation_selectors_number;
|
|
3517
|
+
}
|
|
3518
|
+
// タグ文字の確認を行い、タグ文字用の制御文字(TAG_xx)を返す
|
|
3519
|
+
const info_tag_character = getTagCharacterFromCodePoint(codepoint);
|
|
3520
|
+
if (info_tag_character !== null) {
|
|
3521
|
+
return info_tag_character;
|
|
3522
|
+
}
|
|
3523
|
+
// その他の制御文字の確認を行う
|
|
3524
|
+
const name = control_charcter_map[codepoint];
|
|
3525
|
+
return name ? name : null;
|
|
3526
|
+
}
|
|
3527
|
+
|
|
3528
|
+
/**
|
|
3529
|
+
* コードポイントからグラフェム(見た目の1文字)を構成する文字の判定
|
|
3530
|
+
*
|
|
3531
|
+
* ※単独では新しいグラフェムを開始せず、直前のベース文字に結合・修飾される要素
|
|
3532
|
+
*
|
|
3533
|
+
* 含まれるもの:
|
|
3534
|
+
* - 結合文字 (Mn / Mc / Me ※VS除外)
|
|
3535
|
+
* - 異体字セレクタ (VS / IVS / FVS)
|
|
3536
|
+
* - スキントーン修飾子(EMOJI MODIFIER FITZPATRICK)
|
|
3537
|
+
* - タグ文字(TAG CHARACTER)
|
|
3538
|
+
* - ゼロ幅接合子
|
|
3539
|
+
*
|
|
3540
|
+
* 含まれないもの
|
|
3541
|
+
* - 国旗(Regional Indicator)※ペア規則
|
|
3542
|
+
*
|
|
3543
|
+
* @param {number} codepoint - コードポイント
|
|
3544
|
+
* @returns {boolean} 確認結果
|
|
3545
|
+
*/
|
|
3546
|
+
static isGraphemeComponentFromCodePoint(codepoint) {
|
|
3547
|
+
// prettier-ignore
|
|
3548
|
+
return (
|
|
3549
|
+
Unicode.isCombiningMarkFromCodePoint(codepoint) // 結合文字
|
|
3550
|
+
|| Unicode.isVariationSelectorFromCodePoint(codepoint) // 異体字セレクタ
|
|
3551
|
+
|| Unicode.isEmojiModifierFromCodePoint(codepoint) // スキントーン修飾子
|
|
3552
|
+
|| Unicode.isTagCharacterFromCodePoint(codepoint) // タグ文字
|
|
3553
|
+
|| codepoint === 0x200D // ZWJ (ZERO WIDTH JOINER) ゼロ幅接合子
|
|
3554
|
+
);
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
/**
|
|
3558
|
+
* コードポイントから国旗(Regional Indicator)を構成する文字の判定
|
|
3559
|
+
*
|
|
3560
|
+
* @param {number} codepoint - コードポイント
|
|
3561
|
+
* @returns {boolean} 確認結果
|
|
3562
|
+
*/
|
|
3563
|
+
static isRegionalIndicatorFromCodePoint(codepoint) {
|
|
3564
|
+
// prettier-ignore
|
|
3565
|
+
return (0x1F1E6 <= codepoint && codepoint <= 0x1F1FF);
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
/**
|
|
3569
|
+
* 2つのコードポイントが結合する場合の判定処理
|
|
3570
|
+
*
|
|
3571
|
+
* 含まれるもの:
|
|
3572
|
+
* - 国旗(Regional Indicator)
|
|
3573
|
+
*
|
|
3574
|
+
* @param {number|null} codepoint1 - 直前のコードポイント
|
|
3575
|
+
* @param {number|null} codepoint2 - 現在のコードポイント
|
|
3576
|
+
* @returns {boolean} 確認結果
|
|
3577
|
+
*/
|
|
3578
|
+
static isRegionalIndicatorContinuation(codepoint1, codepoint2) {
|
|
3579
|
+
if ((codepoint1 == null || codepoint1 === undefined) || codepoint2 == null || codepoint2 === undefined) {
|
|
3580
|
+
return false;
|
|
3581
|
+
}
|
|
3582
|
+
return Unicode.isRegionalIndicatorFromCodePoint(codepoint1)
|
|
3583
|
+
&& Unicode.isRegionalIndicatorFromCodePoint(codepoint2);
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
/**
|
|
3587
|
+
* コードポイントから「表示上の横幅が 0 の文字」の文字の判定
|
|
3588
|
+
*
|
|
3589
|
+
* 含まれるもの:
|
|
3590
|
+
* - ゼロ幅スペース, ゼロ幅非接合子, ゼロ幅接合子, 単語結合子
|
|
3591
|
+
* @param {number} codepoint - コードポイント
|
|
3592
|
+
* @returns {boolean} 確認結果
|
|
3593
|
+
*/
|
|
3594
|
+
static isZeroWidthCharacterFromCodePoint(codepoint) {
|
|
3595
|
+
// prettier-ignore
|
|
3596
|
+
return (
|
|
3597
|
+
codepoint === 0x200B // ZWSP (ZERO WIDTH SPACE) ゼロ幅スペース
|
|
3598
|
+
|| codepoint === 0x200C // ZWNJ (ZERO WIDTH NON-JOINER) ゼロ幅非接合子
|
|
3599
|
+
|| codepoint === 0x200D // ZWJ (ZERO WIDTH JOINER) ゼロ幅接合子
|
|
3600
|
+
|| codepoint === 0x2060 // WJ (WORD JOINER) 単語結合子
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
/**
|
|
3605
|
+
* コードポイントから結合文字の判定
|
|
3606
|
+
* @param {number} codepoint - コードポイント
|
|
3607
|
+
* @returns {boolean} 確認結果
|
|
3608
|
+
*/
|
|
3609
|
+
static isCombiningMarkFromCodePoint(codepoint) {
|
|
3610
|
+
// 異体字セレクタは除外
|
|
3611
|
+
if (Unicode.isVariationSelectorFromCodePoint(codepoint)) {
|
|
3612
|
+
return false;
|
|
3613
|
+
}
|
|
3614
|
+
try {
|
|
3615
|
+
return new RegExp("\\p{Mark}", "u").test(String.fromCodePoint(codepoint));
|
|
3616
|
+
// eslint-disable-next-line no-unused-vars
|
|
3617
|
+
} catch (e) {
|
|
3618
|
+
// フォールバック処理
|
|
3619
|
+
return (
|
|
3620
|
+
// Combining Diacritical Marks
|
|
3621
|
+
// prettier-ignore
|
|
3622
|
+
(0x0300 <= codepoint && codepoint <= 0x036F)
|
|
3623
|
+
// Combining Diacritical Marks Extended
|
|
3624
|
+
// prettier-ignore
|
|
3625
|
+
|| (0x1AB0 <= codepoint && codepoint <= 0x1AFF)
|
|
3626
|
+
// Combining Diacritical Marks Supplement
|
|
3627
|
+
// prettier-ignore
|
|
3628
|
+
|| (0x1DC0 <= codepoint && codepoint <= 0x1DFF)
|
|
3629
|
+
// Combining Diacritical Marks for Symbols
|
|
3630
|
+
// prettier-ignore
|
|
3631
|
+
|| (0x20D0 <= codepoint && codepoint <= 0x20FF)
|
|
3632
|
+
// 日本語に含まれる2種類の文字
|
|
3633
|
+
// COMBINING VOICED SOUND MARK
|
|
3634
|
+
// COMBINING SEMI-VOICED SOUND MARK
|
|
3635
|
+
// prettier-ignore
|
|
3636
|
+
|| (0x3099 <= codepoint && codepoint <= 0x309A)
|
|
3637
|
+
// Combining Half Marks
|
|
3638
|
+
// prettier-ignore
|
|
3639
|
+
|| (0xFE20 <= codepoint && codepoint <= 0xFE2F)
|
|
3640
|
+
);
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
/**
|
|
3645
|
+
* コードポイントから異体字セレクタの判定
|
|
3646
|
+
* @param {number} codepoint - コードポイント
|
|
3647
|
+
* @returns {boolean} 確認結果
|
|
3648
|
+
*/
|
|
3649
|
+
static isVariationSelectorFromCodePoint(codepoint) {
|
|
3650
|
+
return (
|
|
3651
|
+
// モンゴル自由字形選択子 U+180B〜U+180D (3個)
|
|
3652
|
+
// prettier-ignore
|
|
3653
|
+
(0x180B <= codepoint && codepoint <= 0x180D)
|
|
3654
|
+
// SVSで利用される異体字セレクタ U+FE00〜U+FE0F (VS1~VS16) (16個)
|
|
3655
|
+
// prettier-ignore
|
|
3656
|
+
|| (0xFE00 <= codepoint && codepoint <= 0xFE0F)
|
|
3657
|
+
// IVSで利用される異体字セレクタ U+E0100〜U+E01EF (VS17~VS256) (240個)
|
|
3658
|
+
// prettier-ignore
|
|
3659
|
+
|| (0xE0100 <= codepoint && codepoint <= 0xE01EF)
|
|
3660
|
+
);
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
/**
|
|
3664
|
+
* コードポイントからスキントーン修飾子の判定
|
|
3665
|
+
* @param {number} codepoint - コードポイント
|
|
3666
|
+
* @returns {boolean} 確認結果
|
|
3667
|
+
*/
|
|
3668
|
+
static isEmojiModifierFromCodePoint(codepoint) {
|
|
3669
|
+
return (
|
|
3670
|
+
// EMOJI MODIFIER FITZPATRICK
|
|
3671
|
+
// prettier-ignore
|
|
3672
|
+
0x1F3FB <= codepoint && codepoint <= 0x1F3FF
|
|
3673
|
+
);
|
|
3674
|
+
}
|
|
3675
|
+
|
|
3676
|
+
/**
|
|
3677
|
+
* コードポイントからタグ文字の判定
|
|
3678
|
+
* @param {number} codepoint - コードポイント
|
|
3679
|
+
* @returns {boolean} 確認結果
|
|
3680
|
+
*/
|
|
3681
|
+
static isTagCharacterFromCodePoint(codepoint) {
|
|
3682
|
+
return (
|
|
3683
|
+
// TAG CHARACTER
|
|
3684
|
+
// prettier-ignore
|
|
3685
|
+
0xE0000 <= codepoint && codepoint <= 0xE007F
|
|
3686
|
+
);
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
|
|
3690
|
+
/**
|
|
3691
|
+
* マップを初期化した否か
|
|
3692
|
+
*/
|
|
3693
|
+
Unicode.is_initmap = false;
|
|
3694
|
+
|
|
3695
|
+
/**
|
|
3696
|
+
* The script is part of Mojix for TextInputGuard.
|
|
3697
|
+
*
|
|
3698
|
+
* AUTHOR:
|
|
3699
|
+
* natade-jp (https://github.com/natade-jp)
|
|
3700
|
+
*
|
|
3701
|
+
* LICENSE:
|
|
3702
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
3703
|
+
*/
|
|
3704
|
+
|
|
3705
|
+
|
|
3706
|
+
/**
|
|
3707
|
+
* 面区点情報
|
|
3708
|
+
* @typedef {Object} MenKuTen
|
|
3709
|
+
* @property {string} [text] 面-区-点
|
|
3710
|
+
* @property {number} [men=1] 面
|
|
3711
|
+
* @property {number} ku 区
|
|
3712
|
+
* @property {number} ten 点
|
|
3713
|
+
*/
|
|
3714
|
+
|
|
3715
|
+
/**
|
|
3716
|
+
* Shift_JIS を扱うクラス
|
|
3717
|
+
* @ignore
|
|
3718
|
+
*/
|
|
3719
|
+
class SJIS {
|
|
3720
|
+
/**
|
|
3721
|
+
* 文字列を Shift_JIS の配列に変換。変換できない文字は "?" に変換される。
|
|
3722
|
+
* @param {string} text - 変換したいテキスト
|
|
3723
|
+
* @param {Record<number, number>} unicode_to_sjis - Unicode から Shift_JIS への変換マップ
|
|
3724
|
+
* @returns {number[]} Shift_JIS のデータが入った配列
|
|
3725
|
+
* @ignore
|
|
3726
|
+
*/
|
|
3727
|
+
static toSJISArray(text, unicode_to_sjis) {
|
|
3728
|
+
const map = unicode_to_sjis;
|
|
3729
|
+
const utf32 = Unicode.toUTF32Array(text);
|
|
3730
|
+
const sjis = [];
|
|
3731
|
+
const ng = "?".charCodeAt(0);
|
|
3732
|
+
for (let i = 0; i < utf32.length; i++) {
|
|
3733
|
+
const map_bin = map[utf32[i]];
|
|
3734
|
+
if (map_bin) {
|
|
3735
|
+
sjis.push(map_bin);
|
|
3736
|
+
} else {
|
|
3737
|
+
sjis.push(ng);
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
return sjis;
|
|
3741
|
+
}
|
|
3742
|
+
|
|
3743
|
+
/**
|
|
3744
|
+
* 文字列を Shift_JIS のバイナリ配列に変換。変換できない文字は "?" に変換される。
|
|
3745
|
+
* - 日本語文字は2バイトとして、配列も2つ分、使用します。
|
|
3746
|
+
* @param {string} text - 変換したいテキスト
|
|
3747
|
+
* @param {Record<number, number>} unicode_to_sjis - Unicode から Shift_JIS への変換マップ
|
|
3748
|
+
* @returns {number[]} Shift_JIS のデータが入ったバイナリ配列
|
|
3749
|
+
* @ignore
|
|
3750
|
+
*/
|
|
3751
|
+
static toSJISBinary(text, unicode_to_sjis) {
|
|
3752
|
+
const sjis = SJIS.toSJISArray(text, unicode_to_sjis);
|
|
3753
|
+
const sjisbin = [];
|
|
3754
|
+
for (let i = 0; i < sjis.length; i++) {
|
|
3755
|
+
if (sjis[i] < 0x100) {
|
|
3756
|
+
sjisbin.push(sjis[i]);
|
|
3757
|
+
} else {
|
|
3758
|
+
sjisbin.push(sjis[i] >> 8);
|
|
3759
|
+
sjisbin.push(sjis[i] & 0xFF);
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
return sjisbin;
|
|
3763
|
+
}
|
|
3764
|
+
|
|
3765
|
+
/**
|
|
3766
|
+
* SJISの配列から文字列に変換
|
|
3767
|
+
* @param {number[]} sjis - 変換したいテキスト
|
|
3768
|
+
* @param {Record<number, number|number[]>} sjis_to_unicode - Shift_JIS から Unicode への変換マップ
|
|
3769
|
+
* @returns {string} 変換後のテキスト
|
|
3770
|
+
* @ignore
|
|
3771
|
+
*/
|
|
3772
|
+
static fromSJISArray(sjis, sjis_to_unicode) {
|
|
3773
|
+
const map = sjis_to_unicode;
|
|
3774
|
+
const utf16 = [];
|
|
3775
|
+
const ng = "?".charCodeAt(0);
|
|
3776
|
+
for (let i = 0; i < sjis.length; i++) {
|
|
3777
|
+
let x = sjis[i];
|
|
3778
|
+
/**
|
|
3779
|
+
* @type {number|number[]}
|
|
3780
|
+
*/
|
|
3781
|
+
let y;
|
|
3782
|
+
if (x >= 0x100) {
|
|
3783
|
+
// すでに1つの変数にまとめられている
|
|
3784
|
+
y = map[x];
|
|
3785
|
+
} else {
|
|
3786
|
+
// 2バイト文字かのチェック
|
|
3787
|
+
// prettier-ignore
|
|
3788
|
+
if ((0x81 <= x && x <= 0x9F) || (0xE0 <= x && x <= 0xFC)) {
|
|
3789
|
+
x <<= 8;
|
|
3790
|
+
i++;
|
|
3791
|
+
x |= sjis[i];
|
|
3792
|
+
y = map[x];
|
|
3793
|
+
} else {
|
|
3794
|
+
y = map[x];
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
if (y) {
|
|
3798
|
+
// 配列なら配列を結合
|
|
3799
|
+
// ※ Unicodeの結合文字の可能性があるため
|
|
3800
|
+
if (Array.isArray(y)) {
|
|
3801
|
+
for (let j = 0; j < y.length; j++) {
|
|
3802
|
+
utf16.push(y[j]);
|
|
3803
|
+
}
|
|
3804
|
+
} else {
|
|
3805
|
+
// 値しかない場合は値を結合
|
|
3806
|
+
utf16.push(y);
|
|
3807
|
+
}
|
|
3808
|
+
} else {
|
|
3809
|
+
utf16.push(ng);
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
return Unicode.fromUTF32Array(utf16);
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
/**
|
|
3816
|
+
* 指定したコードポイントの文字から Shift_JIS 上の符号化数値に変換
|
|
3817
|
+
* @param {number} unicode_codepoint - Unicodeのコードポイント
|
|
3818
|
+
* @param {Record<number, number>} unicode_to_sjis - Unicode から Shift_JIS への変換マップ
|
|
3819
|
+
* @returns {number} 符号化数値(変換できない場合はnullとなる)
|
|
3820
|
+
* @ignore
|
|
3821
|
+
*/
|
|
3822
|
+
static toSJISCodeFromUnicode(unicode_codepoint, unicode_to_sjis) {
|
|
3823
|
+
if (!unicode_to_sjis[unicode_codepoint]) {
|
|
3824
|
+
return null;
|
|
3825
|
+
}
|
|
3826
|
+
const utf16_text = Unicode.fromUTF32Array([unicode_codepoint]);
|
|
3827
|
+
const sjis_array = SJIS.toSJISArray(utf16_text, unicode_to_sjis);
|
|
3828
|
+
return sjis_array[0];
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3831
|
+
/**
|
|
3832
|
+
* 指定した Shift_JIS のコードから区点番号に変換
|
|
3833
|
+
* @param {number} sjis_code - Shift_JIS のコードポイント
|
|
3834
|
+
* @returns {MenKuTen} 区点番号(存在しない場合(1バイトのJISコードなど)はnullを返す)
|
|
3835
|
+
*/
|
|
3836
|
+
static toKuTenFromSJISCode(sjis_code) {
|
|
3837
|
+
if (!sjis_code) {
|
|
3838
|
+
return null;
|
|
3839
|
+
}
|
|
3840
|
+
const x = sjis_code;
|
|
3841
|
+
if (x < 0x100) {
|
|
3842
|
+
return null;
|
|
3843
|
+
}
|
|
3844
|
+
// アルゴリズムは区点番号表からリバースエンジニアリング
|
|
3845
|
+
|
|
3846
|
+
let s1 = x >> 8;
|
|
3847
|
+
let s2 = x & 0xFF;
|
|
3848
|
+
|
|
3849
|
+
/** @type {number} */
|
|
3850
|
+
let ku;
|
|
3851
|
+
|
|
3852
|
+
// 区の計算方法の切り替え
|
|
3853
|
+
// 63区から、0x9F→0xE0に飛ぶ
|
|
3854
|
+
// prettier-ignore
|
|
3855
|
+
if (s1 < 0xE0) {
|
|
3856
|
+
s1 = s1 - 0x81;
|
|
3857
|
+
} else {
|
|
3858
|
+
s1 = s1 - 0xC1;
|
|
3859
|
+
}
|
|
3860
|
+
|
|
3861
|
+
// 区情報の位置判定
|
|
3862
|
+
// prettier-ignore
|
|
3863
|
+
if (s2 < 0x9F) {
|
|
3864
|
+
ku = s1 * 2 + 1;
|
|
3865
|
+
// 点情報の計算方法の切り替え
|
|
3866
|
+
// 0x7Fが欠番のため「+1」を除去
|
|
3867
|
+
// prettier-ignore
|
|
3868
|
+
if (s2 < 0x80) {
|
|
3869
|
+
// prettier-ignore
|
|
3870
|
+
s2 = s2 - 0x40 + 1;
|
|
3871
|
+
} else {
|
|
3872
|
+
// prettier-ignore
|
|
3873
|
+
s2 = s2 - 0x40;
|
|
3874
|
+
}
|
|
3875
|
+
} else {
|
|
3876
|
+
ku = s1 * 2 + 2;
|
|
3877
|
+
// prettier-ignore
|
|
3878
|
+
s2 = s2 - 0x9F + 1;
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
// 点情報の位置判定
|
|
3882
|
+
const ten = s2;
|
|
3883
|
+
|
|
3884
|
+
return {
|
|
3885
|
+
text: ku + "-" + ten,
|
|
3886
|
+
men: 1,
|
|
3887
|
+
ku: ku,
|
|
3888
|
+
ten: ten
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3892
|
+
/**
|
|
3893
|
+
* 指定した面区点番号から Shift_JIS の仕様上、正規な物か判定
|
|
3894
|
+
* @param {MenKuTen|string} menkuten - 面区点番号(面が省略された場合は、1とみなす)
|
|
3895
|
+
* @returns {boolean} 正規なデータは true, 不正なデータは false
|
|
3896
|
+
*/
|
|
3897
|
+
static isRegularMenKuten(menkuten) {
|
|
3898
|
+
let m, k, t;
|
|
3899
|
+
|
|
3900
|
+
// 引数のテスト
|
|
3901
|
+
if (menkuten instanceof Object) {
|
|
3902
|
+
m = menkuten.men ? menkuten.men : 1;
|
|
3903
|
+
k = menkuten.ku;
|
|
3904
|
+
t = menkuten.ten;
|
|
3905
|
+
} else if (typeof menkuten === "string") {
|
|
3906
|
+
const strmkt = menkuten.split("-");
|
|
3907
|
+
if (strmkt.length === 3) {
|
|
3908
|
+
m = parseInt(strmkt[0], 10);
|
|
3909
|
+
k = parseInt(strmkt[1], 10);
|
|
3910
|
+
t = parseInt(strmkt[2], 10);
|
|
3911
|
+
} else if (strmkt.length === 2) {
|
|
3912
|
+
m = 1;
|
|
3913
|
+
k = parseInt(strmkt[0], 10);
|
|
3914
|
+
t = parseInt(strmkt[1], 10);
|
|
3915
|
+
} else {
|
|
3916
|
+
return false;
|
|
3917
|
+
}
|
|
3918
|
+
} else {
|
|
3919
|
+
return false;
|
|
3920
|
+
}
|
|
3921
|
+
|
|
3922
|
+
/**
|
|
3923
|
+
* @type {Record<number, number>}
|
|
3924
|
+
*/
|
|
3925
|
+
const kmap = { 1: 1, 3: 1, 4: 1, 5: 1, 8: 1, 12: 1, 13: 1, 14: 1, 15: 1 };
|
|
3926
|
+
if (m === 1) {
|
|
3927
|
+
// 1面は1-94区まで存在
|
|
3928
|
+
if (!(1 <= k && k <= 94)) {
|
|
3929
|
+
return false;
|
|
3930
|
+
}
|
|
3931
|
+
} else if (m === 2) {
|
|
3932
|
+
// 2面は、1,3,4,5,8,12,13,14,15,78-94区まで存在
|
|
3933
|
+
if (!(kmap[k] || (78 <= k && k <= 94))) {
|
|
3934
|
+
return false;
|
|
3935
|
+
}
|
|
3936
|
+
} else {
|
|
3937
|
+
// 面が不正
|
|
3938
|
+
return false;
|
|
3939
|
+
}
|
|
3940
|
+
// 点は1-94点まで存在
|
|
3941
|
+
if (!(1 <= t && t <= 94)) {
|
|
3942
|
+
return false;
|
|
3943
|
+
}
|
|
3944
|
+
return true;
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
/**
|
|
3949
|
+
* The script is part of Mojix for TextInputGuard.
|
|
3950
|
+
*
|
|
3951
|
+
* AUTHOR:
|
|
3952
|
+
* natade-jp (https://github.com/natade-jp)
|
|
3953
|
+
*
|
|
3954
|
+
* LICENSE:
|
|
3955
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
3956
|
+
*/
|
|
3957
|
+
|
|
3958
|
+
|
|
3959
|
+
/**
|
|
3960
|
+
* CP932, Windows-31J の変換マップ作成用クラス
|
|
3961
|
+
* @ignore
|
|
3962
|
+
*/
|
|
3963
|
+
class CP932MAP {
|
|
3964
|
+
/**
|
|
3965
|
+
* 変換マップを初期化
|
|
3966
|
+
*/
|
|
3967
|
+
static init() {
|
|
3968
|
+
if (CP932MAP.is_initmap) {
|
|
3969
|
+
return;
|
|
3970
|
+
}
|
|
3971
|
+
CP932MAP.is_initmap = true;
|
|
3972
|
+
|
|
3973
|
+
/**
|
|
3974
|
+
* @returns {Record<number, number>}
|
|
3975
|
+
*/
|
|
3976
|
+
const getCp932ToUnicodeMap = function () {
|
|
3977
|
+
/* eslint-disable max-len */
|
|
3978
|
+
/* eslint-disable object-property-newline */
|
|
3979
|
+
/**
|
|
3980
|
+
* 1バイトの変換マップ
|
|
3981
|
+
*
|
|
3982
|
+
*
|
|
3983
|
+
* 参考:WideCharToMultiByte
|
|
3984
|
+
* メモ:今回は使っていないが、以下の文献も参考になるかもしれません。
|
|
3985
|
+
* ftp://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/JIS0208.TXT
|
|
3986
|
+
* @type {Record<number, number>}
|
|
3987
|
+
*/
|
|
3988
|
+
// prettier-ignore
|
|
3989
|
+
const cp932_to_unicode_map = {
|
|
3990
|
+
0x01: 0x01, 0x02: 0x02, 0x03: 0x03, 0x04: 0x04, 0x05: 0x05, 0x06: 0x06, 0x07: 0x07, 0x08: 0x08,
|
|
3991
|
+
0x09: 0x09, 0x0A: 0x0A, 0x0B: 0x0B, 0x0C: 0x0C, 0x0D: 0x0D, 0x0E: 0x0E, 0x0F: 0x0F, 0x10: 0x10,
|
|
3992
|
+
0x11: 0x11, 0x12: 0x12, 0x13: 0x13, 0x14: 0x14, 0x15: 0x15, 0x16: 0x16, 0x17: 0x17, 0x18: 0x18,
|
|
3993
|
+
0x19: 0x19, 0x1A: 0x1A, 0x1B: 0x1B, 0x1C: 0x1C, 0x1D: 0x1D, 0x1E: 0x1E, 0x1F: 0x1F, 0x20: 0x20,
|
|
3994
|
+
0x21: 0x21, 0x22: 0x22, 0x23: 0x23, 0x24: 0x24, 0x25: 0x25, 0x26: 0x26, 0x27: 0x27, 0x28: 0x28,
|
|
3995
|
+
0x29: 0x29, 0x2A: 0x2A, 0x2B: 0x2B, 0x2C: 0x2C, 0x2D: 0x2D, 0x2E: 0x2E, 0x2F: 0x2F, 0x30: 0x30,
|
|
3996
|
+
0x31: 0x31, 0x32: 0x32, 0x33: 0x33, 0x34: 0x34, 0x35: 0x35, 0x36: 0x36, 0x37: 0x37, 0x38: 0x38,
|
|
3997
|
+
0x39: 0x39, 0x3A: 0x3A, 0x3B: 0x3B, 0x3C: 0x3C, 0x3D: 0x3D, 0x3E: 0x3E, 0x3F: 0x3F, 0x40: 0x40,
|
|
3998
|
+
0x41: 0x41, 0x42: 0x42, 0x43: 0x43, 0x44: 0x44, 0x45: 0x45, 0x46: 0x46, 0x47: 0x47, 0x48: 0x48,
|
|
3999
|
+
0x49: 0x49, 0x4A: 0x4A, 0x4B: 0x4B, 0x4C: 0x4C, 0x4D: 0x4D, 0x4E: 0x4E, 0x4F: 0x4F, 0x50: 0x50,
|
|
4000
|
+
0x51: 0x51, 0x52: 0x52, 0x53: 0x53, 0x54: 0x54, 0x55: 0x55, 0x56: 0x56, 0x57: 0x57, 0x58: 0x58,
|
|
4001
|
+
0x59: 0x59, 0x5A: 0x5A, 0x5B: 0x5B, 0x5C: 0x5C, 0x5D: 0x5D, 0x5E: 0x5E, 0x5F: 0x5F, 0x60: 0x60,
|
|
4002
|
+
0x61: 0x61, 0x62: 0x62, 0x63: 0x63, 0x64: 0x64, 0x65: 0x65, 0x66: 0x66, 0x67: 0x67, 0x68: 0x68,
|
|
4003
|
+
0x69: 0x69, 0x6A: 0x6A, 0x6B: 0x6B, 0x6C: 0x6C, 0x6D: 0x6D, 0x6E: 0x6E, 0x6F: 0x6F, 0x70: 0x70,
|
|
4004
|
+
0x71: 0x71, 0x72: 0x72, 0x73: 0x73, 0x74: 0x74, 0x75: 0x75, 0x76: 0x76, 0x77: 0x77, 0x78: 0x78,
|
|
4005
|
+
0x79: 0x79, 0x7A: 0x7A, 0x7B: 0x7B, 0x7C: 0x7C, 0x7D: 0x7D, 0x7E: 0x7E, 0x7F: 0x7F, 0x80: 0x80,
|
|
4006
|
+
0xA0: 0xF8F0, 0xA1: 0xFF61, 0xA2: 0xFF62, 0xA3: 0xFF63, 0xA4: 0xFF64, 0xA5: 0xFF65, 0xA6: 0xFF66, 0xA7: 0xFF67,
|
|
4007
|
+
0xA8: 0xFF68, 0xA9: 0xFF69, 0xAA: 0xFF6A, 0xAB: 0xFF6B, 0xAC: 0xFF6C, 0xAD: 0xFF6D, 0xAE: 0xFF6E, 0xAF: 0xFF6F,
|
|
4008
|
+
0xB0: 0xFF70, 0xB1: 0xFF71, 0xB2: 0xFF72, 0xB3: 0xFF73, 0xB4: 0xFF74, 0xB5: 0xFF75, 0xB6: 0xFF76, 0xB7: 0xFF77,
|
|
4009
|
+
0xB8: 0xFF78, 0xB9: 0xFF79, 0xBA: 0xFF7A, 0xBB: 0xFF7B, 0xBC: 0xFF7C, 0xBD: 0xFF7D, 0xBE: 0xFF7E, 0xBF: 0xFF7F,
|
|
4010
|
+
0xC0: 0xFF80, 0xC1: 0xFF81, 0xC2: 0xFF82, 0xC3: 0xFF83, 0xC4: 0xFF84, 0xC5: 0xFF85, 0xC6: 0xFF86, 0xC7: 0xFF87,
|
|
4011
|
+
0xC8: 0xFF88, 0xC9: 0xFF89, 0xCA: 0xFF8A, 0xCB: 0xFF8B, 0xCC: 0xFF8C, 0xCD: 0xFF8D, 0xCE: 0xFF8E, 0xCF: 0xFF8F,
|
|
4012
|
+
0xD0: 0xFF90, 0xD1: 0xFF91, 0xD2: 0xFF92, 0xD3: 0xFF93, 0xD4: 0xFF94, 0xD5: 0xFF95, 0xD6: 0xFF96, 0xD7: 0xFF97,
|
|
4013
|
+
0xD8: 0xFF98, 0xD9: 0xFF99, 0xDA: 0xFF9A, 0xDB: 0xFF9B, 0xDC: 0xFF9C, 0xDD: 0xFF9D, 0xDE: 0xFF9E, 0xDF: 0xFF9F,
|
|
4014
|
+
0xFD: 0xF8F1, 0xFE: 0xF8F2, 0xFF: 0xF8F3
|
|
4015
|
+
};
|
|
4016
|
+
/* eslint-enable object-property-newline */
|
|
4017
|
+
/* eslint-enable max-len */
|
|
4018
|
+
|
|
4019
|
+
/**
|
|
4020
|
+
* 2バイト文字(0x8140-0xffff)の変換マップ作成用の文字列
|
|
4021
|
+
* @type {string}
|
|
4022
|
+
*/
|
|
4023
|
+
// prettier-ignore
|
|
4024
|
+
const map = [
|
|
4025
|
+
" 、。,.・:;?!゛゜´`¨^ ̄_ヽヾゝゞ〃仝々〆〇ー―‐/\~∥|…‥‘’“”()〔〕[]{}〈〉《》「」『』【】+-±×1÷=≠<>≦≧∞∴♂♀°′″℃¥$¢£%#&*@§☆★○●◎◇◆□■△▲▽▼※〒→←↑↓〓11∈∋⊆⊇⊂⊃∪∩8∧∨¬⇒⇔∀∃11∠⊥⌒∂∇≡≒≪≫√∽∝∵∫∬7ʼn♯♭♪†‡¶4◯82",
|
|
4026
|
+
"01234567897ABCDEFGHIJKLMNOPQRSTUVWXYZ7abcdefghijklmnopqrstuvwxyz4ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをん78",
|
|
4027
|
+
"ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミ1ムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶ8ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ8αβγδεζηθικλμνξοπρστυφχψω105АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ15абвгдеёжзийклмн1опрстуфхцчшщъыьэюя13─│┌┐┘└├┬┤┴┼━┃┏┓┛┗┣┳┫┻╋┠┯┨┷┿┝┰┥┸╂641",
|
|
4028
|
+
"①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ1㍉㌔㌢㍍㌘㌧㌃㌶㍑㍗㌍㌦㌣㌫㍊㌻㎜㎝㎞㎎㎏㏄㎡8㍻1〝〟№㏍℡㊤㊥㊦㊧㊨㈱㈲㈹㍾㍽㍼≒≡∫∮∑√⊥∠∟⊿∵∩∪258",
|
|
4029
|
+
"亜唖娃阿哀愛挨姶逢葵茜穐悪握渥旭葦芦鯵梓圧斡扱宛姐虻飴絢綾鮎或粟袷安庵按暗案闇鞍杏以伊位依偉囲夷委威尉惟意慰易椅為畏異移維緯胃萎衣謂違遺医井亥域育郁磯一壱溢逸稲茨芋鰯允印咽員因姻引飲淫胤蔭67",
|
|
4030
|
+
"院陰隠韻吋右宇烏羽迂雨卯鵜窺丑碓臼渦嘘唄欝蔚鰻姥厩浦瓜閏噂云運雲荏餌叡営嬰影映曳栄永泳洩瑛盈穎頴英衛詠鋭液疫益駅悦謁越閲榎厭円1園堰奄宴延怨掩援沿演炎焔煙燕猿縁艶苑薗遠鉛鴛塩於汚甥凹央奥往応押旺横欧殴王翁襖鴬鴎黄岡沖荻億屋憶臆桶牡乙俺卸恩温穏音下化仮何伽価佳加可嘉夏嫁家寡科暇果架歌河火珂禍禾稼箇花苛茄荷華菓蝦課嘩貨迦過霞蚊俄峨我牙画臥芽蛾賀雅餓駕介会解回塊壊廻快怪悔恢懐戒拐改67",
|
|
4031
|
+
"魁晦械海灰界皆絵芥蟹開階貝凱劾外咳害崖慨概涯碍蓋街該鎧骸浬馨蛙垣柿蛎鈎劃嚇各廓拡撹格核殻獲確穫覚角赫較郭閣隔革学岳楽額顎掛笠樫1橿梶鰍潟割喝恰括活渇滑葛褐轄且鰹叶椛樺鞄株兜竃蒲釜鎌噛鴨栢茅萱粥刈苅瓦乾侃冠寒刊勘勧巻喚堪姦完官寛干幹患感慣憾換敢柑桓棺款歓汗漢澗潅環甘監看竿管簡緩缶翰肝艦莞観諌貫還鑑間閑関陥韓館舘丸含岸巌玩癌眼岩翫贋雁頑顔願企伎危喜器基奇嬉寄岐希幾忌揮机旗既期棋棄67",
|
|
4032
|
+
"機帰毅気汽畿祈季稀紀徽規記貴起軌輝飢騎鬼亀偽儀妓宜戯技擬欺犠疑祇義蟻誼議掬菊鞠吉吃喫桔橘詰砧杵黍却客脚虐逆丘久仇休及吸宮弓急救1朽求汲泣灸球究窮笈級糾給旧牛去居巨拒拠挙渠虚許距鋸漁禦魚亨享京供侠僑兇競共凶協匡卿叫喬境峡強彊怯恐恭挟教橋況狂狭矯胸脅興蕎郷鏡響饗驚仰凝尭暁業局曲極玉桐粁僅勤均巾錦斤欣欽琴禁禽筋緊芹菌衿襟謹近金吟銀九倶句区狗玖矩苦躯駆駈駒具愚虞喰空偶寓遇隅串櫛釧屑屈67",
|
|
4033
|
+
"掘窟沓靴轡窪熊隈粂栗繰桑鍬勲君薫訓群軍郡卦袈祁係傾刑兄啓圭珪型契形径恵慶慧憩掲携敬景桂渓畦稽系経継繋罫茎荊蛍計詣警軽頚鶏芸迎鯨1劇戟撃激隙桁傑欠決潔穴結血訣月件倹倦健兼券剣喧圏堅嫌建憲懸拳捲検権牽犬献研硯絹県肩見謙賢軒遣鍵険顕験鹸元原厳幻弦減源玄現絃舷言諺限乎個古呼固姑孤己庫弧戸故枯湖狐糊袴股胡菰虎誇跨鈷雇顧鼓五互伍午呉吾娯後御悟梧檎瑚碁語誤護醐乞鯉交佼侯候倖光公功効勾厚口向67",
|
|
4034
|
+
"后喉坑垢好孔孝宏工巧巷幸広庚康弘恒慌抗拘控攻昂晃更杭校梗構江洪浩港溝甲皇硬稿糠紅紘絞綱耕考肯肱腔膏航荒行衡講貢購郊酵鉱砿鋼閤降1項香高鴻剛劫号合壕拷濠豪轟麹克刻告国穀酷鵠黒獄漉腰甑忽惚骨狛込此頃今困坤墾婚恨懇昏昆根梱混痕紺艮魂些佐叉唆嵯左差査沙瑳砂詐鎖裟坐座挫債催再最哉塞妻宰彩才採栽歳済災采犀砕砦祭斎細菜裁載際剤在材罪財冴坂阪堺榊肴咲崎埼碕鷺作削咋搾昨朔柵窄策索錯桜鮭笹匙冊刷67",
|
|
4035
|
+
"察拶撮擦札殺薩雑皐鯖捌錆鮫皿晒三傘参山惨撒散桟燦珊産算纂蚕讃賛酸餐斬暫残仕仔伺使刺司史嗣四士始姉姿子屍市師志思指支孜斯施旨枝止1死氏獅祉私糸紙紫肢脂至視詞詩試誌諮資賜雌飼歯事似侍児字寺慈持時次滋治爾璽痔磁示而耳自蒔辞汐鹿式識鴫竺軸宍雫七叱執失嫉室悉湿漆疾質実蔀篠偲柴芝屡蕊縞舎写射捨赦斜煮社紗者謝車遮蛇邪借勺尺杓灼爵酌釈錫若寂弱惹主取守手朱殊狩珠種腫趣酒首儒受呪寿授樹綬需囚収周67",
|
|
4036
|
+
"宗就州修愁拾洲秀秋終繍習臭舟蒐衆襲讐蹴輯週酋酬集醜什住充十従戎柔汁渋獣縦重銃叔夙宿淑祝縮粛塾熟出術述俊峻春瞬竣舜駿准循旬楯殉淳1準潤盾純巡遵醇順処初所暑曙渚庶緒署書薯藷諸助叙女序徐恕鋤除傷償勝匠升召哨商唱嘗奨妾娼宵将小少尚庄床廠彰承抄招掌捷昇昌昭晶松梢樟樵沼消渉湘焼焦照症省硝礁祥称章笑粧紹肖菖蒋蕉衝裳訟証詔詳象賞醤鉦鍾鐘障鞘上丈丞乗冗剰城場壌嬢常情擾条杖浄状畳穣蒸譲醸錠嘱埴飾67",
|
|
4037
|
+
"拭植殖燭織職色触食蝕辱尻伸信侵唇娠寝審心慎振新晋森榛浸深申疹真神秦紳臣芯薪親診身辛進針震人仁刃塵壬尋甚尽腎訊迅陣靭笥諏須酢図厨1逗吹垂帥推水炊睡粋翠衰遂酔錐錘随瑞髄崇嵩数枢趨雛据杉椙菅頗雀裾澄摺寸世瀬畝是凄制勢姓征性成政整星晴棲栖正清牲生盛精聖声製西誠誓請逝醒青静斉税脆隻席惜戚斥昔析石積籍績脊責赤跡蹟碩切拙接摂折設窃節説雪絶舌蝉仙先千占宣専尖川戦扇撰栓栴泉浅洗染潜煎煽旋穿箭線67",
|
|
4038
|
+
"繊羨腺舛船薦詮賎践選遷銭銑閃鮮前善漸然全禅繕膳糎噌塑岨措曾曽楚狙疏疎礎祖租粗素組蘇訴阻遡鼠僧創双叢倉喪壮奏爽宋層匝惣想捜掃挿掻1操早曹巣槍槽漕燥争痩相窓糟総綜聡草荘葬蒼藻装走送遭鎗霜騒像増憎臓蔵贈造促側則即息捉束測足速俗属賊族続卒袖其揃存孫尊損村遜他多太汰詑唾堕妥惰打柁舵楕陀駄騨体堆対耐岱帯待怠態戴替泰滞胎腿苔袋貸退逮隊黛鯛代台大第醍題鷹滝瀧卓啄宅托択拓沢濯琢託鐸濁諾茸凧蛸只67",
|
|
4039
|
+
"叩但達辰奪脱巽竪辿棚谷狸鱈樽誰丹単嘆坦担探旦歎淡湛炭短端箪綻耽胆蛋誕鍛団壇弾断暖檀段男談値知地弛恥智池痴稚置致蜘遅馳築畜竹筑蓄1逐秩窒茶嫡着中仲宙忠抽昼柱注虫衷註酎鋳駐樗瀦猪苧著貯丁兆凋喋寵帖帳庁弔張彫徴懲挑暢朝潮牒町眺聴脹腸蝶調諜超跳銚長頂鳥勅捗直朕沈珍賃鎮陳津墜椎槌追鎚痛通塚栂掴槻佃漬柘辻蔦綴鍔椿潰坪壷嬬紬爪吊釣鶴亭低停偵剃貞呈堤定帝底庭廷弟悌抵挺提梯汀碇禎程締艇訂諦蹄逓67",
|
|
4040
|
+
"邸鄭釘鼎泥摘擢敵滴的笛適鏑溺哲徹撤轍迭鉄典填天展店添纏甜貼転顛点伝殿澱田電兎吐堵塗妬屠徒斗杜渡登菟賭途都鍍砥砺努度土奴怒倒党冬1凍刀唐塔塘套宕島嶋悼投搭東桃梼棟盗淘湯涛灯燈当痘祷等答筒糖統到董蕩藤討謄豆踏逃透鐙陶頭騰闘働動同堂導憧撞洞瞳童胴萄道銅峠鴇匿得徳涜特督禿篤毒独読栃橡凸突椴届鳶苫寅酉瀞噸屯惇敦沌豚遁頓呑曇鈍奈那内乍凪薙謎灘捺鍋楢馴縄畷南楠軟難汝二尼弐迩匂賑肉虹廿日乳入67",
|
|
4041
|
+
"如尿韮任妊忍認濡禰祢寧葱猫熱年念捻撚燃粘乃廼之埜嚢悩濃納能脳膿農覗蚤巴把播覇杷波派琶破婆罵芭馬俳廃拝排敗杯盃牌背肺輩配倍培媒梅1楳煤狽買売賠陪這蝿秤矧萩伯剥博拍柏泊白箔粕舶薄迫曝漠爆縛莫駁麦函箱硲箸肇筈櫨幡肌畑畠八鉢溌発醗髪伐罰抜筏閥鳩噺塙蛤隼伴判半反叛帆搬斑板氾汎版犯班畔繁般藩販範釆煩頒飯挽晩番盤磐蕃蛮匪卑否妃庇彼悲扉批披斐比泌疲皮碑秘緋罷肥被誹費避非飛樋簸備尾微枇毘琵眉美67",
|
|
4042
|
+
"鼻柊稗匹疋髭彦膝菱肘弼必畢筆逼桧姫媛紐百謬俵彪標氷漂瓢票表評豹廟描病秒苗錨鋲蒜蛭鰭品彬斌浜瀕貧賓頻敏瓶不付埠夫婦富冨布府怖扶敷1斧普浮父符腐膚芙譜負賦赴阜附侮撫武舞葡蕪部封楓風葺蕗伏副復幅服福腹複覆淵弗払沸仏物鮒分吻噴墳憤扮焚奮粉糞紛雰文聞丙併兵塀幣平弊柄並蔽閉陛米頁僻壁癖碧別瞥蔑箆偏変片篇編辺返遍便勉娩弁鞭保舗鋪圃捕歩甫補輔穂募墓慕戊暮母簿菩倣俸包呆報奉宝峰峯崩庖抱捧放方朋67",
|
|
4043
|
+
"法泡烹砲縫胞芳萌蓬蜂褒訪豊邦鋒飽鳳鵬乏亡傍剖坊妨帽忘忙房暴望某棒冒紡肪膨謀貌貿鉾防吠頬北僕卜墨撲朴牧睦穆釦勃没殆堀幌奔本翻凡盆1摩磨魔麻埋妹昧枚毎哩槙幕膜枕鮪柾鱒桝亦俣又抹末沫迄侭繭麿万慢満漫蔓味未魅巳箕岬密蜜湊蓑稔脈妙粍民眠務夢無牟矛霧鵡椋婿娘冥名命明盟迷銘鳴姪牝滅免棉綿緬面麺摸模茂妄孟毛猛盲網耗蒙儲木黙目杢勿餅尤戻籾貰問悶紋門匁也冶夜爺耶野弥矢厄役約薬訳躍靖柳薮鑓愉愈油癒67",
|
|
4044
|
+
"諭輸唯佑優勇友宥幽悠憂揖有柚湧涌猶猷由祐裕誘遊邑郵雄融夕予余与誉輿預傭幼妖容庸揚揺擁曜楊様洋溶熔用窯羊耀葉蓉要謡踊遥陽養慾抑欲1沃浴翌翼淀羅螺裸来莱頼雷洛絡落酪乱卵嵐欄濫藍蘭覧利吏履李梨理璃痢裏裡里離陸律率立葎掠略劉流溜琉留硫粒隆竜龍侶慮旅虜了亮僚両凌寮料梁涼猟療瞭稜糧良諒遼量陵領力緑倫厘林淋燐琳臨輪隣鱗麟瑠塁涙累類令伶例冷励嶺怜玲礼苓鈴隷零霊麗齢暦歴列劣烈裂廉恋憐漣煉簾練聯67",
|
|
4045
|
+
"蓮連錬呂魯櫓炉賂路露労婁廊弄朗楼榔浪漏牢狼篭老聾蝋郎六麓禄肋録論倭和話歪賄脇惑枠鷲亙亘鰐詫藁蕨椀湾碗腕44弌丐丕个丱丶丼丿乂乖乘亂亅豫亊舒弍于亞亟亠亢亰亳亶从仍仄仆仂仗仞仭仟价伉佚估佛佝佗佇佶侈侏侘佻佩佰侑佯來侖儘俔俟俎俘俛俑俚俐俤俥倚倨倔倪倥倅伜俶倡倩倬俾俯們倆偃假會偕偐偈做偖偬偸傀傚傅傴傲67",
|
|
4046
|
+
"僉僊傳僂僖僞僥僭僣僮價僵儉儁儂儖儕儔儚儡儺儷儼儻儿兀兒兌兔兢竸兩兪兮冀冂囘册冉冏冑冓冕冖冤冦冢冩冪冫决冱冲冰况冽凅凉凛几處凩凭1凰凵凾刄刋刔刎刧刪刮刳刹剏剄剋剌剞剔剪剴剩剳剿剽劍劔劒剱劈劑辨辧劬劭劼劵勁勍勗勞勣勦飭勠勳勵勸勹匆匈甸匍匐匏匕匚匣匯匱匳匸區卆卅丗卉卍凖卞卩卮夘卻卷厂厖厠厦厥厮厰厶參簒雙叟曼燮叮叨叭叺吁吽呀听吭吼吮吶吩吝呎咏呵咎呟呱呷呰咒呻咀呶咄咐咆哇咢咸咥咬哄哈咨67",
|
|
4047
|
+
"咫哂咤咾咼哘哥哦唏唔哽哮哭哺哢唹啀啣啌售啜啅啖啗唸唳啝喙喀咯喊喟啻啾喘喞單啼喃喩喇喨嗚嗅嗟嗄嗜嗤嗔嘔嗷嘖嗾嗽嘛嗹噎噐營嘴嘶嘲嘸1噫噤嘯噬噪嚆嚀嚊嚠嚔嚏嚥嚮嚶嚴囂嚼囁囃囀囈囎囑囓囗囮囹圀囿圄圉圈國圍圓團圖嗇圜圦圷圸坎圻址坏坩埀垈坡坿垉垓垠垳垤垪垰埃埆埔埒埓堊埖埣堋堙堝塲堡塢塋塰毀塒堽塹墅墹墟墫墺壞墻墸墮壅壓壑壗壙壘壥壜壤壟壯壺壹壻壼壽夂夊夐夛梦夥夬夭夲夸夾竒奕奐奎奚奘奢奠奧奬奩67",
|
|
4048
|
+
"奸妁妝佞侫妣妲姆姨姜妍姙姚娥娟娑娜娉娚婀婬婉娵娶婢婪媚媼媾嫋嫂媽嫣嫗嫦嫩嫖嫺嫻嬌嬋嬖嬲嫐嬪嬶嬾孃孅孀孑孕孚孛孥孩孰孳孵學斈孺宀1它宦宸寃寇寉寔寐寤實寢寞寥寫寰寶寳尅將專對尓尠尢尨尸尹屁屆屎屓屐屏孱屬屮乢屶屹岌岑岔妛岫岻岶岼岷峅岾峇峙峩峽峺峭嶌峪崋崕崗嵜崟崛崑崔崢崚崙崘嵌嵒嵎嵋嵬嵳嵶嶇嶄嶂嶢嶝嶬嶮嶽嶐嶷嶼巉巍巓巒巖巛巫已巵帋帚帙帑帛帶帷幄幃幀幎幗幔幟幢幤幇幵并幺麼广庠廁廂廈廐廏67",
|
|
4049
|
+
"廖廣廝廚廛廢廡廨廩廬廱廳廰廴廸廾弃弉彝彜弋弑弖弩弭弸彁彈彌彎弯彑彖彗彙彡彭彳彷徃徂彿徊很徑徇從徙徘徠徨徭徼忖忻忤忸忱忝悳忿怡恠1怙怐怩怎怱怛怕怫怦怏怺恚恁恪恷恟恊恆恍恣恃恤恂恬恫恙悁悍惧悃悚悄悛悖悗悒悧悋惡悸惠惓悴忰悽惆悵惘慍愕愆惶惷愀惴惺愃愡惻惱愍愎慇愾愨愧慊愿愼愬愴愽慂慄慳慷慘慙慚慫慴慯慥慱慟慝慓慵憙憖憇憬憔憚憊憑憫憮懌懊應懷懈懃懆憺懋罹懍懦懣懶懺懴懿懽懼懾戀戈戉戍戌戔戛67",
|
|
4050
|
+
"戞戡截戮戰戲戳扁扎扞扣扛扠扨扼抂抉找抒抓抖拔抃抔拗拑抻拏拿拆擔拈拜拌拊拂拇抛拉挌拮拱挧挂挈拯拵捐挾捍搜捏掖掎掀掫捶掣掏掉掟掵捫1捩掾揩揀揆揣揉插揶揄搖搴搆搓搦搶攝搗搨搏摧摯摶摎攪撕撓撥撩撈撼據擒擅擇撻擘擂擱擧舉擠擡抬擣擯攬擶擴擲擺攀擽攘攜攅攤攣攫攴攵攷收攸畋效敖敕敍敘敞敝敲數斂斃變斛斟斫斷旃旆旁旄旌旒旛旙无旡旱杲昊昃旻杳昵昶昴昜晏晄晉晁晞晝晤晧晨晟晢晰暃暈暎暉暄暘暝曁暹曉暾暼67",
|
|
4051
|
+
"曄暸曖曚曠昿曦曩曰曵曷朏朖朞朦朧霸朮朿朶杁朸朷杆杞杠杙杣杤枉杰枩杼杪枌枋枦枡枅枷柯枴柬枳柩枸柤柞柝柢柮枹柎柆柧檜栞框栩桀桍栲桎1梳栫桙档桷桿梟梏梭梔條梛梃檮梹桴梵梠梺椏梍桾椁棊椈棘椢椦棡椌棍棔棧棕椶椒椄棗棣椥棹棠棯椨椪椚椣椡棆楹楷楜楸楫楔楾楮椹楴椽楙椰楡楞楝榁楪榲榮槐榿槁槓榾槎寨槊槝榻槃榧樮榑榠榜榕榴槞槨樂樛槿權槹槲槧樅榱樞槭樔槫樊樒櫁樣樓橄樌橲樶橸橇橢橙橦橈樸樢檐檍檠檄檢檣67",
|
|
4052
|
+
"檗蘗檻櫃櫂檸檳檬櫞櫑櫟檪櫚櫪櫻欅蘖櫺欒欖鬱欟欸欷盜欹飮歇歃歉歐歙歔歛歟歡歸歹歿殀殄殃殍殘殕殞殤殪殫殯殲殱殳殷殼毆毋毓毟毬毫毳毯1麾氈氓气氛氤氣汞汕汢汪沂沍沚沁沛汾汨汳沒沐泄泱泓沽泗泅泝沮沱沾沺泛泯泙泪洟衍洶洫洽洸洙洵洳洒洌浣涓浤浚浹浙涎涕濤涅淹渕渊涵淇淦涸淆淬淞淌淨淒淅淺淙淤淕淪淮渭湮渮渙湲湟渾渣湫渫湶湍渟湃渺湎渤滿渝游溂溪溘滉溷滓溽溯滄溲滔滕溏溥滂溟潁漑灌滬滸滾漿滲漱滯漲滌16451",
|
|
4053
|
+
"漾漓滷澆潺潸澁澀潯潛濳潭澂潼潘澎澑濂潦澳澣澡澤澹濆澪濟濕濬濔濘濱濮濛瀉瀋濺瀑瀁瀏濾瀛瀚潴瀝瀘瀟瀰瀾瀲灑灣炙炒炯烱炬炸炳炮烟烋烝1烙焉烽焜焙煥煕熈煦煢煌煖煬熏燻熄熕熨熬燗熹熾燒燉燔燎燠燬燧燵燼燹燿爍爐爛爨爭爬爰爲爻爼爿牀牆牋牘牴牾犂犁犇犒犖犢犧犹犲狃狆狄狎狒狢狠狡狹狷倏猗猊猜猖猝猴猯猩猥猾獎獏默獗獪獨獰獸獵獻獺珈玳珎玻珀珥珮珞璢琅瑯琥珸琲琺瑕琿瑟瑙瑁瑜瑩瑰瑣瑪瑶瑾璋璞璧瓊瓏瓔珱67",
|
|
4054
|
+
"瓠瓣瓧瓩瓮瓲瓰瓱瓸瓷甄甃甅甌甎甍甕甓甞甦甬甼畄畍畊畉畛畆畚畩畤畧畫畭畸當疆疇畴疊疉疂疔疚疝疥疣痂疳痃疵疽疸疼疱痍痊痒痙痣痞痾痿1痼瘁痰痺痲痳瘋瘍瘉瘟瘧瘠瘡瘢瘤瘴瘰瘻癇癈癆癜癘癡癢癨癩癪癧癬癰癲癶癸發皀皃皈皋皎皖皓皙皚皰皴皸皹皺盂盍盖盒盞盡盥盧盪蘯盻眈眇眄眩眤眞眥眦眛眷眸睇睚睨睫睛睥睿睾睹瞎瞋瞑瞠瞞瞰瞶瞹瞿瞼瞽瞻矇矍矗矚矜矣矮矼砌砒礦砠礪硅碎硴碆硼碚碌碣碵碪碯磑磆磋磔碾碼磅磊磬67",
|
|
4055
|
+
"磧磚磽磴礇礒礑礙礬礫祀祠祗祟祚祕祓祺祿禊禝禧齋禪禮禳禹禺秉秕秧秬秡秣稈稍稘稙稠稟禀稱稻稾稷穃穗穉穡穢穩龝穰穹穽窈窗窕窘窖窩竈窰1窶竅竄窿邃竇竊竍竏竕竓站竚竝竡竢竦竭竰笂笏笊笆笳笘笙笞笵笨笶筐筺笄筍笋筌筅筵筥筴筧筰筱筬筮箝箘箟箍箜箚箋箒箏筝箙篋篁篌篏箴篆篝篩簑簔篦篥籠簀簇簓篳篷簗簍篶簣簧簪簟簷簫簽籌籃籔籏籀籐籘籟籤籖籥籬籵粃粐粤粭粢粫粡粨粳粲粱粮粹粽糀糅糂糘糒糜糢鬻糯糲糴糶糺紆67",
|
|
4056
|
+
"紂紜紕紊絅絋紮紲紿紵絆絳絖絎絲絨絮絏絣經綉絛綏絽綛綺綮綣綵緇綽綫總綢綯緜綸綟綰緘緝緤緞緻緲緡縅縊縣縡縒縱縟縉縋縢繆繦縻縵縹繃縷1縲縺繧繝繖繞繙繚繹繪繩繼繻纃緕繽辮繿纈纉續纒纐纓纔纖纎纛纜缸缺罅罌罍罎罐网罕罔罘罟罠罨罩罧罸羂羆羃羈羇羌羔羞羝羚羣羯羲羹羮羶羸譱翅翆翊翕翔翡翦翩翳翹飜耆耄耋耒耘耙耜耡耨耿耻聊聆聒聘聚聟聢聨聳聲聰聶聹聽聿肄肆肅肛肓肚肭冐肬胛胥胙胝胄胚胖脉胯胱脛脩脣脯腋67",
|
|
4057
|
+
"隋腆脾腓腑胼腱腮腥腦腴膃膈膊膀膂膠膕膤膣腟膓膩膰膵膾膸膽臀臂膺臉臍臑臙臘臈臚臟臠臧臺臻臾舁舂舅與舊舍舐舖舩舫舸舳艀艙艘艝艚艟艤1艢艨艪艫舮艱艷艸艾芍芒芫芟芻芬苡苣苟苒苴苳苺莓范苻苹苞茆苜茉苙茵茴茖茲茱荀茹荐荅茯茫茗茘莅莚莪莟莢莖茣莎莇莊荼莵荳荵莠莉莨菴萓菫菎菽萃菘萋菁菷萇菠菲萍萢萠莽萸蔆菻葭萪萼蕚蒄葷葫蒭葮蒂葩葆萬葯葹萵蓊葢蒹蒿蒟蓙蓍蒻蓚蓐蓁蓆蓖蒡蔡蓿蓴蔗蔘蔬蔟蔕蔔蓼蕀蕣蕘蕈67",
|
|
4058
|
+
"蕁蘂蕋蕕薀薤薈薑薊薨蕭薔薛藪薇薜蕷蕾薐藉薺藏薹藐藕藝藥藜藹蘊蘓蘋藾藺蘆蘢蘚蘰蘿虍乕虔號虧虱蚓蚣蚩蚪蚋蚌蚶蚯蛄蛆蚰蛉蠣蚫蛔蛞蛩蛬1蛟蛛蛯蜒蜆蜈蜀蜃蛻蜑蜉蜍蛹蜊蜴蜿蜷蜻蜥蜩蜚蝠蝟蝸蝌蝎蝴蝗蝨蝮蝙蝓蝣蝪蠅螢螟螂螯蟋螽蟀蟐雖螫蟄螳蟇蟆螻蟯蟲蟠蠏蠍蟾蟶蟷蠎蟒蠑蠖蠕蠢蠡蠱蠶蠹蠧蠻衄衂衒衙衞衢衫袁衾袞衵衽袵衲袂袗袒袮袙袢袍袤袰袿袱裃裄裔裘裙裝裹褂裼裴裨裲褄褌褊褓襃褞褥褪褫襁襄褻褶褸襌褝襠襞67",
|
|
4059
|
+
"襦襤襭襪襯襴襷襾覃覈覊覓覘覡覩覦覬覯覲覺覽覿觀觚觜觝觧觴觸訃訖訐訌訛訝訥訶詁詛詒詆詈詼詭詬詢誅誂誄誨誡誑誥誦誚誣諄諍諂諚諫諳諧1諤諱謔諠諢諷諞諛謌謇謚諡謖謐謗謠謳鞫謦謫謾謨譁譌譏譎證譖譛譚譫譟譬譯譴譽讀讌讎讒讓讖讙讚谺豁谿豈豌豎豐豕豢豬豸豺貂貉貅貊貍貎貔豼貘戝貭貪貽貲貳貮貶賈賁賤賣賚賽賺賻贄贅贊贇贏贍贐齎贓賍贔贖赧赭赱赳趁趙跂趾趺跏跚跖跌跛跋跪跫跟跣跼踈踉跿踝踞踐踟蹂踵踰踴蹊67",
|
|
4060
|
+
"蹇蹉蹌蹐蹈蹙蹤蹠踪蹣蹕蹶蹲蹼躁躇躅躄躋躊躓躑躔躙躪躡躬躰軆躱躾軅軈軋軛軣軼軻軫軾輊輅輕輒輙輓輜輟輛輌輦輳輻輹轅轂輾轌轉轆轎轗轜1轢轣轤辜辟辣辭辯辷迚迥迢迪迯邇迴逅迹迺逑逕逡逍逞逖逋逧逶逵逹迸遏遐遑遒逎遉逾遖遘遞遨遯遶隨遲邂遽邁邀邊邉邏邨邯邱邵郢郤扈郛鄂鄒鄙鄲鄰酊酖酘酣酥酩酳酲醋醉醂醢醫醯醪醵醴醺釀釁釉釋釐釖釟釡釛釼釵釶鈞釿鈔鈬鈕鈑鉞鉗鉅鉉鉤鉈銕鈿鉋鉐銜銖銓銛鉚鋏銹銷鋩錏鋺鍄錮67",
|
|
4061
|
+
"錙錢錚錣錺錵錻鍜鍠鍼鍮鍖鎰鎬鎭鎔鎹鏖鏗鏨鏥鏘鏃鏝鏐鏈鏤鐚鐔鐓鐃鐇鐐鐶鐫鐵鐡鐺鑁鑒鑄鑛鑠鑢鑞鑪鈩鑰鑵鑷鑽鑚鑼鑾钁鑿閂閇閊閔閖閘閙1閠閨閧閭閼閻閹閾闊濶闃闍闌闕闔闖關闡闥闢阡阨阮阯陂陌陏陋陷陜陞陝陟陦陲陬隍隘隕隗險隧隱隲隰隴隶隸隹雎雋雉雍襍雜霍雕雹霄霆霈霓霎霑霏霖霙霤霪霰霹霽霾靄靆靈靂靉靜靠靤靦靨勒靫靱靹鞅靼鞁靺鞆鞋鞏鞐鞜鞨鞦鞣鞳鞴韃韆韈韋韜韭齏韲竟韶韵頏頌頸頤頡頷頽顆顏顋顫顯顰67",
|
|
4062
|
+
"顱顴顳颪颯颱颶飄飃飆飩飫餃餉餒餔餘餡餝餞餤餠餬餮餽餾饂饉饅饐饋饑饒饌饕馗馘馥馭馮馼駟駛駝駘駑駭駮駱駲駻駸騁騏騅駢騙騫騷驅驂驀驃1騾驕驍驛驗驟驢驥驤驩驫驪骭骰骼髀髏髑髓體髞髟髢髣髦髯髫髮髴髱髷髻鬆鬘鬚鬟鬢鬣鬥鬧鬨鬩鬪鬮鬯鬲魄魃魏魍魎魑魘魴鮓鮃鮑鮖鮗鮟鮠鮨鮴鯀鯊鮹鯆鯏鯑鯒鯣鯢鯤鯔鯡鰺鯲鯱鯰鰕鰔鰉鰓鰌鰆鰈鰒鰊鰄鰮鰛鰥鰤鰡鰰鱇鰲鱆鰾鱚鱠鱧鱶鱸鳧鳬鳰鴉鴈鳫鴃鴆鴪鴦鶯鴣鴟鵄鴕鴒鵁鴿鴾鵆鵈67",
|
|
4063
|
+
"鵝鵞鵤鵑鵐鵙鵲鶉鶇鶫鵯鵺鶚鶤鶩鶲鷄鷁鶻鶸鶺鷆鷏鷂鷙鷓鷸鷦鷭鷯鷽鸚鸛鸞鹵鹹鹽麁麈麋麌麒麕麑麝麥麩麸麪麭靡黌黎黏黐黔黜點黝黠黥黨黯1黴黶黷黹黻黼黽鼇鼈皷鼕鼡鼬鼾齊齒齔齣齟齠齡齦齧齬齪齷齲齶龕龜龠堯槇遙瑤凜熙667",
|
|
4064
|
+
"纊褜鍈銈蓜俉炻昱棈鋹曻彅丨仡仼伀伃伹佖侒侊侚侔俍偀倢俿倞偆偰偂傔僴僘兊兤冝冾凬刕劜劦勀勛匀匇匤卲厓厲叝﨎咜咊咩哿喆坙坥垬埈埇﨏1塚增墲夋奓奛奝奣妤妺孖寀甯寘寬尞岦岺峵崧嵓﨑嵂嵭嶸嶹巐弡弴彧德忞恝悅悊惞惕愠惲愑愷愰憘戓抦揵摠撝擎敎昀昕昻昉昮昞昤晥晗晙晴晳暙暠暲暿曺朎朗杦枻桒柀栁桄棏﨓楨﨔榘槢樰橫橆橳橾櫢櫤毖氿汜沆汯泚洄涇浯涖涬淏淸淲淼渹湜渧渼溿澈澵濵瀅瀇瀨炅炫焏焄煜煆煇凞燁燾犱67",
|
|
4065
|
+
"犾猤猪獷玽珉珖珣珒琇珵琦琪琩琮瑢璉璟甁畯皂皜皞皛皦益睆劯砡硎硤硺礰礼神祥禔福禛竑竧靖竫箞精絈絜綷綠緖繒罇羡羽茁荢荿菇菶葈蒴蕓蕙1蕫﨟薰蘒﨡蠇裵訒訷詹誧誾諟諸諶譓譿賰賴贒赶﨣軏﨤逸遧郞都鄕鄧釚釗釞釭釮釤釥鈆鈐鈊鈺鉀鈼鉎鉙鉑鈹鉧銧鉷鉸鋧鋗鋙鋐﨧鋕鋠鋓錥錡鋻﨨錞鋿錝錂鍰鍗鎤鏆鏞鏸鐱鑅鑈閒隆﨩隝隯霳霻靃靍靏靑靕顗顥飯飼餧館馞驎髙髜魵魲鮏鮱鮻鰀鵰鵫鶴鸙黑2ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹ¬¦'"323",
|
|
4066
|
+
"167",
|
|
4067
|
+
"167",
|
|
4068
|
+
"167",
|
|
4069
|
+
"167",
|
|
4070
|
+
"167",
|
|
4071
|
+
"167",
|
|
4072
|
+
"167",
|
|
4073
|
+
"167",
|
|
4074
|
+
"167",
|
|
4075
|
+
"167",
|
|
4076
|
+
"ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ¬¦'"㈱№℡∵纊褜鍈銈蓜俉炻昱棈鋹曻彅丨仡仼伀伃伹佖侒侊侚侔俍偀倢俿倞偆偰偂傔僴僘兊1兤冝冾凬刕劜劦勀勛匀匇匤卲厓厲叝﨎咜咊咩哿喆坙坥垬埈埇﨏塚增墲夋奓奛奝奣妤妺孖寀甯寘寬尞岦岺峵崧嵓﨑嵂嵭嶸嶹巐弡弴彧德忞恝悅悊惞惕愠惲愑愷愰憘戓抦揵摠撝擎敎昀昕昻昉昮昞昤晥晗晙晴晳暙暠暲暿曺朎朗杦枻桒柀栁桄棏﨓楨﨔榘槢樰橫橆橳橾櫢櫤毖氿汜沆汯泚洄涇浯67",
|
|
4077
|
+
"涖涬淏淸淲淼渹湜渧渼溿澈澵濵瀅瀇瀨炅炫焏焄煜煆煇凞燁燾犱犾猤猪獷玽珉珖珣珒琇珵琦琪琩琮瑢璉璟甁畯皂皜皞皛皦益睆劯砡硎硤硺礰礼神1祥禔福禛竑竧靖竫箞精絈絜綷綠緖繒罇羡羽茁荢荿菇菶葈蒴蕓蕙蕫﨟薰蘒﨡蠇裵訒訷詹誧誾諟諸諶譓譿賰賴贒赶﨣軏﨤逸遧郞都鄕鄧釚釗釞釭釮釤釥鈆鈐鈊鈺鉀鈼鉎鉙鉑鈹鉧銧鉷鉸鋧鋗鋙鋐﨧鋕鋠鋓錥錡鋻﨨錞鋿錝錂鍰鍗鎤鏆鏞鏸鐱鑅鑈閒隆﨩隝隯霳霻靃靍靏靑靕顗顥飯飼餧館馞驎髙67",
|
|
4078
|
+
"髜魵魲鮏鮱鮻鰀鵰鵫鶴鸙黑"
|
|
4079
|
+
].join("");
|
|
4080
|
+
|
|
4081
|
+
/*
|
|
4082
|
+
上の変換マップ作成用の文字列は数値が入った変換マップのコードから作成している
|
|
4083
|
+
let output = "";
|
|
4084
|
+
let nul_count = 0;
|
|
4085
|
+
for(i = 0x8140; i <= 0xffff; i++) {
|
|
4086
|
+
if(map[i]) {
|
|
4087
|
+
if(nul_count !== 0){
|
|
4088
|
+
output += nul_count;
|
|
4089
|
+
nul_count = 0;
|
|
4090
|
+
}
|
|
4091
|
+
output += MojiJS.fromCodePoint(map[i]);
|
|
4092
|
+
}
|
|
4093
|
+
else {
|
|
4094
|
+
nul_count++;
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
*/
|
|
4098
|
+
|
|
4099
|
+
/**
|
|
4100
|
+
* UTF16へ変換
|
|
4101
|
+
*/
|
|
4102
|
+
const utf16_array = Unicode.toUTF16Array(map);
|
|
4103
|
+
|
|
4104
|
+
// マップ展開
|
|
4105
|
+
let is_num = false;
|
|
4106
|
+
let num_array = [];
|
|
4107
|
+
let key = 0x8140;
|
|
4108
|
+
for (let i = 0; i < utf16_array.length; i++) {
|
|
4109
|
+
const x = utf16_array[i];
|
|
4110
|
+
if (0x30 <= x && x <= 0x39) {
|
|
4111
|
+
if (!is_num) {
|
|
4112
|
+
is_num = true;
|
|
4113
|
+
num_array = [];
|
|
4114
|
+
}
|
|
4115
|
+
num_array.push(x);
|
|
4116
|
+
} else {
|
|
4117
|
+
if (is_num) {
|
|
4118
|
+
key += parseFloat(Unicode.fromUTF16Array(num_array));
|
|
4119
|
+
is_num = false;
|
|
4120
|
+
}
|
|
4121
|
+
cp932_to_unicode_map[key] = x;
|
|
4122
|
+
key++;
|
|
4123
|
+
}
|
|
4124
|
+
}
|
|
4125
|
+
|
|
4126
|
+
return cp932_to_unicode_map;
|
|
4127
|
+
};
|
|
4128
|
+
|
|
4129
|
+
/**
|
|
4130
|
+
* CP932 変換マップ
|
|
4131
|
+
* @type {Record<number, number>}
|
|
4132
|
+
*/
|
|
4133
|
+
const cp932_to_unicode_map = getCp932ToUnicodeMap();
|
|
4134
|
+
|
|
4135
|
+
/* eslint-disable max-len */
|
|
4136
|
+
/**
|
|
4137
|
+
* 重複された CP932 のコード
|
|
4138
|
+
* @type {number[]}
|
|
4139
|
+
*/
|
|
4140
|
+
// prettier-ignore
|
|
4141
|
+
const duplicate_map_array = [
|
|
4142
|
+
0x8790, 0x8791, 0x8792, 0x8795, 0x8796, 0x8797, 0x879A, 0x879B, 0x879C, 0xED40, 0xED41, 0xED42, 0xED43, 0xED44, 0xED45, 0xED46,
|
|
4143
|
+
0xED47, 0xED48, 0xED49, 0xED4A, 0xED4B, 0xED4C, 0xED4D, 0xED4E, 0xED4F, 0xED50, 0xED51, 0xED52, 0xED53, 0xED54, 0xED55, 0xED56,
|
|
4144
|
+
0xED57, 0xED58, 0xED59, 0xED5A, 0xED5B, 0xED5C, 0xED5D, 0xED5E, 0xED5F, 0xED60, 0xED61, 0xED62, 0xED63, 0xED64, 0xED65, 0xED66,
|
|
4145
|
+
0xED67, 0xED68, 0xED69, 0xED6A, 0xED6B, 0xED6C, 0xED6D, 0xED6E, 0xED6F, 0xED70, 0xED71, 0xED72, 0xED73, 0xED74, 0xED75, 0xED76,
|
|
4146
|
+
0xED77, 0xED78, 0xED79, 0xED7A, 0xED7B, 0xED7C, 0xED7D, 0xED7E, 0xED80, 0xED81, 0xED82, 0xED83, 0xED84, 0xED85, 0xED86, 0xED87,
|
|
4147
|
+
0xED88, 0xED89, 0xED8A, 0xED8B, 0xED8C, 0xED8D, 0xED8E, 0xED8F, 0xED90, 0xED91, 0xED92, 0xED93, 0xED94, 0xED95, 0xED96, 0xED97,
|
|
4148
|
+
0xED98, 0xED99, 0xED9A, 0xED9B, 0xED9C, 0xED9D, 0xED9E, 0xED9F, 0xEDA0, 0xEDA1, 0xEDA2, 0xEDA3, 0xEDA4, 0xEDA5, 0xEDA6, 0xEDA7,
|
|
4149
|
+
0xEDA8, 0xEDA9, 0xEDAA, 0xEDAB, 0xEDAC, 0xEDAD, 0xEDAE, 0xEDAF, 0xEDB0, 0xEDB1, 0xEDB2, 0xEDB3, 0xEDB4, 0xEDB5, 0xEDB6, 0xEDB7,
|
|
4150
|
+
0xEDB8, 0xEDB9, 0xEDBA, 0xEDBB, 0xEDBC, 0xEDBD, 0xEDBE, 0xEDBF, 0xEDC0, 0xEDC1, 0xEDC2, 0xEDC3, 0xEDC4, 0xEDC5, 0xEDC6, 0xEDC7,
|
|
4151
|
+
0xEDC8, 0xEDC9, 0xEDCA, 0xEDCB, 0xEDCC, 0xEDCD, 0xEDCE, 0xEDCF, 0xEDD0, 0xEDD1, 0xEDD2, 0xEDD3, 0xEDD4, 0xEDD5, 0xEDD6, 0xEDD7,
|
|
4152
|
+
0xEDD8, 0xEDD9, 0xEDDA, 0xEDDB, 0xEDDC, 0xEDDD, 0xEDDE, 0xEDDF, 0xEDE0, 0xEDE1, 0xEDE2, 0xEDE3, 0xEDE4, 0xEDE5, 0xEDE6, 0xEDE7,
|
|
4153
|
+
0xEDE8, 0xEDE9, 0xEDEA, 0xEDEB, 0xEDEC, 0xEDED, 0xEDEE, 0xEDEF, 0xEDF0, 0xEDF1, 0xEDF2, 0xEDF3, 0xEDF4, 0xEDF5, 0xEDF6, 0xEDF7,
|
|
4154
|
+
0xEDF8, 0xEDF9, 0xEDFA, 0xEDFB, 0xEDFC, 0xEE40, 0xEE41, 0xEE42, 0xEE43, 0xEE44, 0xEE45, 0xEE46, 0xEE47, 0xEE48, 0xEE49, 0xEE4A,
|
|
4155
|
+
0xEE4B, 0xEE4C, 0xEE4D, 0xEE4E, 0xEE4F, 0xEE50, 0xEE51, 0xEE52, 0xEE53, 0xEE54, 0xEE55, 0xEE56, 0xEE57, 0xEE58, 0xEE59, 0xEE5A,
|
|
4156
|
+
0xEE5B, 0xEE5C, 0xEE5D, 0xEE5E, 0xEE5F, 0xEE60, 0xEE61, 0xEE62, 0xEE63, 0xEE64, 0xEE65, 0xEE66, 0xEE67, 0xEE68, 0xEE69, 0xEE6A,
|
|
4157
|
+
0xEE6B, 0xEE6C, 0xEE6D, 0xEE6E, 0xEE6F, 0xEE70, 0xEE71, 0xEE72, 0xEE73, 0xEE74, 0xEE75, 0xEE76, 0xEE77, 0xEE78, 0xEE79, 0xEE7A,
|
|
4158
|
+
0xEE7B, 0xEE7C, 0xEE7D, 0xEE7E, 0xEE80, 0xEE81, 0xEE82, 0xEE83, 0xEE84, 0xEE85, 0xEE86, 0xEE87, 0xEE88, 0xEE89, 0xEE8A, 0xEE8B,
|
|
4159
|
+
0xEE8C, 0xEE8D, 0xEE8E, 0xEE8F, 0xEE90, 0xEE91, 0xEE92, 0xEE93, 0xEE94, 0xEE95, 0xEE96, 0xEE97, 0xEE98, 0xEE99, 0xEE9A, 0xEE9B,
|
|
4160
|
+
0xEE9C, 0xEE9D, 0xEE9E, 0xEE9F, 0xEEA0, 0xEEA1, 0xEEA2, 0xEEA3, 0xEEA4, 0xEEA5, 0xEEA6, 0xEEA7, 0xEEA8, 0xEEA9, 0xEEAA, 0xEEAB,
|
|
4161
|
+
0xEEAC, 0xEEAD, 0xEEAE, 0xEEAF, 0xEEB0, 0xEEB1, 0xEEB2, 0xEEB3, 0xEEB4, 0xEEB5, 0xEEB6, 0xEEB7, 0xEEB8, 0xEEB9, 0xEEBA, 0xEEBB,
|
|
4162
|
+
0xEEBC, 0xEEBD, 0xEEBE, 0xEEBF, 0xEEC0, 0xEEC1, 0xEEC2, 0xEEC3, 0xEEC4, 0xEEC5, 0xEEC6, 0xEEC7, 0xEEC8, 0xEEC9, 0xEECA, 0xEECB,
|
|
4163
|
+
0xEECC, 0xEECD, 0xEECE, 0xEECF, 0xEED0, 0xEED1, 0xEED2, 0xEED3, 0xEED4, 0xEED5, 0xEED6, 0xEED7, 0xEED8, 0xEED9, 0xEEDA, 0xEEDB,
|
|
4164
|
+
0xEEDC, 0xEEDD, 0xEEDE, 0xEEDF, 0xEEE0, 0xEEE1, 0xEEE2, 0xEEE3, 0xEEE4, 0xEEE5, 0xEEE6, 0xEEE7, 0xEEE8, 0xEEE9, 0xEEEA, 0xEEEB,
|
|
4165
|
+
0xEEEC, 0xEEEF, 0xEEF0, 0xEEF1, 0xEEF2, 0xEEF3, 0xEEF4, 0xEEF5, 0xEEF6, 0xEEF7, 0xEEF8, 0xEEF9, 0xEEFA, 0xEEFB, 0xEEFC, 0xFA4A,
|
|
4166
|
+
0xFA4B, 0xFA4C, 0xFA4D, 0xFA4E, 0xFA4F, 0xFA50, 0xFA51, 0xFA52, 0xFA53, 0xFA54, 0xFA58, 0xFA59, 0xFA5A, 0xFA5B
|
|
4167
|
+
];
|
|
4168
|
+
/* eslint-enable max-len */
|
|
4169
|
+
|
|
4170
|
+
/**
|
|
4171
|
+
* @type {Record<number, number>}
|
|
4172
|
+
*/
|
|
4173
|
+
const duplicate_map = {};
|
|
4174
|
+
|
|
4175
|
+
/**
|
|
4176
|
+
* @type {Record<number, number>}
|
|
4177
|
+
*/
|
|
4178
|
+
const unicode_to_cp932_map = {};
|
|
4179
|
+
|
|
4180
|
+
for (const key in duplicate_map_array) {
|
|
4181
|
+
duplicate_map[duplicate_map_array[key]] = 1;
|
|
4182
|
+
}
|
|
4183
|
+
for (const key in cp932_to_unicode_map) {
|
|
4184
|
+
// 重複登録された文字
|
|
4185
|
+
// IBM拡張文字 と NEC特殊文字 と NEC選定IBM拡張文字 で
|
|
4186
|
+
// マッピング先が一部重複している。
|
|
4187
|
+
// WideCharToMultiByte の仕様に基づき、登録しない。
|
|
4188
|
+
if (duplicate_map[key]) {
|
|
4189
|
+
continue;
|
|
4190
|
+
}
|
|
4191
|
+
const x = cp932_to_unicode_map[key];
|
|
4192
|
+
unicode_to_cp932_map[x] = parseInt(key, 10);
|
|
4193
|
+
}
|
|
4194
|
+
|
|
4195
|
+
// 逆引きの注意点
|
|
4196
|
+
|
|
4197
|
+
// 半角¥マーク問題
|
|
4198
|
+
// 半角¥マークは、Shift_JISの「5c 0xReverse Solidus 逆斜線」にする
|
|
4199
|
+
// Unicode '¥' 0x00a5 Yen Sign 半角円マーク
|
|
4200
|
+
unicode_to_cp932_map[0xA5] = 0x5C;
|
|
4201
|
+
|
|
4202
|
+
// 波線問題
|
|
4203
|
+
// SJIS2004上は 0x8160 と 0x81B0 とで区別されている。
|
|
4204
|
+
// Shift_JISは 0x301c を 0x8160 に統一
|
|
4205
|
+
// Unicode '〜' 0x301c Shift_JIS-2004 0x8160 Wave Dash 波ダッシュ
|
|
4206
|
+
// Unicode '~' 0xff5e Shift_JIS-2004 0x81B0 Fullwidth Tilde 全角チルダ
|
|
4207
|
+
unicode_to_cp932_map[0x301C] = 0x8160;
|
|
4208
|
+
|
|
4209
|
+
// マイナス問題
|
|
4210
|
+
// SJIS2004上は 0x817c と 0x81af とで区別されている。
|
|
4211
|
+
// Shift_JISは、0x2212 を全角負記号 0x817c へ変更
|
|
4212
|
+
// Unicode `−` 0x2212 Shift_JIS-2004 0x817c 負符号/減算記号
|
|
4213
|
+
// Unicode `-` 0xff0d Shift_JIS-2004 0x81af ハイフンマイナス
|
|
4214
|
+
unicode_to_cp932_map[0x2212] = 0x817C;
|
|
4215
|
+
|
|
4216
|
+
CP932MAP.cp932_to_unicode_map = cp932_to_unicode_map;
|
|
4217
|
+
CP932MAP.unicode_to_cp932_map = unicode_to_cp932_map;
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
/**
|
|
4221
|
+
* @returns {Record<number, number>}
|
|
4222
|
+
*/
|
|
4223
|
+
static CP932_TO_UNICODE() {
|
|
4224
|
+
CP932MAP.init();
|
|
4225
|
+
return CP932MAP.cp932_to_unicode_map;
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
/**
|
|
4229
|
+
* @returns {Record<number, number>}
|
|
4230
|
+
*/
|
|
4231
|
+
static UNICODE_TO_CP932() {
|
|
4232
|
+
CP932MAP.init();
|
|
4233
|
+
return CP932MAP.unicode_to_cp932_map;
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
|
|
4237
|
+
/**
|
|
4238
|
+
* 変換マップを初期化したかどうか
|
|
4239
|
+
* @type {boolean}
|
|
4240
|
+
*/
|
|
4241
|
+
CP932MAP.is_initmap = false;
|
|
4242
|
+
|
|
4243
|
+
/**
|
|
4244
|
+
* 変換用マップ
|
|
4245
|
+
* @type {Record<number, number>}
|
|
4246
|
+
*/
|
|
4247
|
+
CP932MAP.cp932_to_unicode_map = null;
|
|
4248
|
+
|
|
4249
|
+
/**
|
|
4250
|
+
* 変換用マップ
|
|
4251
|
+
* @type {Record<number, number>}
|
|
4252
|
+
*/
|
|
4253
|
+
CP932MAP.unicode_to_cp932_map = null;
|
|
4254
|
+
|
|
4255
|
+
/**
|
|
4256
|
+
* CP932, Windows-31J を扱うクラス
|
|
4257
|
+
* @ignore
|
|
4258
|
+
*/
|
|
4259
|
+
class CP932 {
|
|
4260
|
+
/**
|
|
4261
|
+
* Unicode のコードから CP932 のコードに変換
|
|
4262
|
+
* @param {number} unicode_codepoint - Unicode のコードポイント
|
|
4263
|
+
* @returns {number} CP932 のコードポイント (存在しない場合は undefined)
|
|
4264
|
+
*/
|
|
4265
|
+
static toCP932FromUnicode(unicode_codepoint) {
|
|
4266
|
+
return CP932MAP.UNICODE_TO_CP932()[unicode_codepoint];
|
|
4267
|
+
}
|
|
4268
|
+
|
|
4269
|
+
/**
|
|
4270
|
+
* CP932 のコードから Unicode のコードに変換
|
|
4271
|
+
* @param {number} cp932_codepoint - CP932 のコードポイント
|
|
4272
|
+
* @returns {number} Unicode のコードポイント (存在しない場合は undefined)
|
|
4273
|
+
*/
|
|
4274
|
+
static toUnicodeFromCP932(cp932_codepoint) {
|
|
4275
|
+
return CP932MAP.CP932_TO_UNICODE()[cp932_codepoint];
|
|
4276
|
+
}
|
|
4277
|
+
|
|
4278
|
+
/**
|
|
4279
|
+
* 文字列を CP932 の配列に変換。変換できない文字は "?" に変換される。
|
|
4280
|
+
* @param {string} text - 変換したいテキスト
|
|
4281
|
+
* @returns {number[]} CP932 のデータが入った配列
|
|
4282
|
+
*/
|
|
4283
|
+
static toCP932Array(text) {
|
|
4284
|
+
return SJIS.toSJISArray(text, CP932MAP.UNICODE_TO_CP932());
|
|
4285
|
+
}
|
|
4286
|
+
|
|
4287
|
+
/**
|
|
4288
|
+
* 文字列を CP932 のバイナリ配列に変換。変換できない文字は "?" に変換される。
|
|
4289
|
+
* - 日本語文字は2バイトとして、配列も2つ分、使用します。
|
|
4290
|
+
* @param {string} text - 変換したいテキスト
|
|
4291
|
+
* @returns {number[]} CP932 のデータが入ったバイナリ配列
|
|
4292
|
+
*/
|
|
4293
|
+
static toCP932Binary(text) {
|
|
4294
|
+
return SJIS.toSJISBinary(text, CP932MAP.UNICODE_TO_CP932());
|
|
4295
|
+
}
|
|
4296
|
+
|
|
4297
|
+
/**
|
|
4298
|
+
* CP932 の配列から文字列に変換
|
|
4299
|
+
* @param {number[]} cp932 - 変換したいテキスト
|
|
4300
|
+
* @returns {string} 変換後のテキスト
|
|
4301
|
+
*/
|
|
4302
|
+
static fromCP932Array(cp932) {
|
|
4303
|
+
return SJIS.fromSJISArray(cp932, CP932MAP.CP932_TO_UNICODE());
|
|
4304
|
+
}
|
|
4305
|
+
|
|
4306
|
+
/**
|
|
4307
|
+
* 指定した文字から Windows-31J 上の区点番号に変換
|
|
4308
|
+
* - 2文字以上を指定した場合は、1文字目のみを変換する
|
|
4309
|
+
* @param {string} text - 変換したいテキスト
|
|
4310
|
+
* @returns {MenKuTen} 区点番号(存在しない場合(1バイトのJISコードなど)はnullを返す)
|
|
4311
|
+
*/
|
|
4312
|
+
static toKuTen(text) {
|
|
4313
|
+
if (text.length === 0) {
|
|
4314
|
+
return null;
|
|
4315
|
+
}
|
|
4316
|
+
const cp932_code = CP932.toCP932FromUnicode(Unicode.toUTF32Array(text)[0]);
|
|
4317
|
+
return cp932_code ? SJIS.toKuTenFromSJISCode(cp932_code) : null;
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4321
|
+
/**
|
|
4322
|
+
* The script is part of Mojix for TextInputGuard.
|
|
4323
|
+
*
|
|
4324
|
+
* AUTHOR:
|
|
4325
|
+
* natade-jp (https://github.com/natade-jp)
|
|
4326
|
+
*
|
|
4327
|
+
* LICENSE:
|
|
4328
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
4329
|
+
*/
|
|
4330
|
+
|
|
4331
|
+
|
|
4332
|
+
/**
|
|
4333
|
+
* Encode用のツールクラス
|
|
4334
|
+
* @ignore
|
|
4335
|
+
*/
|
|
4336
|
+
class EncodeTools {
|
|
4337
|
+
/**
|
|
4338
|
+
* キャラセット名の正規化
|
|
4339
|
+
* @param {string} charset
|
|
4340
|
+
* @returns {string}
|
|
4341
|
+
*/
|
|
4342
|
+
static normalizeCharSetName(charset) {
|
|
4343
|
+
let x1, x2;
|
|
4344
|
+
let is_with_bom = false;
|
|
4345
|
+
// BOM の文字がある場合は BOM 付きとする
|
|
4346
|
+
if (/^bom\s+|\s+bom\s+|\s+bom$/i.test(x1)) {
|
|
4347
|
+
is_with_bom = true;
|
|
4348
|
+
x1 = charset.replace(/^bom\s+|(\s+with)?\s+bom\s+|(\s+with\s*)?\s+bom$/, "");
|
|
4349
|
+
} else {
|
|
4350
|
+
x1 = charset;
|
|
4351
|
+
}
|
|
4352
|
+
if (/^(unicode-1-1-utf-8|UTF[-_]?8)$/i.test(x1)) {
|
|
4353
|
+
x2 = "UTF-8";
|
|
4354
|
+
} else if (/^(csunicode|iso-10646-ucs-2|ucs-2|Unicode|UnicodeFEFF|UTF[-_]?16([-_]?LE)?)$/i.test(x1)) {
|
|
4355
|
+
x2 = "UTF-16LE";
|
|
4356
|
+
} else if (/^(UnicodeFFFE|UTF[-_]?16[-_]?BE)$/i.test(x1)) {
|
|
4357
|
+
x2 = "UTF-16BE";
|
|
4358
|
+
} else if (/^(utf32_littleendian|UTF[-_]?32([-_]?LE)?)$/i.test(x1)) {
|
|
4359
|
+
x2 = "UTF-32LE";
|
|
4360
|
+
} else if (/^(utf32_bigendian|UTF[-_]?32[-_]?BE)$/i.test(x1)) {
|
|
4361
|
+
x2 = "UTF-32BE";
|
|
4362
|
+
} else if (/^(csshiftjis|ms_kanji|(cp|ms)932|shift[-_]?jis|sjis|Windows[-_]?31J|x-sjis)$/i.test(x1)) {
|
|
4363
|
+
x2 = "Shift_JIS";
|
|
4364
|
+
} else {
|
|
4365
|
+
x2 = x1;
|
|
4366
|
+
}
|
|
4367
|
+
if (is_with_bom) {
|
|
4368
|
+
x2 += " with BOM";
|
|
4369
|
+
}
|
|
4370
|
+
return x2;
|
|
4371
|
+
}
|
|
4372
|
+
|
|
4373
|
+
/**
|
|
4374
|
+
* 同一の種別の文字列の重なりをカウントする
|
|
4375
|
+
* @param {number[]} utf32_array
|
|
4376
|
+
* @returns {number}
|
|
4377
|
+
*/
|
|
4378
|
+
static countWord(utf32_array) {
|
|
4379
|
+
let count = 0;
|
|
4380
|
+
let type;
|
|
4381
|
+
let old_type = -1;
|
|
4382
|
+
for (let i = 0; i < utf32_array.length; i++) {
|
|
4383
|
+
const ch = utf32_array[i];
|
|
4384
|
+
// a-zA-Z
|
|
4385
|
+
// prettier-ignore
|
|
4386
|
+
if ((0x41 <= ch && ch <= 0x5A) || (0x61 <= ch && ch <= 0x6A)) {
|
|
4387
|
+
type = 1;
|
|
4388
|
+
// prettier-ignore
|
|
4389
|
+
} else if (0x30 <= ch && ch <= 0x39) {
|
|
4390
|
+
// 0-9
|
|
4391
|
+
type = 2;
|
|
4392
|
+
// prettier-ignore
|
|
4393
|
+
} else if (0x3041 <= ch && ch <= 0x3093) {
|
|
4394
|
+
// ぁ-ん
|
|
4395
|
+
type = 3;
|
|
4396
|
+
// prettier-ignore
|
|
4397
|
+
} else if (0x30A1 <= ch && ch <= 0x30F3) {
|
|
4398
|
+
// ァ-ン
|
|
4399
|
+
type = 4;
|
|
4400
|
+
// prettier-ignore
|
|
4401
|
+
} else if ((0xFF21 <= ch && ch <= 0xFF3A) || (0xFF41 <= ch && ch <= 0xFF5A)) {
|
|
4402
|
+
// 全角英字
|
|
4403
|
+
type = 5;
|
|
4404
|
+
} else if (0xFF10 <= ch && ch <= 0xFF19) {
|
|
4405
|
+
// 全角数値
|
|
4406
|
+
type = 6;
|
|
4407
|
+
// prettier-ignore
|
|
4408
|
+
} else if (0xFF61 <= ch && ch < 0xFFA0) {
|
|
4409
|
+
// 半角カタカナ
|
|
4410
|
+
type = 7;
|
|
4411
|
+
// prettier-ignore
|
|
4412
|
+
} else if ((0x3400 <= ch && ch < 0xA000) || (0x20000 <= ch && ch < 0x2FA20)) {
|
|
4413
|
+
// CJK統合漢字拡張A - CJK統合漢字, 追加漢字面
|
|
4414
|
+
type = 8;
|
|
4415
|
+
} else {
|
|
4416
|
+
old_type = -1;
|
|
4417
|
+
continue;
|
|
4418
|
+
}
|
|
4419
|
+
if (type === old_type) {
|
|
4420
|
+
count++;
|
|
4421
|
+
}
|
|
4422
|
+
old_type = type;
|
|
4423
|
+
}
|
|
4424
|
+
return count;
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
|
|
4428
|
+
/**
|
|
4429
|
+
* 文字データのバイナリへのエンコード、文字列へのデコードを扱うクラス
|
|
4430
|
+
* @ignore
|
|
4431
|
+
*/
|
|
4432
|
+
class Encode {
|
|
4433
|
+
/**
|
|
4434
|
+
* 文字列からバイナリ配列にエンコードする
|
|
4435
|
+
* @param {string} text - 変換したいテキスト
|
|
4436
|
+
* @param {string} charset - キャラセット(UTF-8/16/32,Shift_JIS,Windows-31J,Shift_JIS-2004,EUC-JP,EUC-JP-2004)
|
|
4437
|
+
* @param {boolean} [is_with_bom=true] - BOMをつけるかどうか
|
|
4438
|
+
* @returns {number[]} バイナリ配列(失敗時はnull)
|
|
4439
|
+
*/
|
|
4440
|
+
static encode(text, charset, is_with_bom) {
|
|
4441
|
+
const ncharset = charset ? EncodeTools.normalizeCharSetName(charset) : "autodetect";
|
|
4442
|
+
if (/^UTF-(8|16|32)/i.test(ncharset)) {
|
|
4443
|
+
const utf32_array = Unicode.toUTF32Array(text);
|
|
4444
|
+
return Unicode.toUTFBinaryFromCodePoint(utf32_array, ncharset, is_with_bom);
|
|
4445
|
+
} else if (/^Shift_JIS$/i.test(ncharset)) {
|
|
4446
|
+
return CP932.toCP932Binary(text);
|
|
4447
|
+
}
|
|
4448
|
+
return null;
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4451
|
+
/**
|
|
4452
|
+
* バイナリ配列から文字列にデコードする
|
|
4453
|
+
* @param {number[]} binary - 変換したいバイナリ配列
|
|
4454
|
+
* @param {string} [charset="autodetect"] - キャラセット(UTF-8/16/32,Shift_JIS)
|
|
4455
|
+
* @returns {string} 変換した文字列(失敗したらnull)
|
|
4456
|
+
*/
|
|
4457
|
+
static decode(binary, charset) {
|
|
4458
|
+
const ncharset = charset ? EncodeTools.normalizeCharSetName(charset) : "autodetect";
|
|
4459
|
+
if (/^UTF-(8|16|32)/i.test(ncharset)) {
|
|
4460
|
+
const ret = Unicode.toCodePointFromUTFBinary(binary, charset);
|
|
4461
|
+
if (ret) {
|
|
4462
|
+
return Unicode.fromUTF32Array(ret);
|
|
4463
|
+
}
|
|
4464
|
+
} else if (/^Shift_JIS$/i.test(ncharset)) {
|
|
4465
|
+
return CP932.fromCP932Array(binary);
|
|
4466
|
+
}
|
|
4467
|
+
return null;
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
|
|
4471
|
+
/**
|
|
4472
|
+
* The script is part of Mojix for TextInputGuard.
|
|
4473
|
+
*
|
|
4474
|
+
* AUTHOR:
|
|
4475
|
+
* natade-jp (https://github.com/natade-jp)
|
|
4476
|
+
*
|
|
4477
|
+
* LICENSE:
|
|
4478
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
4479
|
+
*/
|
|
4480
|
+
|
|
4481
|
+
|
|
4482
|
+
/**
|
|
4483
|
+
* 日本語を扱うクラス
|
|
4484
|
+
* @ignore
|
|
4485
|
+
*/
|
|
4486
|
+
class Japanese {
|
|
4487
|
+
/**
|
|
4488
|
+
* カタカナをひらがなに変換
|
|
4489
|
+
* @param {string} text - 変換したいテキスト
|
|
4490
|
+
* @returns {string} 変換後のテキスト
|
|
4491
|
+
*/
|
|
4492
|
+
static toHiragana(text) {
|
|
4493
|
+
/**
|
|
4494
|
+
* @param {string} ch
|
|
4495
|
+
*/
|
|
4496
|
+
const func = function (ch) {
|
|
4497
|
+
// prettier-ignore
|
|
4498
|
+
return String.fromCharCode(ch.charCodeAt(0) - 0x0060);
|
|
4499
|
+
};
|
|
4500
|
+
return text.replace(/[\u30A1-\u30F6]/g, func);
|
|
4501
|
+
}
|
|
4502
|
+
|
|
4503
|
+
/**
|
|
4504
|
+
* ひらがなをカタカナに変換
|
|
4505
|
+
* @param {string} text - 変換したいテキスト
|
|
4506
|
+
* @returns {string} 変換後のテキスト
|
|
4507
|
+
*/
|
|
4508
|
+
static toKatakana(text) {
|
|
4509
|
+
/**
|
|
4510
|
+
* @param {string} ch
|
|
4511
|
+
*/
|
|
4512
|
+
const func = function (ch) {
|
|
4513
|
+
// prettier-ignore
|
|
4514
|
+
return String.fromCharCode(ch.charCodeAt(0) + 0x0060);
|
|
4515
|
+
};
|
|
4516
|
+
return text.replace(/[\u3041-\u3096]/g, func);
|
|
4517
|
+
}
|
|
4518
|
+
|
|
4519
|
+
/**
|
|
4520
|
+
* スペースを半角に変換
|
|
4521
|
+
* @param {string} text - 変換したいテキスト
|
|
4522
|
+
* @returns {string} 変換後のテキスト
|
|
4523
|
+
*/
|
|
4524
|
+
static toHalfWidthSpace(text) {
|
|
4525
|
+
// prettier-ignore
|
|
4526
|
+
return text.replace(/\u3000/g, String.fromCharCode(0x0020));
|
|
4527
|
+
}
|
|
4528
|
+
|
|
4529
|
+
/**
|
|
4530
|
+
* スペースを全角に変換
|
|
4531
|
+
* @param {string} text - 変換したいテキスト
|
|
4532
|
+
* @returns {string} 変換後のテキスト
|
|
4533
|
+
*/
|
|
4534
|
+
static toFullWidthSpace(text) {
|
|
4535
|
+
// prettier-ignore
|
|
4536
|
+
return text.replace(/\u0020/g, String.fromCharCode(0x3000));
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
/**
|
|
4540
|
+
* 英数記号を半角に変換
|
|
4541
|
+
* @param {string} text - 変換したいテキスト
|
|
4542
|
+
* @returns {string} 変換後のテキスト
|
|
4543
|
+
*/
|
|
4544
|
+
static toHalfWidthAsciiCode(text) {
|
|
4545
|
+
let out = text;
|
|
4546
|
+
out = out.replace(/\u3000/g, "\u0020"); //全角スペース
|
|
4547
|
+
out = out.replace(/[\u2018-\u201B]/g, "\u0027"); //シングルクォーテーション
|
|
4548
|
+
out = out.replace(/[\u201C-\u201F]/g, "\u0022"); //ダブルクォーテーション
|
|
4549
|
+
/**
|
|
4550
|
+
* @param {string} ch
|
|
4551
|
+
*/
|
|
4552
|
+
const func = function (ch) {
|
|
4553
|
+
const code = ch.charCodeAt(0);
|
|
4554
|
+
// prettier-ignore
|
|
4555
|
+
return String.fromCharCode(code - 0xFEE0);
|
|
4556
|
+
};
|
|
4557
|
+
return out.replace(/[\uFF01-\uFF5E]/g, func);
|
|
4558
|
+
}
|
|
4559
|
+
|
|
4560
|
+
/**
|
|
4561
|
+
* 英数記号を全角に変換
|
|
4562
|
+
* @param {string} text - 変換したいテキスト
|
|
4563
|
+
* @returns {string} 変換後のテキスト
|
|
4564
|
+
*/
|
|
4565
|
+
static toFullWidthAsciiCode(text) {
|
|
4566
|
+
let out = text;
|
|
4567
|
+
out = out.replace(/\u0020/g, "\u3000"); //全角スペース
|
|
4568
|
+
out = out.replace(/\u0022/g, "\u201D"); //ダブルクォーテーション
|
|
4569
|
+
out = out.replace(/\u0027/g, "\u2019"); //アポストロフィー
|
|
4570
|
+
/**
|
|
4571
|
+
* @param {string} ch
|
|
4572
|
+
*/
|
|
4573
|
+
const func = function (ch) {
|
|
4574
|
+
const code = ch.charCodeAt(0);
|
|
4575
|
+
// prettier-ignore
|
|
4576
|
+
return String.fromCharCode(code + 0xFEE0);
|
|
4577
|
+
};
|
|
4578
|
+
return out.replace(/[\u0020-\u007E]/g, func);
|
|
4579
|
+
}
|
|
4580
|
+
|
|
4581
|
+
/**
|
|
4582
|
+
* アルファベットを半角に変換
|
|
4583
|
+
* @param {string} text - 変換したいテキスト
|
|
4584
|
+
* @returns {string} 変換後のテキスト
|
|
4585
|
+
*/
|
|
4586
|
+
static toHalfWidthAlphabet(text) {
|
|
4587
|
+
/**
|
|
4588
|
+
* @param {string} ch
|
|
4589
|
+
*/
|
|
4590
|
+
const func = function (ch) {
|
|
4591
|
+
// prettier-ignore
|
|
4592
|
+
return String.fromCharCode(ch.charCodeAt(0) - 0xFEE0);
|
|
4593
|
+
};
|
|
4594
|
+
return text.replace(/[\uFF21-\uFF3A\uFF41-\uFF5A]/g, func);
|
|
4595
|
+
}
|
|
4596
|
+
|
|
4597
|
+
/**
|
|
4598
|
+
* アルファベットを全角に変換
|
|
4599
|
+
* @param {string} text - 変換したいテキスト
|
|
4600
|
+
* @returns {string} 変換後のテキスト
|
|
4601
|
+
*/
|
|
4602
|
+
static toFullWidthAlphabet(text) {
|
|
4603
|
+
/**
|
|
4604
|
+
* @param {string} ch
|
|
4605
|
+
*/
|
|
4606
|
+
const func = function (ch) {
|
|
4607
|
+
// prettier-ignore
|
|
4608
|
+
return String.fromCharCode(ch.charCodeAt(0) + 0xFEE0);
|
|
4609
|
+
};
|
|
4610
|
+
return text.replace(/[A-Za-z]/g, func);
|
|
4611
|
+
}
|
|
4612
|
+
|
|
4613
|
+
/**
|
|
4614
|
+
* 数値を半角に変換
|
|
4615
|
+
* @param {string} text - 変換したいテキスト
|
|
4616
|
+
* @returns {string} 変換後のテキスト
|
|
4617
|
+
*/
|
|
4618
|
+
static toHalfWidthNumber(text) {
|
|
4619
|
+
/**
|
|
4620
|
+
* @param {string} ch
|
|
4621
|
+
*/
|
|
4622
|
+
const func = function (ch) {
|
|
4623
|
+
// prettier-ignore
|
|
4624
|
+
return String.fromCharCode(ch.charCodeAt(0) - 0xFEE0);
|
|
4625
|
+
};
|
|
4626
|
+
return text.replace(/[\uFF10-\uFF19]/g, func);
|
|
4627
|
+
}
|
|
4628
|
+
|
|
4629
|
+
/**
|
|
4630
|
+
* 数値を全角に変換
|
|
4631
|
+
* @param {string} text - 変換したいテキスト
|
|
4632
|
+
* @returns {string} 変換後のテキスト
|
|
4633
|
+
*/
|
|
4634
|
+
static toFullWidthNumber(text) {
|
|
4635
|
+
/**
|
|
4636
|
+
* @param {string} ch
|
|
4637
|
+
*/
|
|
4638
|
+
const func = function (ch) {
|
|
4639
|
+
// prettier-ignore
|
|
4640
|
+
return String.fromCharCode(ch.charCodeAt(0) + 0xFEE0);
|
|
4641
|
+
};
|
|
4642
|
+
return text.replace(/[0-9]/g, func);
|
|
4643
|
+
}
|
|
4644
|
+
|
|
4645
|
+
/**
|
|
4646
|
+
* カタカナを半角に変換
|
|
4647
|
+
* @param {string} text - 変換したいテキスト
|
|
4648
|
+
* @returns {string} 変換後のテキスト
|
|
4649
|
+
*/
|
|
4650
|
+
static toHalfWidthKana(text) {
|
|
4651
|
+
/**
|
|
4652
|
+
* @type {Object<number, string>}
|
|
4653
|
+
*/
|
|
4654
|
+
// prettier-ignore
|
|
4655
|
+
const map = {
|
|
4656
|
+
0x3001: "\uFF64", // 、
|
|
4657
|
+
0x3002: "\uFF61", // 。 。
|
|
4658
|
+
0x300C: "\uFF62", // 「 「
|
|
4659
|
+
0x300D: "\uFF63", // 」 」
|
|
4660
|
+
0x309B: "\uFF9E", // ゛ ゙
|
|
4661
|
+
0x309C: "\uFF9F", // ゜ ゚
|
|
4662
|
+
0x30A1: "\uFF67", // ァ ァ
|
|
4663
|
+
0x30A2: "\uFF71", // ア ア
|
|
4664
|
+
0x30A3: "\uFF68", // ィ ィ
|
|
4665
|
+
0x30A4: "\uFF72", // イ イ
|
|
4666
|
+
0x30A5: "\uFF69", // ゥ ゥ
|
|
4667
|
+
0x30A6: "\uFF73", // ウ ウ
|
|
4668
|
+
0x30A7: "\uFF6A", // ェ ェ
|
|
4669
|
+
0x30A8: "\uFF74", // エ エ
|
|
4670
|
+
0x30A9: "\uFF6B", // ォ ォ
|
|
4671
|
+
0x30AA: "\uFF75", // オ オ
|
|
4672
|
+
0x30AB: "\uFF76", // カ カ
|
|
4673
|
+
0x30AC: "\uFF76\uFF9E", // ガ ガ
|
|
4674
|
+
0x30AD: "\uFF77", // キ キ
|
|
4675
|
+
0x30AE: "\uFF77\uFF9E", // ギ ギ
|
|
4676
|
+
0x30AF: "\uFF78", // ク ク
|
|
4677
|
+
0x30B0: "\uFF78\uFF9E", // グ グ
|
|
4678
|
+
0x30B1: "\uFF79", // ケ ケ
|
|
4679
|
+
0x30B2: "\uFF79\uFF9E", // ゲ ゲ
|
|
4680
|
+
0x30B3: "\uFF7A", // コ コ
|
|
4681
|
+
0x30B4: "\uFF7A\uFF9E", // ゴ ゴ
|
|
4682
|
+
0x30B5: "\uFF7B", // サ サ
|
|
4683
|
+
0x30B6: "\uFF7B\uFF9E", // ザ ザ
|
|
4684
|
+
0x30B7: "\uFF7C", // シ シ
|
|
4685
|
+
0x30B8: "\uFF7C\uFF9E", // ジ ジ
|
|
4686
|
+
0x30B9: "\uFF7D", // ス ス
|
|
4687
|
+
0x30BA: "\uFF7D\uFF9E", // ズ ズ
|
|
4688
|
+
0x30BB: "\uFF7E", // セ セ
|
|
4689
|
+
0x30BC: "\uFF7E\uFF9E", // ゼ ゼ
|
|
4690
|
+
0x30BD: "\uFF7F", // ソ ソ
|
|
4691
|
+
0x30BE: "\uFF7F\uFF9E", // ゾ ゾ
|
|
4692
|
+
0x30BF: "\uFF80", // タ タ
|
|
4693
|
+
0x30C0: "\uFF80\uFF9E", // ダ ダ
|
|
4694
|
+
0x30C1: "\uFF81", // チ チ
|
|
4695
|
+
0x30C2: "\uFF81\uFF9E", // ヂ ヂ
|
|
4696
|
+
0x30C3: "\uFF6F", // ッ ッ
|
|
4697
|
+
0x30C4: "\uFF82", // ツ ツ
|
|
4698
|
+
0x30C5: "\uFF82\uFF9E", // ヅ ヅ
|
|
4699
|
+
0x30C6: "\uFF83", // テ テ
|
|
4700
|
+
0x30C7: "\uFF83\uFF9E", // デ デ
|
|
4701
|
+
0x30C8: "\uFF84", // ト ト
|
|
4702
|
+
0x30C9: "\uFF84\uFF9E", // ド ド
|
|
4703
|
+
0x30CA: "\uFF85", // ナ ナ
|
|
4704
|
+
0x30CB: "\uFF86", // ニ ニ
|
|
4705
|
+
0x30CC: "\uFF87", // ヌ ヌ
|
|
4706
|
+
0x30CD: "\uFF88", // ネ ネ
|
|
4707
|
+
0x30CE: "\uFF89", // ノ ノ
|
|
4708
|
+
0x30CF: "\uFF8A", // ハ ハ
|
|
4709
|
+
0x30D0: "\uFF8A\uFF9E", // バ バ
|
|
4710
|
+
0x30D1: "\uFF8A\uFF9F", // パ パ
|
|
4711
|
+
0x30D2: "\uFF8B", // ヒ ヒ
|
|
4712
|
+
0x30D3: "\uFF8B\uFF9E", // ビ ビ
|
|
4713
|
+
0x30D4: "\uFF8B\uFF9F", // ピ ピ
|
|
4714
|
+
0x30D5: "\uFF8C", // フ フ
|
|
4715
|
+
0x30D6: "\uFF8C\uFF9E", // ブ ブ
|
|
4716
|
+
0x30D7: "\uFF8C\uFF9F", // プ プ
|
|
4717
|
+
0x30D8: "\uFF8D", // ヘ ヘ
|
|
4718
|
+
0x30D9: "\uFF8D\uFF9E", // ベ ベ
|
|
4719
|
+
0x30DA: "\uFF8D\uFF9F", // ペ ペ
|
|
4720
|
+
0x30DB: "\uFF8E", // ホ ホ
|
|
4721
|
+
0x30DC: "\uFF8E\uFF9E", // ボ ボ
|
|
4722
|
+
0x30DD: "\uFF8E\uFF9F", // ポ ポ
|
|
4723
|
+
0x30DE: "\uFF8F", // マ マ
|
|
4724
|
+
0x30DF: "\uFF90", // ミ ミ
|
|
4725
|
+
0x30E0: "\uFF91", // ム ム
|
|
4726
|
+
0x30E1: "\uFF92", // メ メ
|
|
4727
|
+
0x30E2: "\uFF93", // モ モ
|
|
4728
|
+
0x30E3: "\uFF6C", // ャ ャ
|
|
4729
|
+
0x30E4: "\uFF94", // ヤ ヤ
|
|
4730
|
+
0x30E5: "\uFF6D", // ュ ュ
|
|
4731
|
+
0x30E6: "\uFF95", // ユ ユ
|
|
4732
|
+
0x30E7: "\uFF6E", // ョ ョ
|
|
4733
|
+
0x30E8: "\uFF96", // ヨ ヨ
|
|
4734
|
+
0x30E9: "\uFF97", // ラ ラ
|
|
4735
|
+
0x30EA: "\uFF98", // リ リ
|
|
4736
|
+
0x30EB: "\uFF99", // ル ル
|
|
4737
|
+
0x30EC: "\uFF9A", // レ レ
|
|
4738
|
+
0x30ED: "\uFF9B", // ロ ロ
|
|
4739
|
+
0x30EE: "\uFF9C", // ヮ ワ
|
|
4740
|
+
0x30EF: "\uFF9C", // ワ ワ
|
|
4741
|
+
0x30F0: "\uFF72", // ヰ イ
|
|
4742
|
+
0x30F1: "\uFF74", // ヱ エ
|
|
4743
|
+
0x30F2: "\uFF66", // ヲ ヲ
|
|
4744
|
+
0x30F3: "\uFF9D", // ン ン
|
|
4745
|
+
0x30F4: "\uFF73\uFF9E", // ヴ ヴ
|
|
4746
|
+
0x30F5: "\uFF76", // ヵ カ
|
|
4747
|
+
0x30F6: "\uFF79", // ヶ ケ
|
|
4748
|
+
0x30F7: "\uFF9C\uFF9E", // ヷ ヷ
|
|
4749
|
+
0x30F8: "\uFF72\uFF9E", // ヸ イ゙
|
|
4750
|
+
0x30F9: "\uFF74\uFF9E", // ヹ エ゙
|
|
4751
|
+
0x30FA: "\uFF66\uFF9E", // ヺ ヺ
|
|
4752
|
+
0x30FB: "\uFF65", // ・ ・
|
|
4753
|
+
0x30FC: "\uFF70" // ー ー
|
|
4754
|
+
};
|
|
4755
|
+
/**
|
|
4756
|
+
* @param {string} ch
|
|
4757
|
+
*/
|
|
4758
|
+
const func = function (ch) {
|
|
4759
|
+
if (ch.length === 1) {
|
|
4760
|
+
return map[ch.charCodeAt(0)];
|
|
4761
|
+
} else {
|
|
4762
|
+
return map[ch.charCodeAt(0)] + map[ch.charCodeAt(1)];
|
|
4763
|
+
}
|
|
4764
|
+
};
|
|
4765
|
+
return text.replace(/[\u3001\u3002\u300C\u300D\u309B\u309C\u30A1-\u30FC][\u309B\u309C]?/g, func);
|
|
4766
|
+
}
|
|
4767
|
+
|
|
4768
|
+
/**
|
|
4769
|
+
* カタカナを全角に変換
|
|
4770
|
+
* @param {string} text - 変換したいテキスト
|
|
4771
|
+
* @returns {string} 変換後のテキスト
|
|
4772
|
+
*/
|
|
4773
|
+
static toFullWidthKana(text) {
|
|
4774
|
+
/**
|
|
4775
|
+
* @type {Record<number, number>}
|
|
4776
|
+
*/
|
|
4777
|
+
// prettier-ignore
|
|
4778
|
+
const map = {
|
|
4779
|
+
0xFF61: 0x3002, // 。 。
|
|
4780
|
+
0xFF62: 0x300C, // 「 「
|
|
4781
|
+
0xFF63: 0x300D, // 」 」
|
|
4782
|
+
0xFF64: 0x3001, // 、
|
|
4783
|
+
0xFF65: 0x30FB, // ・ ・
|
|
4784
|
+
0xFF66: 0x30F2, // ヲ ヲ
|
|
4785
|
+
0xFF67: 0x30A1, // ァ ァ
|
|
4786
|
+
0xFF68: 0x30A3, // ィ ィ
|
|
4787
|
+
0xFF69: 0x30A5, // ゥ ゥ
|
|
4788
|
+
0xFF6A: 0x30A7, // ェ ェ
|
|
4789
|
+
0xFF6B: 0x30A9, // ォ ォ
|
|
4790
|
+
0xFF6C: 0x30E3, // ャ ャ
|
|
4791
|
+
0xFF6D: 0x30E5, // ュ ュ
|
|
4792
|
+
0xFF6E: 0x30E7, // ョ ョ
|
|
4793
|
+
0xFF6F: 0x30C3, // ッ ッ
|
|
4794
|
+
0xFF70: 0x30FC, // ー ー
|
|
4795
|
+
0xFF71: 0x30A2, // ア ア
|
|
4796
|
+
0xFF72: 0x30A4, // イ イ
|
|
4797
|
+
0xFF73: 0x30A6, // ウ ウ
|
|
4798
|
+
0xFF74: 0x30A8, // エ エ
|
|
4799
|
+
0xFF75: 0x30AA, // オ オ
|
|
4800
|
+
0xFF76: 0x30AB, // カ カ
|
|
4801
|
+
0xFF77: 0x30AD, // キ キ
|
|
4802
|
+
0xFF78: 0x30AF, // ク ク
|
|
4803
|
+
0xFF79: 0x30B1, // ケ ケ
|
|
4804
|
+
0xFF7A: 0x30B3, // コ コ
|
|
4805
|
+
0xFF7B: 0x30B5, // サ サ
|
|
4806
|
+
0xFF7C: 0x30B7, // シ シ
|
|
4807
|
+
0xFF7D: 0x30B9, // ス ス
|
|
4808
|
+
0xFF7E: 0x30BB, // セ セ
|
|
4809
|
+
0xFF7F: 0x30BD, // ソ ソ
|
|
4810
|
+
0xFF80: 0x30BF, // タ タ
|
|
4811
|
+
0xFF81: 0x30C1, // チ チ
|
|
4812
|
+
0xFF82: 0x30C4, // ツ ツ
|
|
4813
|
+
0xFF83: 0x30C6, // テ テ
|
|
4814
|
+
0xFF84: 0x30C8, // ト ト
|
|
4815
|
+
0xFF85: 0x30CA, // ナ ナ
|
|
4816
|
+
0xFF86: 0x30CB, // ニ ニ
|
|
4817
|
+
0xFF87: 0x30CC, // ヌ ヌ
|
|
4818
|
+
0xFF88: 0x30CD, // ネ ネ
|
|
4819
|
+
0xFF89: 0x30CE, // ノ ノ
|
|
4820
|
+
0xFF8A: 0x30CF, // ハ ハ
|
|
4821
|
+
0xFF8B: 0x30D2, // ヒ ヒ
|
|
4822
|
+
0xFF8C: 0x30D5, // フ フ
|
|
4823
|
+
0xFF8D: 0x30D8, // ヘ ヘ
|
|
4824
|
+
0xFF8E: 0x30DB, // ホ ホ
|
|
4825
|
+
0xFF8F: 0x30DE, // マ マ
|
|
4826
|
+
0xFF90: 0x30DF, // ミ ミ
|
|
4827
|
+
0xFF91: 0x30E0, // ム ム
|
|
4828
|
+
0xFF92: 0x30E1, // メ メ
|
|
4829
|
+
0xFF93: 0x30E2, // モ モ
|
|
4830
|
+
0xFF94: 0x30E4, // ヤ ヤ
|
|
4831
|
+
0xFF95: 0x30E6, // ユ ユ
|
|
4832
|
+
0xFF96: 0x30E8, // ヨ ヨ
|
|
4833
|
+
0xFF97: 0x30E9, // ラ ラ
|
|
4834
|
+
0xFF98: 0x30EA, // リ リ
|
|
4835
|
+
0xFF99: 0x30EB, // ル ル
|
|
4836
|
+
0xFF9A: 0x30EC, // レ レ
|
|
4837
|
+
0xFF9B: 0x30ED, // ロ ロ
|
|
4838
|
+
0xFF9C: 0x30EF, // ワ ワ
|
|
4839
|
+
0xFF9D: 0x30F3, // ン ン
|
|
4840
|
+
0xFF9E: 0x309B, // ゛ ゙
|
|
4841
|
+
0xFF9F: 0x309C // ゜ ゚
|
|
4842
|
+
};
|
|
4843
|
+
/**
|
|
4844
|
+
* @param {string} str
|
|
4845
|
+
*/
|
|
4846
|
+
const func = function (str) {
|
|
4847
|
+
if (str.length === 1) {
|
|
4848
|
+
return String.fromCharCode(map[str.charCodeAt(0)]);
|
|
4849
|
+
} else {
|
|
4850
|
+
const next = str.charCodeAt(1);
|
|
4851
|
+
const ch = str.charCodeAt(0);
|
|
4852
|
+
if (next === 0xFF9E) {
|
|
4853
|
+
// Shift-JISにない濁点(ヷ、ヸ、ヹ、ヺ)は意図的に無視
|
|
4854
|
+
if (ch === 0xFF73) {
|
|
4855
|
+
// ヴ
|
|
4856
|
+
return String.fromCharCode(0x3094);
|
|
4857
|
+
} else if ((0xFF76 <= ch && ch <= 0xFF84) || (0xFF8A <= ch && ch <= 0xFF8E)) {
|
|
4858
|
+
// ガ-ド、バ-ボ
|
|
4859
|
+
return String.fromCharCode(map[ch] + 1);
|
|
4860
|
+
}
|
|
4861
|
+
} else if (next === 0xFF9F) {
|
|
4862
|
+
// 半濁点
|
|
4863
|
+
if (0xFF8A <= ch && ch <= 0xFF8E) {
|
|
4864
|
+
// パ-ポ
|
|
4865
|
+
return String.fromCharCode(map[ch] + 2);
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
return String.fromCharCode(map[ch]) + String.fromCharCode(map[next]);
|
|
4869
|
+
}
|
|
4870
|
+
};
|
|
4871
|
+
return text.replace(/[\uFF61-\uFF9F][\uFF9E\uFF9F]?/g, func);
|
|
4872
|
+
}
|
|
4873
|
+
|
|
4874
|
+
/**
|
|
4875
|
+
* 半角に変換
|
|
4876
|
+
* @param {string} text - 変換したいテキスト
|
|
4877
|
+
* @returns {string} 変換後のテキスト
|
|
4878
|
+
*/
|
|
4879
|
+
static toHalfWidth(text) {
|
|
4880
|
+
return Japanese.toHalfWidthKana(Japanese.toHalfWidthAsciiCode(text));
|
|
4881
|
+
}
|
|
4882
|
+
|
|
4883
|
+
/**
|
|
4884
|
+
* 全角に変換
|
|
4885
|
+
* @param {string} text - 変換したいテキスト
|
|
4886
|
+
* @returns {string} 変換後のテキスト
|
|
4887
|
+
*/
|
|
4888
|
+
static toFullWidth(text) {
|
|
4889
|
+
return Japanese.toFullWidthKana(Japanese.toFullWidthAsciiCode(text));
|
|
4890
|
+
}
|
|
4891
|
+
|
|
4892
|
+
/**
|
|
4893
|
+
* 指定したコードポイントの横幅を推定して取得します
|
|
4894
|
+
* - 0幅 ... グラフェムを構成する要素
|
|
4895
|
+
* (結合文字, 異体字セレクタ, スキントーン修飾子,
|
|
4896
|
+
* Tag Sequence 構成文字, ZWSP, ZWNJ, ZWJ, WJ)
|
|
4897
|
+
* - 1幅 ... ASCII文字, 半角カタカナ, Regional Indicator(単体)
|
|
4898
|
+
* - 2幅 ... 上記以外
|
|
4899
|
+
* @param {number} cp1 調査するコードポイント
|
|
4900
|
+
* @param {number} [cp2] 調査するコードポイント
|
|
4901
|
+
* @returns {number} 文字の横幅
|
|
4902
|
+
*/
|
|
4903
|
+
static getWidthFromCodePoint(cp1, cp2) {
|
|
4904
|
+
if (cp2 !== undefined) {
|
|
4905
|
+
if (Unicode.isRegionalIndicatorContinuation(cp1, cp2)) {
|
|
4906
|
+
return 2;
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4909
|
+
if (Unicode.isGraphemeComponentFromCodePoint(cp1) || Unicode.isZeroWidthCharacterFromCodePoint(cp1)) {
|
|
4910
|
+
return 0;
|
|
4911
|
+
// prettier-ignore
|
|
4912
|
+
} else if (cp1 < 0x80 || (0xFF61 <= cp1 && cp1 < 0xFFA0) || Unicode.isRegionalIndicatorFromCodePoint(cp1)) {
|
|
4913
|
+
return 1;
|
|
4914
|
+
} else {
|
|
4915
|
+
return 2;
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
|
|
4919
|
+
/**
|
|
4920
|
+
* 指定したテキストの横幅を半角/全角でカウント
|
|
4921
|
+
* - 0幅 ... グラフェムを構成する要素
|
|
4922
|
+
* (結合文字, 異体字セレクタ, スキントーン修飾子,
|
|
4923
|
+
* Tag Sequence 構成文字, ZWSP, ZWNJ, ZWJ, WJ)
|
|
4924
|
+
* - 1幅 ... ASCII文字, 半角カタカナ, Regional Indicator(単体)
|
|
4925
|
+
* - 2幅 ... 上記以外
|
|
4926
|
+
* @param {string} text - カウントしたいテキスト
|
|
4927
|
+
* @returns {number} 文字の横幅
|
|
4928
|
+
*/
|
|
4929
|
+
static getWidth(text) {
|
|
4930
|
+
const utf32_array = Unicode.toUTF32Array(text);
|
|
4931
|
+
let count = 0;
|
|
4932
|
+
let isZWJ = false;
|
|
4933
|
+
for (let i = 0; i < utf32_array.length; i++) {
|
|
4934
|
+
const cp = utf32_array[i];
|
|
4935
|
+
// 国旗 (Regional Indicator)
|
|
4936
|
+
if (i < utf32_array.length - 1) {
|
|
4937
|
+
const next = utf32_array[i + 1];
|
|
4938
|
+
if (Unicode.isRegionalIndicatorContinuation(cp, next)) {
|
|
4939
|
+
if (!isZWJ) {
|
|
4940
|
+
count += Japanese.getWidthFromCodePoint(cp, next);
|
|
4941
|
+
}
|
|
4942
|
+
i++;
|
|
4943
|
+
isZWJ = false;
|
|
4944
|
+
continue;
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
if (!isZWJ) {
|
|
4948
|
+
count += Japanese.getWidthFromCodePoint(cp);
|
|
4949
|
+
}
|
|
4950
|
+
// prettier-ignore
|
|
4951
|
+
isZWJ = cp === 0x200D;
|
|
4952
|
+
}
|
|
4953
|
+
return count;
|
|
4954
|
+
}
|
|
4955
|
+
|
|
4956
|
+
/**
|
|
4957
|
+
* 文字幅を考慮して文字列を文字の配列に変換する
|
|
4958
|
+
* @param {string} text - 変換したいテキスト
|
|
4959
|
+
* @returns {number[][]} UTF32(コードポイント)の配列が入った配列
|
|
4960
|
+
*/
|
|
4961
|
+
static toMojiArrayFromString(text) {
|
|
4962
|
+
const utf32_array = Unicode.toUTF32Array(text);
|
|
4963
|
+
|
|
4964
|
+
/** @type {number[][]} */
|
|
4965
|
+
const moji_array = [];
|
|
4966
|
+
|
|
4967
|
+
/** @type {number[]} */
|
|
4968
|
+
let moji = [];
|
|
4969
|
+
|
|
4970
|
+
let isZWJ = false;
|
|
4971
|
+
|
|
4972
|
+
for (let i = 0; i < utf32_array.length; i++) {
|
|
4973
|
+
const cp = utf32_array[i];
|
|
4974
|
+
|
|
4975
|
+
// --- 国旗 (Regional Indicator) は2つで1グラフェム ---
|
|
4976
|
+
if (i < utf32_array.length - 1) {
|
|
4977
|
+
const next = utf32_array[i + 1];
|
|
4978
|
+
if (Unicode.isRegionalIndicatorContinuation(cp, next)) {
|
|
4979
|
+
// 前のグラフェムを確定
|
|
4980
|
+
if (moji.length > 0) {
|
|
4981
|
+
moji_array.push(moji);
|
|
4982
|
+
}
|
|
4983
|
+
// RIペアで新しいグラフェムを作る
|
|
4984
|
+
moji = [cp, next];
|
|
4985
|
+
|
|
4986
|
+
moji_array.push(moji);
|
|
4987
|
+
moji = []; // 次のグラフェムに備える
|
|
4988
|
+
|
|
4989
|
+
i++; // 2つ目のRIを消費
|
|
4990
|
+
isZWJ = false;
|
|
4991
|
+
continue;
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
|
|
4995
|
+
// --- 新しいグラフェム開始判定 ---
|
|
4996
|
+
// 「ZWJ直後」または「グラフェム構成要素」は前に結合させる
|
|
4997
|
+
const isComponent = Unicode.isGraphemeComponentFromCodePoint(cp);
|
|
4998
|
+
|
|
4999
|
+
if (!isZWJ && !isComponent) {
|
|
5000
|
+
// ベース文字が来たので、前のグラフェムを確定して新しく開始
|
|
5001
|
+
if (moji.length > 0) {
|
|
5002
|
+
moji_array.push(moji);
|
|
5003
|
+
}
|
|
5004
|
+
moji = [];
|
|
5005
|
+
}
|
|
5006
|
+
|
|
5007
|
+
moji.push(cp);
|
|
5008
|
+
|
|
5009
|
+
// 次ループ用:ZWJ は次の文字とグラフェムを結合するため、新しい境界を作らないフラグを立てる
|
|
5010
|
+
isZWJ = (cp === 0x200D);
|
|
5011
|
+
}
|
|
5012
|
+
|
|
5013
|
+
// 末尾が残っていれば追加
|
|
5014
|
+
if (moji.length > 0) {
|
|
5015
|
+
moji_array.push(moji);
|
|
5016
|
+
}
|
|
5017
|
+
|
|
5018
|
+
return moji_array;
|
|
5019
|
+
}
|
|
5020
|
+
|
|
5021
|
+
/**
|
|
5022
|
+
* 結合した文字を考慮して文字の配列を文字列に変換する
|
|
5023
|
+
* @param {number[][]} mojiarray - UTF32(コードポイント)の配列が入った配列
|
|
5024
|
+
* @returns {string} UTF32(コードポイント)の配列が入った配列
|
|
5025
|
+
*/
|
|
5026
|
+
static toStringFromMojiArray(mojiarray) {
|
|
5027
|
+
/**
|
|
5028
|
+
* @type {number[]}
|
|
5029
|
+
*/
|
|
5030
|
+
const utf32 = [];
|
|
5031
|
+
for (let i = 0; i < mojiarray.length; i++) {
|
|
5032
|
+
for (let j = 0; j < mojiarray[i].length; j++) {
|
|
5033
|
+
utf32.push(mojiarray[i][j]);
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
return Unicode.fromUTF32Array(utf32);
|
|
5037
|
+
}
|
|
5038
|
+
|
|
5039
|
+
/**
|
|
5040
|
+
* 指定したテキストの横幅を半角/全角で換算した場合の切り出し
|
|
5041
|
+
* - 0幅 ... グラフェムを構成する要素
|
|
5042
|
+
* (結合文字, 異体字セレクタ, スキントーン修飾子,
|
|
5043
|
+
* Tag Sequence 構成文字, ZWSP, ZWNJ, ZWJ, WJ)
|
|
5044
|
+
* - 1幅 ... ASCII文字, 半角カタカナ, Regional Indicator(単体)
|
|
5045
|
+
* - 2幅 ... 上記以外
|
|
5046
|
+
* @param {string} text - 切り出したいテキスト
|
|
5047
|
+
* @param {number} offset - 切り出し位置
|
|
5048
|
+
* @param {number} size - 切り出す長さ
|
|
5049
|
+
* @returns {string} 切り出したテキスト
|
|
5050
|
+
* @ignore
|
|
5051
|
+
*/
|
|
5052
|
+
static cutTextForWidth(text, offset, size) {
|
|
5053
|
+
const moji_array = Japanese.toMojiArrayFromString(text);
|
|
5054
|
+
const SPACE = [0x20]; // ' '
|
|
5055
|
+
/**
|
|
5056
|
+
* @type {number[][]}
|
|
5057
|
+
*/
|
|
5058
|
+
const output = [];
|
|
5059
|
+
let is_target = false;
|
|
5060
|
+
let position = 0;
|
|
5061
|
+
let cut_size = size;
|
|
5062
|
+
if (offset < 0) {
|
|
5063
|
+
cut_size += offset;
|
|
5064
|
+
offset = 0;
|
|
5065
|
+
}
|
|
5066
|
+
if (cut_size <= 0) {
|
|
5067
|
+
return "";
|
|
5068
|
+
}
|
|
5069
|
+
for (let i = 0; i < moji_array.length; i++) {
|
|
5070
|
+
// 文字データ
|
|
5071
|
+
const moji = moji_array[i];
|
|
5072
|
+
// 1文字目の横幅を取得
|
|
5073
|
+
const cp = moji[0];
|
|
5074
|
+
// ASCII文字, 半角カタカナ, Regional Indicator(単体)
|
|
5075
|
+
// prettier-ignore
|
|
5076
|
+
const cp_size = cp < 0x80
|
|
5077
|
+
|| (0xFF61 <= cp && cp < 0xFFA0)
|
|
5078
|
+
|| (moji.length === 1 && Unicode.isRegionalIndicatorFromCodePoint(cp)) ? 1 : 2;
|
|
5079
|
+
if (position >= offset) {
|
|
5080
|
+
is_target = true;
|
|
5081
|
+
if (cut_size >= cp_size) {
|
|
5082
|
+
output.push(moji);
|
|
5083
|
+
} else {
|
|
5084
|
+
output.push(SPACE);
|
|
5085
|
+
}
|
|
5086
|
+
cut_size -= cp_size;
|
|
5087
|
+
if (cut_size <= 0) {
|
|
5088
|
+
break;
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
position += cp_size;
|
|
5092
|
+
// 2バイト文字の途中をoffset指定していた場合になる。
|
|
5093
|
+
if (position - 1 >= offset && !is_target) {
|
|
5094
|
+
cut_size--;
|
|
5095
|
+
output.push(SPACE);
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
return Japanese.toStringFromMojiArray(output);
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
|
|
5102
|
+
/**
|
|
5103
|
+
* The script is part of Mojix for TextInputGuard.
|
|
5104
|
+
*
|
|
5105
|
+
* AUTHOR:
|
|
5106
|
+
* natade-jp (https://github.com/natade-jp)
|
|
5107
|
+
*
|
|
5108
|
+
* LICENSE:
|
|
5109
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
5110
|
+
*/
|
|
5111
|
+
|
|
5112
|
+
|
|
5113
|
+
/**
|
|
5114
|
+
* 文字のエンコード情報
|
|
5115
|
+
* @typedef {Object} MojiEncodeData
|
|
5116
|
+
* @property {MenKuTen} kuten 区点 コード
|
|
5117
|
+
* @property {number} cp932_code CP932(Windows-31J) コード
|
|
5118
|
+
* @property {number[]} utf8_array UTF-8 配列
|
|
5119
|
+
* @property {number[]} utf16_array UTF-16 配列
|
|
5120
|
+
* @property {number[]} utf32_array UTF-32 配列
|
|
5121
|
+
* @property {number[]} cp932_array CP932(Windows-31J) バイト配列
|
|
5122
|
+
* @property {number[]} shift_jis_array Shift_JIS バイト配列
|
|
5123
|
+
* @property {number[]} iso2022jp_array ISO-2022-JP バイト配列
|
|
5124
|
+
*/
|
|
5125
|
+
|
|
5126
|
+
/**
|
|
5127
|
+
* 文字の種別情報
|
|
5128
|
+
* @typedef {Object} MojiTypeData
|
|
5129
|
+
* @property {boolean} is_regular_sjis Shift_JIS に登録された文字
|
|
5130
|
+
* @property {boolean} is_gaiji_cp932 Windows-31J(CP932) 外字
|
|
5131
|
+
* @property {boolean} is_IBM_extended_character Windows-31J(CP932) IBM拡張文字
|
|
5132
|
+
* @property {boolean} is_NEC_selection_IBM_extended_character Windows-31J(CP932) NEC選定IBM拡張文字
|
|
5133
|
+
* @property {boolean} is_NEC_special_character Windows-31J(CP932) NEC特殊文字
|
|
5134
|
+
* @property {number} kanji_suijun Shift_JIS-2004 を使用して漢字の水準調査(計算不可の場合 0)
|
|
5135
|
+
* @property {boolean} is_surrogate_pair 要 Unicode サロゲートペア
|
|
5136
|
+
* @property {string|null} control_name 制御文字名(制御文字ではない場合は null)
|
|
5137
|
+
* @property {boolean} is_control_character 制御文字
|
|
5138
|
+
* @property {string} blockname Unicodeブロック名
|
|
5139
|
+
* @property {boolean} is_kanji 漢字
|
|
5140
|
+
* @property {boolean} is_hiragana ひらがな
|
|
5141
|
+
* @property {boolean} is_katakana カタカナ
|
|
5142
|
+
* @property {boolean} is_fullwidth_ascii 全角ASCII
|
|
5143
|
+
* @property {boolean} is_halfwidth_katakana 半角カタカナ
|
|
5144
|
+
* @property {boolean} is_emoji 絵文字(絵文字表示されることが多い Unicode ブロックに属する文字)
|
|
5145
|
+
* @property {boolean} is_emoticons 顔文字(Emoticons ブロックに属する文字)
|
|
5146
|
+
* @property {boolean} is_symbol_base 記号(テキスト記号の定義だがVS16が続くと絵文字に切り替えが発生)
|
|
5147
|
+
* @property {boolean} is_gaiji 外字
|
|
5148
|
+
* @property {boolean} is_grapheme_component グラフェムを構成するための文字
|
|
5149
|
+
* @property {boolean} is_zero_width_character ゼロ幅文字
|
|
5150
|
+
* @property {boolean} is_combining_mark 結合文字
|
|
5151
|
+
* @property {boolean} is_variation_selector 異体字セレクタ
|
|
5152
|
+
* @property {boolean} is_skin_tone_modifier スキントーン修飾子
|
|
5153
|
+
* @property {boolean} is_tag_character タグ文字
|
|
5154
|
+
* @property {boolean} is_regional_indicator 国旗絵文字を構成するための Regional Indicator 文字(2文字で1つの国旗になる)
|
|
5155
|
+
*/
|
|
5156
|
+
|
|
5157
|
+
/**
|
|
5158
|
+
* 文字の種別情報
|
|
5159
|
+
* @typedef {Object} MojiData
|
|
5160
|
+
* @property {MojiEncodeData} encode 文字のエンコード情報
|
|
5161
|
+
* @property {MojiTypeData} type 文字の種別情報
|
|
5162
|
+
* @property {string} character 解析した文字
|
|
5163
|
+
* @property {number} codepoint 解析した文字のコードポイント
|
|
5164
|
+
*/
|
|
5165
|
+
|
|
5166
|
+
/**
|
|
5167
|
+
* 文字の解析用クラス
|
|
5168
|
+
* @ignore
|
|
5169
|
+
*/
|
|
5170
|
+
class MojiAnalyzer {
|
|
5171
|
+
/**
|
|
5172
|
+
* 初期化
|
|
5173
|
+
* @returns {MojiData}
|
|
5174
|
+
* @ignore
|
|
5175
|
+
*/
|
|
5176
|
+
static _createMojiData() {
|
|
5177
|
+
/**
|
|
5178
|
+
* @type {MojiEncodeData}
|
|
5179
|
+
*/
|
|
5180
|
+
const encode = {
|
|
5181
|
+
kuten: null,
|
|
5182
|
+
cp932_code: 0,
|
|
5183
|
+
utf8_array: [],
|
|
5184
|
+
utf16_array: [],
|
|
5185
|
+
utf32_array: [],
|
|
5186
|
+
cp932_array: [],
|
|
5187
|
+
shift_jis_array: [],
|
|
5188
|
+
iso2022jp_array: []
|
|
5189
|
+
};
|
|
5190
|
+
|
|
5191
|
+
/**
|
|
5192
|
+
* @type {MojiTypeData}
|
|
5193
|
+
*/
|
|
5194
|
+
const type = {
|
|
5195
|
+
is_regular_sjis: false,
|
|
5196
|
+
is_gaiji_cp932: false,
|
|
5197
|
+
is_IBM_extended_character: false,
|
|
5198
|
+
is_NEC_selection_IBM_extended_character: false,
|
|
5199
|
+
is_NEC_special_character: false,
|
|
5200
|
+
kanji_suijun: 0,
|
|
5201
|
+
is_surrogate_pair: false,
|
|
5202
|
+
control_name: null,
|
|
5203
|
+
is_control_character: false,
|
|
5204
|
+
blockname: "",
|
|
5205
|
+
is_kanji: false,
|
|
5206
|
+
is_hiragana: false,
|
|
5207
|
+
is_katakana: false,
|
|
5208
|
+
is_fullwidth_ascii: false,
|
|
5209
|
+
is_halfwidth_katakana: false,
|
|
5210
|
+
is_emoji: false,
|
|
5211
|
+
is_emoticons: false,
|
|
5212
|
+
is_symbol_base: false,
|
|
5213
|
+
is_gaiji: false,
|
|
5214
|
+
is_grapheme_component: false,
|
|
5215
|
+
is_zero_width_character: false,
|
|
5216
|
+
is_combining_mark: false,
|
|
5217
|
+
is_variation_selector: false,
|
|
5218
|
+
is_skin_tone_modifier: false,
|
|
5219
|
+
is_tag_character: false,
|
|
5220
|
+
is_regional_indicator: false
|
|
5221
|
+
};
|
|
5222
|
+
|
|
5223
|
+
/**
|
|
5224
|
+
* @type {MojiData}
|
|
5225
|
+
*/
|
|
5226
|
+
const data = {
|
|
5227
|
+
encode: encode,
|
|
5228
|
+
type: type,
|
|
5229
|
+
character: null,
|
|
5230
|
+
codepoint: 0
|
|
5231
|
+
};
|
|
5232
|
+
|
|
5233
|
+
return data;
|
|
5234
|
+
}
|
|
5235
|
+
|
|
5236
|
+
/**
|
|
5237
|
+
* 指定した1つのUTF-32 コードポイントに関して、解析を行い情報を返します
|
|
5238
|
+
* @param {number} unicode_codepoint - UTF-32 のコードポイント
|
|
5239
|
+
* @returns {MojiData} 文字の情報がつまったオブジェクト
|
|
5240
|
+
*/
|
|
5241
|
+
static getMojiData(unicode_codepoint) {
|
|
5242
|
+
// 基本情報取得
|
|
5243
|
+
const cp932code = CP932.toCP932FromUnicode(unicode_codepoint);
|
|
5244
|
+
const kuten = SJIS.toKuTenFromSJISCode(cp932code);
|
|
5245
|
+
const is_regular_sjis = cp932code < 0x100 || SJIS.isRegularMenKuten(kuten);
|
|
5246
|
+
|
|
5247
|
+
/**
|
|
5248
|
+
* 出力データの箱を用意
|
|
5249
|
+
* @type {MojiData}
|
|
5250
|
+
*/
|
|
5251
|
+
const data = MojiAnalyzer._createMojiData();
|
|
5252
|
+
const encode = data.encode;
|
|
5253
|
+
const type = data.type;
|
|
5254
|
+
const character = Unicode.fromCodePoint(unicode_codepoint);
|
|
5255
|
+
data.character = character;
|
|
5256
|
+
data.codepoint = unicode_codepoint;
|
|
5257
|
+
|
|
5258
|
+
// 句点と面区点情報(ない場合はnullになる)
|
|
5259
|
+
encode.kuten = kuten;
|
|
5260
|
+
// コードの代入
|
|
5261
|
+
encode.cp932_code = cp932code ? cp932code : -1;
|
|
5262
|
+
|
|
5263
|
+
// Shift_JIS として許容されるか
|
|
5264
|
+
type.is_regular_sjis = is_regular_sjis;
|
|
5265
|
+
|
|
5266
|
+
// Windows-31J(CP932) に関しての調査
|
|
5267
|
+
// 外字, IBM拡張文字, NEC選定IBM拡張文字, NEC特殊文字
|
|
5268
|
+
// prettier-ignore
|
|
5269
|
+
type.is_gaiji_cp932 = cp932code ? 0xF040 <= cp932code && cp932code <= 0xF9FC : false;
|
|
5270
|
+
// prettier-ignore
|
|
5271
|
+
type.is_IBM_extended_character = cp932code ? 0xFA40 <= cp932code && cp932code <= 0xFC4B : false;
|
|
5272
|
+
// prettier-ignore
|
|
5273
|
+
type.is_NEC_selection_IBM_extended_character = cp932code ? 0xED40 <= cp932code && cp932code <= 0xEEFC : false;
|
|
5274
|
+
// prettier-ignore
|
|
5275
|
+
type.is_NEC_special_character = cp932code ? 0x8740 <= cp932code && cp932code <= 0x879C : false;
|
|
5276
|
+
|
|
5277
|
+
// Unicodeの配列
|
|
5278
|
+
encode.utf8_array = Unicode.toUTF8Array(data.character);
|
|
5279
|
+
encode.utf16_array = Unicode.toUTF16Array(data.character);
|
|
5280
|
+
encode.utf32_array = [unicode_codepoint];
|
|
5281
|
+
type.is_surrogate_pair = encode.utf16_array.length > 1;
|
|
5282
|
+
|
|
5283
|
+
// SJIS系の配列
|
|
5284
|
+
// prettier-ignore
|
|
5285
|
+
encode.cp932_array = cp932code ? (cp932code >= 0x100 ? [cp932code >> 8, cp932code & 0xFF] : [cp932code]) : [];
|
|
5286
|
+
|
|
5287
|
+
// ISO-2022-JP , EUC-JP
|
|
5288
|
+
// prettier-ignore
|
|
5289
|
+
if (cp932code < 0xE0 || is_regular_sjis) {
|
|
5290
|
+
// prettier-ignore
|
|
5291
|
+
if (cp932code < 0x80) {
|
|
5292
|
+
encode.shift_jis_array = [cp932code];
|
|
5293
|
+
encode.iso2022jp_array = [];
|
|
5294
|
+
// prettier-ignore
|
|
5295
|
+
} else if (cp932code < 0xE0) {
|
|
5296
|
+
// 半角カタカナの扱い
|
|
5297
|
+
encode.shift_jis_array = [cp932code];
|
|
5298
|
+
encode.iso2022jp_array = [];
|
|
5299
|
+
} else if (kuten.ku <= 94) {
|
|
5300
|
+
// 区点は94まで利用できる。
|
|
5301
|
+
// つまり、最大でも 94 + 0xA0 = 0xFE となり 0xFF 以上にならない
|
|
5302
|
+
encode.shift_jis_array = [encode.cp932_array[0], encode.cp932_array[1]];
|
|
5303
|
+
encode.iso2022jp_array = [kuten.ku + 0x20, kuten.ten + 0x20];
|
|
5304
|
+
}
|
|
5305
|
+
} else {
|
|
5306
|
+
encode.shift_jis_array = [];
|
|
5307
|
+
encode.iso2022jp_array = [];
|
|
5308
|
+
}
|
|
5309
|
+
// SJISとして正規でなければ強制エンコード失敗
|
|
5310
|
+
if (!is_regular_sjis) {
|
|
5311
|
+
encode.shift_jis_array = [];
|
|
5312
|
+
encode.iso2022jp_array = [];
|
|
5313
|
+
}
|
|
5314
|
+
|
|
5315
|
+
// 制御文字かどうか
|
|
5316
|
+
type.control_name = Unicode.toControlCharcterName(unicode_codepoint);
|
|
5317
|
+
type.is_control_character = type.control_name ? true : false;
|
|
5318
|
+
|
|
5319
|
+
// Unicodeのブロック名
|
|
5320
|
+
type.blockname = Unicode.toBlockNameFromUnicode(unicode_codepoint);
|
|
5321
|
+
// ブロック名から判断
|
|
5322
|
+
type.is_kanji = /Ideographs/.test(type.blockname);
|
|
5323
|
+
type.is_hiragana = /Hiragana/.test(type.blockname);
|
|
5324
|
+
type.is_katakana = /Katakana/.test(type.blockname);
|
|
5325
|
+
type.is_fullwidth_ascii = /[\u3000\uFF01-\uFF5E]/.test(data.character);
|
|
5326
|
+
type.is_halfwidth_katakana = /[\uFF61-\uFF9F]/.test(data.character);
|
|
5327
|
+
// 絵文字
|
|
5328
|
+
type.is_emoji = /Pictographs|Transport and Map Symbols/.test(type.blockname);
|
|
5329
|
+
// 顔文字
|
|
5330
|
+
type.is_emoticons = /Emoticons/.test(type.blockname);
|
|
5331
|
+
// 記号(VS16 が付くと絵文字化)
|
|
5332
|
+
type.is_symbol_base = /Dingbats|Miscellaneous Symbols/.test(type.blockname);
|
|
5333
|
+
// 外字
|
|
5334
|
+
type.is_gaiji = /Private Use Area/.test(type.blockname);
|
|
5335
|
+
// グラフェムを構成するための文字
|
|
5336
|
+
type.is_grapheme_component = Unicode.isGraphemeComponentFromCodePoint(unicode_codepoint);
|
|
5337
|
+
// 横幅が 0 の文字
|
|
5338
|
+
type.is_zero_width_character = Unicode.isZeroWidthCharacterFromCodePoint(unicode_codepoint);
|
|
5339
|
+
// 結合文字
|
|
5340
|
+
type.is_combining_mark = Unicode.isCombiningMarkFromCodePoint(unicode_codepoint);
|
|
5341
|
+
// 異体字セレクタ
|
|
5342
|
+
type.is_variation_selector = Unicode.isVariationSelectorFromCodePoint(unicode_codepoint);
|
|
5343
|
+
// スキントーン修飾子
|
|
5344
|
+
type.is_skin_tone_modifier = Unicode.isEmojiModifierFromCodePoint(unicode_codepoint);
|
|
5345
|
+
// タグ文字
|
|
5346
|
+
type.is_tag_character = Unicode.isTagCharacterFromCodePoint(unicode_codepoint);
|
|
5347
|
+
// 国旗絵文字を構成するためのRI文字
|
|
5348
|
+
type.is_regional_indicator = Unicode.isRegionalIndicatorFromCodePoint(unicode_codepoint);
|
|
5349
|
+
|
|
5350
|
+
return data;
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
|
|
5354
|
+
/**
|
|
5355
|
+
* The script is part of Mojix for TextInputGuard.
|
|
5356
|
+
*
|
|
5357
|
+
* AUTHOR:
|
|
5358
|
+
* natade-jp (https://github.com/natade-jp)
|
|
5359
|
+
*
|
|
5360
|
+
* LICENSE:
|
|
5361
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
5362
|
+
*/
|
|
5363
|
+
|
|
5364
|
+
|
|
5365
|
+
/**
|
|
5366
|
+
* 日本語を扱うための様々な機能を提供します
|
|
5367
|
+
*/
|
|
5368
|
+
class Mojix {
|
|
5369
|
+
// ---------------------------------
|
|
5370
|
+
// 文字列のエンコードとデコードを扱う関数
|
|
5371
|
+
// ---------------------------------
|
|
5372
|
+
|
|
5373
|
+
/**
|
|
5374
|
+
* 文字列からバイナリ配列にエンコードする
|
|
5375
|
+
* @param {string} text - 変換したいテキスト
|
|
5376
|
+
* @param {string} charset - キャラセット(UTF-8/16/32,Shift_JIS,Windows-31J)
|
|
5377
|
+
* @param {boolean} [is_with_bom=false] - BOMをつけるかどうか
|
|
5378
|
+
* @returns {number[]} バイナリ配列(失敗時はnull)
|
|
5379
|
+
*/
|
|
5380
|
+
static encode(text, charset, is_with_bom) {
|
|
5381
|
+
return Encode.encode(text, charset, is_with_bom);
|
|
5382
|
+
}
|
|
5383
|
+
|
|
5384
|
+
/**
|
|
5385
|
+
* バイナリ配列から文字列にデコードする
|
|
5386
|
+
* @param {number[]} binary - 変換したいバイナリ配列
|
|
5387
|
+
* @param {string} [charset="autodetect"] - キャラセット(UTF-8/16/32,Shift_JIS,Windows-31J)
|
|
5388
|
+
* @returns {string} 変換した文字列(失敗したらnull)
|
|
5389
|
+
*/
|
|
5390
|
+
static decode(binary, charset) {
|
|
5391
|
+
return Encode.decode(binary, charset);
|
|
5392
|
+
}
|
|
5393
|
+
|
|
5394
|
+
// ---------------------------------
|
|
5395
|
+
// Unicode を扱う関数群
|
|
5396
|
+
// ---------------------------------
|
|
5397
|
+
|
|
5398
|
+
/**
|
|
5399
|
+
* サロゲートペア対応のコードポイント取得
|
|
5400
|
+
* @param {string} text - 対象テキスト
|
|
5401
|
+
* @param {number} [index = 0] - インデックス
|
|
5402
|
+
* @returns {number} コードポイント
|
|
5403
|
+
*/
|
|
5404
|
+
static codePointAt(text, index) {
|
|
5405
|
+
return Unicode.codePointAt(text, index);
|
|
5406
|
+
}
|
|
5407
|
+
|
|
5408
|
+
/**
|
|
5409
|
+
* コードポイントの数値データを文字列に変換
|
|
5410
|
+
* @param {...(number|number[])} codepoint - 変換したいコードポイントの数値配列、又は数値を並べた可変引数
|
|
5411
|
+
* @returns {string} 変換後のテキスト
|
|
5412
|
+
*/
|
|
5413
|
+
static fromCodePoint(codepoint) {
|
|
5414
|
+
if (Array.isArray(codepoint)) {
|
|
5415
|
+
return Unicode.fromCodePoint(codepoint);
|
|
5416
|
+
} else {
|
|
5417
|
+
const codepoint_array = [];
|
|
5418
|
+
for (let i = 0; i < arguments.length; i++) {
|
|
5419
|
+
codepoint_array[i] = arguments[i];
|
|
5420
|
+
}
|
|
5421
|
+
return Unicode.fromCodePoint(codepoint_array);
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5424
|
+
|
|
5425
|
+
/**
|
|
5426
|
+
* コードポイント換算で文字列数をカウント
|
|
5427
|
+
* @param {string} text - 対象テキスト
|
|
5428
|
+
* @param {number} [beginIndex=0] - 最初のインデックス(省略可)
|
|
5429
|
+
* @param {number} [endIndex] - 最後のインデックス(ここは含めない)(省略可)
|
|
5430
|
+
* @returns {number} 文字数
|
|
5431
|
+
*/
|
|
5432
|
+
static codePointCount(text, beginIndex, endIndex) {
|
|
5433
|
+
return Unicode.codePointCount(text, beginIndex, endIndex);
|
|
5434
|
+
}
|
|
5435
|
+
|
|
5436
|
+
/**
|
|
5437
|
+
* 文字列をUTF32(コードポイント)の配列に変換
|
|
5438
|
+
* @param {string} text - 変換したいテキスト
|
|
5439
|
+
* @returns {number[]} UTF32(コードポイント)のデータが入った配列
|
|
5440
|
+
*/
|
|
5441
|
+
static toUTF32Array(text) {
|
|
5442
|
+
return Unicode.toUTF32Array(text);
|
|
5443
|
+
}
|
|
5444
|
+
|
|
5445
|
+
/**
|
|
5446
|
+
* UTF32の配列から文字列に変換
|
|
5447
|
+
* @param {number[]} utf32 - 変換したいテキスト
|
|
5448
|
+
* @returns {string} 変換後のテキスト
|
|
5449
|
+
*/
|
|
5450
|
+
static fromUTF32Array(utf32) {
|
|
5451
|
+
return Unicode.fromUTF32Array(utf32);
|
|
5452
|
+
}
|
|
5453
|
+
|
|
5454
|
+
/**
|
|
5455
|
+
* 文字列をUTF16の配列に変換
|
|
5456
|
+
* @param {string} text - 変換したいテキスト
|
|
5457
|
+
* @returns {number[]} UTF16のデータが入った配列
|
|
5458
|
+
*/
|
|
5459
|
+
static toUTF16Array(text) {
|
|
5460
|
+
return Unicode.toUTF16Array(text);
|
|
5461
|
+
}
|
|
5462
|
+
|
|
5463
|
+
/**
|
|
5464
|
+
* UTF16の配列から文字列に変換
|
|
5465
|
+
* @param {number[]} utf16 - 変換したいテキスト
|
|
5466
|
+
* @returns {string} 変換後のテキスト
|
|
5467
|
+
*/
|
|
5468
|
+
static fromUTF16Array(utf16) {
|
|
5469
|
+
return Unicode.fromUTF16Array(utf16);
|
|
5470
|
+
}
|
|
5471
|
+
|
|
5472
|
+
/**
|
|
5473
|
+
* 文字列をUTF8の配列に変換
|
|
5474
|
+
* @param {string} text - 変換したいテキスト
|
|
5475
|
+
* @returns {number[]} UTF8のデータが入った配列
|
|
5476
|
+
*/
|
|
5477
|
+
static toUTF8Array(text) {
|
|
5478
|
+
return Unicode.toUTF8Array(text);
|
|
5479
|
+
}
|
|
5480
|
+
|
|
5481
|
+
/**
|
|
5482
|
+
* UTF8の配列から文字列に変換
|
|
5483
|
+
* @param {number[]} utf8 - 変換したいテキスト
|
|
5484
|
+
* @returns {string} 変換後のテキスト
|
|
5485
|
+
*/
|
|
5486
|
+
static fromUTF8Array(utf8) {
|
|
5487
|
+
return Unicode.fromUTF8Array(utf8);
|
|
5488
|
+
}
|
|
5489
|
+
|
|
5490
|
+
// ---------------------------------
|
|
5491
|
+
// 文字を扱う関数群
|
|
5492
|
+
// ---------------------------------
|
|
5493
|
+
|
|
5494
|
+
/**
|
|
5495
|
+
* 結合した文字を考慮して文字列を文字の配列に変換する
|
|
5496
|
+
* @param {string} text - 変換したいテキスト
|
|
5497
|
+
* @returns {number[][]} UTF32(コードポイント)の配列が入った配列
|
|
5498
|
+
*/
|
|
5499
|
+
static toMojiArrayFromString(text) {
|
|
5500
|
+
return Japanese.toMojiArrayFromString(text);
|
|
5501
|
+
}
|
|
5502
|
+
|
|
5503
|
+
/**
|
|
5504
|
+
* 結合した文字を考慮して文字の配列を文字列に変換する
|
|
5505
|
+
* @param {number[][]} mojiarray - UTF32(コードポイント)の配列が入った配列
|
|
5506
|
+
* @returns {string} UTF32(コードポイント)の配列が入った配列
|
|
5507
|
+
*/
|
|
5508
|
+
static toStringFromMojiArray(mojiarray) {
|
|
5509
|
+
return Japanese.toStringFromMojiArray(mojiarray);
|
|
5510
|
+
}
|
|
5511
|
+
|
|
5512
|
+
// ---------------------------------
|
|
5513
|
+
// 切り出しを扱う関数群
|
|
5514
|
+
// ---------------------------------
|
|
5515
|
+
|
|
5516
|
+
/**
|
|
5517
|
+
* 指定したテキストを切り出す
|
|
5518
|
+
* - 単位はコードポイントの文字数
|
|
5519
|
+
* - 結合文字, 異体字セレクタ, スキントーン修飾子, タグ文字を考慮しません
|
|
5520
|
+
* @param {string} text - 切り出したいテキスト
|
|
5521
|
+
* @param {number} offset - 切り出し位置
|
|
5522
|
+
* @param {number} size - 切り出す長さ
|
|
5523
|
+
* @returns {string} 切り出したテキスト
|
|
5524
|
+
*/
|
|
5525
|
+
static cutTextForCodePoint(text, offset, size) {
|
|
5526
|
+
return Unicode.cutTextForCodePoint(text, offset, size);
|
|
5527
|
+
}
|
|
5528
|
+
|
|
5529
|
+
/**
|
|
5530
|
+
* 指定したテキストの横幅を半角/全角でカウント
|
|
5531
|
+
* - 0幅 ... グラフェムを構成する要素
|
|
5532
|
+
* (結合文字, 異体字セレクタ, スキントーン修飾子,
|
|
5533
|
+
* Tag Sequence 構成文字, ZWSP, ZWNJ, ZWJ, WJ)
|
|
5534
|
+
* - 1幅 ... ASCII文字, 半角カタカナ, Regional Indicator(単体)
|
|
5535
|
+
* - 2幅 ... 上記以外
|
|
5536
|
+
* @param {string} text - カウントしたいテキスト
|
|
5537
|
+
* @returns {number} 文字の横幅
|
|
5538
|
+
*/
|
|
5539
|
+
static getWidth(text) {
|
|
5540
|
+
return Japanese.getWidth(text);
|
|
5541
|
+
}
|
|
5542
|
+
|
|
5543
|
+
/**
|
|
5544
|
+
* 指定したテキストを切り出す
|
|
5545
|
+
* - 0幅 ... グラフェムを構成する要素
|
|
5546
|
+
* (結合文字, 異体字セレクタ, スキントーン修飾子,
|
|
5547
|
+
* Tag Sequence 構成文字, ZWSP, ZWNJ, ZWJ, WJ)
|
|
5548
|
+
* - 1幅 ... ASCII文字, 半角カタカナ, Regional Indicator(単体)
|
|
5549
|
+
* - 2幅 ... 上記以外
|
|
5550
|
+
* @param {string} text - 切り出したいテキスト
|
|
5551
|
+
* @param {number} offset - 切り出し位置
|
|
5552
|
+
* @param {number} size - 切り出す長さ
|
|
5553
|
+
* @returns {string} 切り出したテキスト
|
|
5554
|
+
*/
|
|
5555
|
+
static cutTextForWidth(text, offset, size) {
|
|
5556
|
+
return Japanese.cutTextForWidth(text, offset, size);
|
|
5557
|
+
}
|
|
5558
|
+
|
|
5559
|
+
// ---------------------------------
|
|
5560
|
+
// 面区点コードの変換用
|
|
5561
|
+
// ---------------------------------
|
|
5562
|
+
|
|
5563
|
+
/**
|
|
5564
|
+
* 指定した文字から Windows-31J 上の区点番号に変換
|
|
5565
|
+
* - 2文字以上を指定した場合は、1文字目のみを変換する
|
|
5566
|
+
* @param {string} text - 変換したいテキスト
|
|
5567
|
+
* @returns {MenKuTen} 区点番号(存在しない場合(1バイトのJISコードなど)はnullを返す)
|
|
5568
|
+
*/
|
|
5569
|
+
static toKuTen(text) {
|
|
5570
|
+
return CP932.toKuTen(text);
|
|
5571
|
+
}
|
|
5572
|
+
|
|
5573
|
+
// ---------------------------------
|
|
5574
|
+
// 日本語の変換用の関数群
|
|
5575
|
+
// ---------------------------------
|
|
5576
|
+
|
|
5577
|
+
/**
|
|
5578
|
+
* カタカナをひらがなに変換
|
|
5579
|
+
* @param {string} text - 変換したいテキスト
|
|
5580
|
+
* @returns {string} 変換後のテキスト
|
|
5581
|
+
*/
|
|
5582
|
+
static toHiragana(text) {
|
|
5583
|
+
return Japanese.toHiragana(text);
|
|
5584
|
+
}
|
|
5585
|
+
|
|
5586
|
+
/**
|
|
5587
|
+
* ひらがなをカタカナに変換
|
|
5588
|
+
* @param {string} text - 変換したいテキスト
|
|
5589
|
+
* @returns {string} 変換後のテキスト
|
|
5590
|
+
*/
|
|
5591
|
+
static toKatakana(text) {
|
|
5592
|
+
return Japanese.toKatakana(text);
|
|
5593
|
+
}
|
|
5594
|
+
|
|
5595
|
+
/**
|
|
5596
|
+
* スペースを半角に変換
|
|
5597
|
+
* @param {string} text - 変換したいテキスト
|
|
5598
|
+
* @returns {string} 変換後のテキスト
|
|
5599
|
+
*/
|
|
5600
|
+
static toHalfWidthSpace(text) {
|
|
5601
|
+
return Japanese.toHalfWidthSpace(text);
|
|
5602
|
+
}
|
|
5603
|
+
|
|
5604
|
+
/**
|
|
5605
|
+
* スペースを全角に変換
|
|
5606
|
+
* @param {string} text - 変換したいテキスト
|
|
5607
|
+
* @returns {string} 変換後のテキスト
|
|
5608
|
+
*/
|
|
5609
|
+
static toFullWidthSpace(text) {
|
|
5610
|
+
return Japanese.toFullWidthSpace(text);
|
|
5611
|
+
}
|
|
5612
|
+
|
|
5613
|
+
/**
|
|
5614
|
+
* 英数記号を半角に変換
|
|
5615
|
+
* @param {string} text - 変換したいテキスト
|
|
5616
|
+
* @returns {string} 変換後のテキスト
|
|
5617
|
+
*/
|
|
5618
|
+
static toHalfWidthAsciiCode(text) {
|
|
5619
|
+
return Japanese.toHalfWidthAsciiCode(text);
|
|
5620
|
+
}
|
|
5621
|
+
|
|
5622
|
+
/**
|
|
5623
|
+
* 英数記号を全角に変換
|
|
5624
|
+
* @param {string} text - 変換したいテキスト
|
|
5625
|
+
* @returns {string} 変換後のテキスト
|
|
5626
|
+
*/
|
|
5627
|
+
static toFullWidthAsciiCode(text) {
|
|
5628
|
+
return Japanese.toFullWidthAsciiCode(text);
|
|
5629
|
+
}
|
|
5630
|
+
|
|
5631
|
+
/**
|
|
5632
|
+
* アルファベットを半角に変換
|
|
5633
|
+
* @param {string} text - 変換したいテキスト
|
|
5634
|
+
* @returns {string} 変換後のテキスト
|
|
5635
|
+
*/
|
|
5636
|
+
static toHalfWidthAlphabet(text) {
|
|
5637
|
+
return Japanese.toHalfWidthAlphabet(text);
|
|
5638
|
+
}
|
|
5639
|
+
|
|
5640
|
+
/**
|
|
5641
|
+
* アルファベットを全角に変換
|
|
5642
|
+
* @param {string} text - 変換したいテキスト
|
|
5643
|
+
* @returns {string} 変換後のテキスト
|
|
5644
|
+
*/
|
|
5645
|
+
static toFullWidthAlphabet(text) {
|
|
5646
|
+
return Japanese.toFullWidthAlphabet(text);
|
|
5647
|
+
}
|
|
5648
|
+
|
|
5649
|
+
/**
|
|
5650
|
+
* 数値を半角に変換
|
|
5651
|
+
* @param {string} text - 変換したいテキスト
|
|
5652
|
+
* @returns {string} 変換後のテキスト
|
|
5653
|
+
*/
|
|
5654
|
+
static toHalfWidthNumber(text) {
|
|
5655
|
+
return Japanese.toHalfWidthNumber(text);
|
|
5656
|
+
}
|
|
5657
|
+
|
|
5658
|
+
/**
|
|
5659
|
+
* 数値を全角に変換
|
|
5660
|
+
* @param {string} text - 変換したいテキスト
|
|
5661
|
+
* @returns {string} 変換後のテキスト
|
|
5662
|
+
*/
|
|
5663
|
+
static toFullWidthNumber(text) {
|
|
5664
|
+
return Japanese.toFullWidthNumber(text);
|
|
5665
|
+
}
|
|
5666
|
+
|
|
5667
|
+
/**
|
|
5668
|
+
* カタカナを半角に変換
|
|
5669
|
+
* @param {string} text - 変換したいテキスト
|
|
5670
|
+
* @returns {string} 変換後のテキスト
|
|
5671
|
+
*/
|
|
5672
|
+
static toHalfWidthKana(text) {
|
|
5673
|
+
return Japanese.toHalfWidthKana(text);
|
|
5674
|
+
}
|
|
5675
|
+
|
|
5676
|
+
/**
|
|
5677
|
+
* カタカナを全角に変換
|
|
5678
|
+
* @param {string} text - 変換したいテキスト
|
|
5679
|
+
* @returns {string} 変換後のテキスト
|
|
5680
|
+
*/
|
|
5681
|
+
static toFullWidthKana(text) {
|
|
5682
|
+
return Japanese.toFullWidthKana(text);
|
|
5683
|
+
}
|
|
5684
|
+
|
|
5685
|
+
/**
|
|
5686
|
+
* 半角に変換
|
|
5687
|
+
* @param {string} text - 変換したいテキスト
|
|
5688
|
+
* @returns {string} 変換後のテキスト
|
|
5689
|
+
*/
|
|
5690
|
+
static toHalfWidth(text) {
|
|
5691
|
+
return Japanese.toHalfWidth(text);
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5694
|
+
/**
|
|
5695
|
+
* 全角に変換
|
|
5696
|
+
* @param {string} text - 変換したいテキスト
|
|
5697
|
+
* @returns {string} 変換後のテキスト
|
|
5698
|
+
*/
|
|
5699
|
+
static toFullWidth(text) {
|
|
5700
|
+
return Japanese.toFullWidth(text);
|
|
5701
|
+
}
|
|
5702
|
+
|
|
5703
|
+
// ---------------------------------
|
|
5704
|
+
// 1つの文字データに対して調査を行う
|
|
5705
|
+
// ---------------------------------
|
|
5706
|
+
|
|
5707
|
+
/**
|
|
5708
|
+
* 指定した1つのUTF-32 コードポイントに関して、解析を行い情報を返します
|
|
5709
|
+
* @param {number} unicode_codepoint - UTF-32 のコードポイント
|
|
5710
|
+
* @returns {MojiData} 文字の情報がつまったオブジェクト
|
|
5711
|
+
*/
|
|
5712
|
+
static getMojiData(unicode_codepoint) {
|
|
5713
|
+
return MojiAnalyzer.getMojiData(unicode_codepoint);
|
|
5714
|
+
}
|
|
5715
|
+
}
|
|
5716
|
+
|
|
5717
|
+
/**
|
|
5718
|
+
* The script is part of TextInputGuard.
|
|
5719
|
+
*
|
|
5720
|
+
* AUTHOR:
|
|
5721
|
+
* natade-jp (https://github.com/natade-jp)
|
|
5722
|
+
*
|
|
5723
|
+
* LICENSE:
|
|
5724
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
5725
|
+
*/
|
|
5726
|
+
|
|
5727
|
+
|
|
5728
|
+
/**
|
|
5729
|
+
* kana ルールのオプション
|
|
5730
|
+
* @typedef {Object} KanaRuleOptions
|
|
5731
|
+
* @property {"katakana-full"|"katakana-half"|"hiragana"} [target="katakana-full"] - 統一先
|
|
5732
|
+
* @property {boolean} [nfkc=true] - 事前に Unicode NFKC 正規化を行う(合体文字などを正規化)
|
|
5733
|
+
*/
|
|
5734
|
+
|
|
5735
|
+
/**
|
|
5736
|
+
* kana ルールを生成する
|
|
5737
|
+
* @param {KanaRuleOptions} [options]
|
|
5738
|
+
* @returns {Rule}
|
|
5739
|
+
*/
|
|
5740
|
+
function kana(options = {}) {
|
|
5741
|
+
/** @type {KanaRuleOptions} */
|
|
5742
|
+
const opt = {
|
|
5743
|
+
target: options.target ?? "katakana-full",
|
|
5744
|
+
nfkc: options.nfkc ?? true
|
|
5745
|
+
};
|
|
5746
|
+
|
|
5747
|
+
return {
|
|
5748
|
+
name: "kana",
|
|
5749
|
+
targets: ["input", "textarea"],
|
|
5750
|
+
|
|
5751
|
+
/**
|
|
5752
|
+
* かな種別の正規化(入力中に都度かける)
|
|
5753
|
+
* - (任意) NFKC 正規化
|
|
5754
|
+
* - Mojix で target へ統一(ここは差し替え)
|
|
5755
|
+
* @param {string} value
|
|
5756
|
+
* @param {GuardContext} ctx
|
|
5757
|
+
* @returns {string}
|
|
5758
|
+
*/
|
|
5759
|
+
normalizeChar(value, ctx) {
|
|
5760
|
+
let s = String(value);
|
|
5761
|
+
if (opt.nfkc) {
|
|
5762
|
+
// 古い環境で normalize が無い可能性もあるので安全に
|
|
5763
|
+
try {
|
|
5764
|
+
s = s.normalize("NFKC");
|
|
5765
|
+
} catch {
|
|
5766
|
+
// noop
|
|
5767
|
+
}
|
|
5768
|
+
}
|
|
5769
|
+
s = Mojix.toKatakana(s);
|
|
5770
|
+
if (opt.target === "katakana-full") {
|
|
5771
|
+
s = Mojix.toFullWidthSpace(s);
|
|
5772
|
+
s = Mojix.toFullWidthKana(s);
|
|
5773
|
+
} else if (opt.target === "katakana-half") {
|
|
5774
|
+
s = Mojix.toHalfWidthSpace(s);
|
|
5775
|
+
s = Mojix.toHalfWidthKana(s);
|
|
5776
|
+
} else {
|
|
5777
|
+
s = Mojix.toFullWidthSpace(s);
|
|
5778
|
+
s = Mojix.toFullWidthKana(s);
|
|
5779
|
+
s = Mojix.toHiragana(s);
|
|
5780
|
+
}
|
|
5781
|
+
return s;
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5784
|
+
}
|
|
5785
|
+
|
|
5786
|
+
/**
|
|
5787
|
+
* datasetから kana ルールを生成する
|
|
5788
|
+
* - data-tig-rules-kana が無ければ null
|
|
5789
|
+
* - オプションは data-tig-rules-kana-xxx から読む
|
|
5790
|
+
*
|
|
5791
|
+
* 対応する data 属性(dataset 名)
|
|
5792
|
+
* - data-tig-rules-kana -> dataset.tigRulesKana
|
|
5793
|
+
* - data-tig-rules-kana-target -> dataset.tigRulesKanaTarget
|
|
5794
|
+
* - data-tig-rules-kana-nfkc -> dataset.tigRulesKanaNfkc
|
|
5795
|
+
*
|
|
5796
|
+
* @param {DOMStringMap} dataset
|
|
5797
|
+
* @param {HTMLInputElement|HTMLTextAreaElement} _el
|
|
5798
|
+
* @returns {Rule|null}
|
|
5799
|
+
*/
|
|
5800
|
+
kana.fromDataset = function fromDataset(dataset, _el) {
|
|
5801
|
+
// ON判定
|
|
5802
|
+
if (dataset.tigRulesKana == null) {
|
|
5803
|
+
return null;
|
|
5804
|
+
}
|
|
5805
|
+
|
|
5806
|
+
/** @type {KanaRuleOptions} */
|
|
5807
|
+
const options = {};
|
|
5808
|
+
|
|
5809
|
+
const target = parseDatasetEnum(dataset.tigRulesKanaTarget, [
|
|
5810
|
+
"katakana-full",
|
|
5811
|
+
"katakana-half",
|
|
5812
|
+
"hiragana"
|
|
5813
|
+
]);
|
|
5814
|
+
if (target != null) {
|
|
5815
|
+
options.target = target;
|
|
5816
|
+
}
|
|
5817
|
+
|
|
5818
|
+
const nfkc = parseDatasetBool(dataset.tigRulesKanaNfkc);
|
|
5819
|
+
if (nfkc != null) {
|
|
5820
|
+
options.nfkc = nfkc;
|
|
5821
|
+
}
|
|
5822
|
+
|
|
5823
|
+
return kana(options);
|
|
5824
|
+
};
|
|
5825
|
+
|
|
5826
|
+
/**
|
|
5827
|
+
* The script is part of TextInputGuard.
|
|
5828
|
+
*
|
|
5829
|
+
* AUTHOR:
|
|
5830
|
+
* natade-jp (https://github.com/natade-jp)
|
|
5831
|
+
*
|
|
5832
|
+
* LICENSE:
|
|
5833
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
5834
|
+
*/
|
|
5835
|
+
|
|
5836
|
+
|
|
5837
|
+
/**
|
|
5838
|
+
* ascii ルールのオプション
|
|
5839
|
+
* @typedef {Object} AsciiRuleOptions
|
|
5840
|
+
* @property {"none"|"upper"|"lower"} [case] - 英字の大文字/小文字統一
|
|
5841
|
+
*/
|
|
5842
|
+
|
|
5843
|
+
/**
|
|
5844
|
+
* ascii ルールを生成する
|
|
5845
|
+
* - 全角英数字・記号・全角スペースを半角へ正規化する
|
|
5846
|
+
* - 必要に応じて英字を大文字/小文字へ統一
|
|
5847
|
+
*
|
|
5848
|
+
* @param {AsciiRuleOptions} [options]
|
|
5849
|
+
* @returns {Rule}
|
|
5850
|
+
*/
|
|
5851
|
+
function ascii(options = {}) {
|
|
5852
|
+
/** @type {AsciiRuleOptions} */
|
|
5853
|
+
const opt = {
|
|
5854
|
+
case: options.case ?? null
|
|
5855
|
+
};
|
|
5856
|
+
|
|
5857
|
+
return {
|
|
5858
|
+
name: "ascii",
|
|
5859
|
+
targets: ["input", "textarea"],
|
|
5860
|
+
|
|
5861
|
+
normalizeChar(value, ctx) {
|
|
5862
|
+
let s = String(value);
|
|
5863
|
+
|
|
5864
|
+
// まず半角へ正規化
|
|
5865
|
+
s = Mojix.toHalfWidthAsciiCode(s);
|
|
5866
|
+
|
|
5867
|
+
// 英字の大文字/小文字統一
|
|
5868
|
+
if (opt.case === "upper") {
|
|
5869
|
+
s = s.toUpperCase();
|
|
5870
|
+
} else if (opt.case === "lower") {
|
|
5871
|
+
s = s.toLowerCase();
|
|
5872
|
+
}
|
|
5873
|
+
|
|
5874
|
+
return s;
|
|
5875
|
+
}
|
|
5876
|
+
};
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
/**
|
|
5880
|
+
* datasetから ascii ルールを生成する
|
|
5881
|
+
*
|
|
5882
|
+
* 対応する data 属性
|
|
5883
|
+
* - data-tig-rules-ascii
|
|
5884
|
+
* - data-tig-rules-ascii-case ("none" | "upper" | "lower")
|
|
5885
|
+
*
|
|
5886
|
+
* @param {DOMStringMap} dataset
|
|
5887
|
+
* @param {HTMLInputElement|HTMLTextAreaElement} _el
|
|
5888
|
+
* @returns {Rule|null}
|
|
5889
|
+
*/
|
|
5890
|
+
ascii.fromDataset = function fromDataset(dataset, _el) {
|
|
5891
|
+
if (dataset.tigRulesAscii == null) {
|
|
5892
|
+
return null;
|
|
5893
|
+
}
|
|
5894
|
+
|
|
5895
|
+
const options = {};
|
|
5896
|
+
|
|
5897
|
+
const caseOpt = parseDatasetEnum(dataset.tigRulesAsciiCase, [
|
|
5898
|
+
"none",
|
|
5899
|
+
"upper",
|
|
5900
|
+
"lower"
|
|
5901
|
+
]);
|
|
5902
|
+
if (caseOpt != null) {
|
|
5903
|
+
options.case = caseOpt;
|
|
5904
|
+
}
|
|
5905
|
+
|
|
5906
|
+
return ascii(options);
|
|
5907
|
+
};
|
|
5908
|
+
|
|
5909
|
+
/* eslint-disable max-len */
|
|
5910
|
+
/**
|
|
5911
|
+
* The script is part of TextInputGuard.
|
|
5912
|
+
*
|
|
5913
|
+
* AUTHOR:
|
|
5914
|
+
* natade-jp (https://github.com/natade-jp)
|
|
5915
|
+
*
|
|
5916
|
+
* LICENSE:
|
|
5917
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
5918
|
+
*/
|
|
5919
|
+
|
|
5920
|
+
|
|
5921
|
+
/**
|
|
5922
|
+
* filter ルールのカテゴリ名
|
|
5923
|
+
*
|
|
5924
|
+
* - "digits" : ASCII 数字 (0-9)
|
|
5925
|
+
* - "alpha-upper" : ASCII 英字大文字 (A-Z)
|
|
5926
|
+
* - "alpha-lower" : ASCII 英字小文字 (a-z)
|
|
5927
|
+
* - "ascii" : ASCII 可視文字 + スペース含む (U+0020–U+007E)
|
|
5928
|
+
* - "hiragana" : ひらがな (U+3040–U+309F)
|
|
5929
|
+
* - "katakana-full" : 全角カタカナ (U+30A0–U+30FF)
|
|
5930
|
+
* - "katakana-half" : 半角カタカナ (U+FF65–U+FF9F)
|
|
5931
|
+
* - "bmp-only" : BMP のみ許可(U+0000–U+FFFF、サロゲートペア、補助平面禁止)
|
|
5932
|
+
* - "sjis-only" : 正規 Shift_JIS(JIS X 0208 + 1バイト領域)のみ許可
|
|
5933
|
+
* - "cp932-only" : Windows-31J (CP932) でエンコード可能な文字のみ許可
|
|
5934
|
+
* - "single-codepoint-only" : 単一コードポイントのみ許可(結合文字や異体字セレクタを含まない)
|
|
5935
|
+
*
|
|
5936
|
+
* @typedef {"digits"|"alpha-upper"|"alpha-lower"|"ascii"|"hiragana"|"katakana-full"|"katakana-half"|"bmp-only"|"sjis-only"|"cp932-only"|"single-codepoint-only"} FilterCategory
|
|
5937
|
+
*/
|
|
5938
|
+
|
|
5939
|
+
/**
|
|
5940
|
+
* グラフェム(1グラフェムは、UTF-32の配列)
|
|
5941
|
+
* @typedef {number[]} Grapheme
|
|
5942
|
+
*/
|
|
5943
|
+
|
|
5944
|
+
/** @type {readonly FilterCategory[]} */
|
|
5945
|
+
const FILTER_CATEGORIES = [
|
|
5946
|
+
"digits",
|
|
5947
|
+
"alpha-upper",
|
|
5948
|
+
"alpha-lower",
|
|
5949
|
+
"ascii",
|
|
5950
|
+
"hiragana",
|
|
5951
|
+
"katakana-full",
|
|
5952
|
+
"katakana-half",
|
|
5953
|
+
"bmp-only",
|
|
5954
|
+
"sjis-only",
|
|
5955
|
+
"cp932-only",
|
|
5956
|
+
"single-codepoint-only"
|
|
5957
|
+
];
|
|
5958
|
+
|
|
5959
|
+
/**
|
|
5960
|
+
* filter ルールの動作モード
|
|
5961
|
+
* @typedef {"drop"|"error"} FilterMode
|
|
5962
|
+
*/
|
|
5963
|
+
|
|
5964
|
+
/**
|
|
5965
|
+
* filter ルールのオプション
|
|
5966
|
+
* - category は和集合で扱う(複数指定OK)
|
|
5967
|
+
* - allow は追加許可(和集合)
|
|
5968
|
+
* - deny は除外(差集合)
|
|
5969
|
+
*
|
|
5970
|
+
* allowed = (category の和集合 ∪ allow) − deny
|
|
5971
|
+
*
|
|
5972
|
+
* @typedef {Object} FilterRuleOptions
|
|
5973
|
+
* @property {FilterMode} [mode="drop"] - drop: 不要文字を削除 / error: 削除せずエラーを積む
|
|
5974
|
+
* @property {FilterCategory[]} [category] - カテゴリ(配列)
|
|
5975
|
+
* @property {RegExp|string} [allow] - 追加で許可する正規表現(1文字にマッチさせる想定)
|
|
5976
|
+
* @property {string} [allowFlags] - allow が文字列のときの flags("iu" など。g/y は無視)
|
|
5977
|
+
* @property {RegExp|string} [deny] - 除外する正規表現(1文字にマッチさせる想定)
|
|
5978
|
+
* @property {string} [denyFlags] - deny が文字列のときの flags("iu" など。g/y は無視)
|
|
5979
|
+
*/
|
|
5980
|
+
|
|
5981
|
+
/**
|
|
5982
|
+
* /g や /y は lastIndex の罠があるので除去して使う
|
|
5983
|
+
* @param {string} flags
|
|
5984
|
+
* @returns {string}
|
|
5985
|
+
*/
|
|
5986
|
+
const stripStatefulFlags = function (flags) {
|
|
5987
|
+
return String(flags || "").replace(/[gy]/g, "");
|
|
5988
|
+
};
|
|
5989
|
+
|
|
5990
|
+
/**
|
|
5991
|
+
* 正規表現(RegExp または pattern 文字列)を安全に RegExp 化する
|
|
5992
|
+
* - g/y を外す
|
|
5993
|
+
* - string の場合、flags 未指定なら "u" を付ける
|
|
5994
|
+
*
|
|
5995
|
+
* @param {RegExp|string|undefined} reOrPattern
|
|
5996
|
+
* @param {string|undefined} flags
|
|
5997
|
+
* @returns {RegExp|undefined}
|
|
5998
|
+
*/
|
|
5999
|
+
const toSafeRegExp = function (reOrPattern, flags) {
|
|
6000
|
+
if (reOrPattern == null) {
|
|
6001
|
+
return;
|
|
6002
|
+
}
|
|
6003
|
+
|
|
6004
|
+
if (reOrPattern instanceof RegExp) {
|
|
6005
|
+
const safeFlags = stripStatefulFlags(reOrPattern.flags);
|
|
6006
|
+
return new RegExp(reOrPattern.source, safeFlags);
|
|
6007
|
+
}
|
|
6008
|
+
|
|
6009
|
+
const f = stripStatefulFlags(flags ?? "u");
|
|
6010
|
+
return new RegExp(String(reOrPattern), f);
|
|
6011
|
+
};
|
|
6012
|
+
|
|
6013
|
+
/**
|
|
6014
|
+
* カテゴリ判定関数を作る
|
|
6015
|
+
* @param {FilterCategory[]} categories
|
|
6016
|
+
* @returns {(g: Grapheme, s: string) => boolean}
|
|
6017
|
+
*/
|
|
6018
|
+
const createCategoryTester = function (categories) {
|
|
6019
|
+
/** @type {Record<FilterCategory, (g: Grapheme, s: string) => boolean>} */
|
|
6020
|
+
const table = {
|
|
6021
|
+
digits: (g, s) => {
|
|
6022
|
+
return g.length === 1 && g[0] >= 0x30 && g[0] <= 0x39; // '0'..'9'
|
|
6023
|
+
},
|
|
6024
|
+
"alpha-upper": (g, s) => {
|
|
6025
|
+
if (g.length !== 1) { return false; }
|
|
6026
|
+
const c = g[0];
|
|
6027
|
+
// 'A'..'Z'
|
|
6028
|
+
return c >= 0x41 && c <= 0x5A;
|
|
6029
|
+
},
|
|
6030
|
+
"alpha-lower": (g, s) => {
|
|
6031
|
+
if (g.length !== 1) { return false; }
|
|
6032
|
+
const c = g[0];
|
|
6033
|
+
// 'a'..'z'
|
|
6034
|
+
return c >= 0x61 && c <= 0x7A;
|
|
6035
|
+
},
|
|
6036
|
+
ascii: (g, s) => {
|
|
6037
|
+
if (g.length !== 1) { return false; }
|
|
6038
|
+
const c = g[0];
|
|
6039
|
+
return c >= 0x20 && c <= 0x7E;
|
|
6040
|
+
},
|
|
6041
|
+
hiragana: (g, s) => {
|
|
6042
|
+
if (g.length !== 1) { return false; }
|
|
6043
|
+
const c = g[0];
|
|
6044
|
+
return c >= 0x3040 && c <= 0x309F;
|
|
6045
|
+
},
|
|
6046
|
+
"katakana-full": (g, s) => {
|
|
6047
|
+
if (g.length !== 1) { return false; }
|
|
6048
|
+
const c = g[0];
|
|
6049
|
+
return c >= 0x30A0 && c <= 0x30FF;
|
|
6050
|
+
},
|
|
6051
|
+
"katakana-half": (g, s) => {
|
|
6052
|
+
if (g.length !== 1) { return false; }
|
|
6053
|
+
const c = g[0];
|
|
6054
|
+
return c >= 0xFF65 && c <= 0xFF9F;
|
|
6055
|
+
},
|
|
6056
|
+
"bmp-only": (g, s) => {
|
|
6057
|
+
// BMPのみ(サロゲートペア禁止)
|
|
6058
|
+
return g.every((cp) => cp <= 0xFFFF);
|
|
6059
|
+
},
|
|
6060
|
+
"sjis-only": (g, s) => {
|
|
6061
|
+
// Shift_JIS でエンコードできる文字かどうか
|
|
6062
|
+
if (g.length !== 1) { return false; }
|
|
6063
|
+
const cp932code = CP932.toCP932FromUnicode(g[0]);
|
|
6064
|
+
if (cp932code === undefined) { return false; }
|
|
6065
|
+
const kuten = SJIS.toKuTenFromSJISCode(cp932code);
|
|
6066
|
+
if (cp932code < 0x100) {
|
|
6067
|
+
return true;
|
|
6068
|
+
}
|
|
6069
|
+
if (!SJIS.isRegularMenKuten(kuten)) { return false; }
|
|
6070
|
+
return kuten.ku <= 94;
|
|
6071
|
+
},
|
|
6072
|
+
"cp932-only": (g, s) => {
|
|
6073
|
+
// Windows-31J (cp932) でエンコードできる文字かどうか
|
|
6074
|
+
if (g.length !== 1) { return false; }
|
|
6075
|
+
return CP932.toCP932FromUnicode(g[0]) !== undefined;
|
|
6076
|
+
},
|
|
6077
|
+
"single-codepoint-only": (g, s) => {
|
|
6078
|
+
// 1グラフェムが単一コードポイントのみで構成されていること
|
|
6079
|
+
return g.length === 1;
|
|
6080
|
+
}
|
|
6081
|
+
};
|
|
6082
|
+
|
|
6083
|
+
// categories は「和集合」なので、該当する tester だけ抜いて使う
|
|
6084
|
+
const list = categories.map((c) => table[c]).filter(Boolean);
|
|
6085
|
+
|
|
6086
|
+
if (list.length === 0) {
|
|
6087
|
+
return function () {
|
|
6088
|
+
return false;
|
|
6089
|
+
};
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6092
|
+
return function (g, s) {
|
|
6093
|
+
for (const test of list) {
|
|
6094
|
+
if (test(g, s)) {
|
|
6095
|
+
return true;
|
|
6096
|
+
}
|
|
6097
|
+
}
|
|
6098
|
+
return false;
|
|
6099
|
+
};
|
|
6100
|
+
};
|
|
6101
|
+
|
|
6102
|
+
/**
|
|
6103
|
+
* 1文字が許可されるか判定する関数を作る
|
|
6104
|
+
* @param {FilterCategory[]} categoryList
|
|
6105
|
+
* @param {(g: Grapheme, s: string) => boolean} categoryTest
|
|
6106
|
+
* @param {RegExp|undefined} allowRe
|
|
6107
|
+
* @param {RegExp|undefined} denyRe
|
|
6108
|
+
* @returns {(g: Grapheme, s: string) => boolean}
|
|
6109
|
+
*/
|
|
6110
|
+
const createAllowedTester = function (categoryList, categoryTest, allowRe, denyRe) {
|
|
6111
|
+
const hasCategory = categoryList.length > 0;
|
|
6112
|
+
const hasAllow = allowRe != null;
|
|
6113
|
+
const hasDeny = denyRe != null;
|
|
6114
|
+
|
|
6115
|
+
// deny だけの指定は「deny に当たる文字だけ落とす」ルールとして扱う
|
|
6116
|
+
const denyOnly = !hasCategory && !hasAllow && hasDeny;
|
|
6117
|
+
|
|
6118
|
+
return function (g, s) {
|
|
6119
|
+
if (denyRe && denyRe.test(s)) {
|
|
6120
|
+
return false;
|
|
6121
|
+
}
|
|
6122
|
+
|
|
6123
|
+
if (denyOnly) {
|
|
6124
|
+
return true;
|
|
6125
|
+
}
|
|
6126
|
+
|
|
6127
|
+
if (hasCategory && categoryTest(g, s)) {
|
|
6128
|
+
return true;
|
|
6129
|
+
}
|
|
6130
|
+
if (allowRe && allowRe.test(s)) {
|
|
6131
|
+
return true;
|
|
6132
|
+
}
|
|
6133
|
+
|
|
6134
|
+
return false;
|
|
6135
|
+
};
|
|
6136
|
+
};
|
|
6137
|
+
|
|
6138
|
+
/**
|
|
6139
|
+
* 文字列を走査して、許可文字のみの文字列と、不正文字の集計を返す
|
|
6140
|
+
* @param {string} value
|
|
6141
|
+
* @param {(g: Grapheme, s: string) => boolean} isAllowed
|
|
6142
|
+
* @param {number} [maxInvalidChars=20]
|
|
6143
|
+
* @returns {{ filtered: string, invalidCount: number, invalidChars: string[] }}
|
|
6144
|
+
*/
|
|
6145
|
+
const scanByAllowed = function (value, isAllowed, maxInvalidChars = 20) {
|
|
6146
|
+
const v = String(value);
|
|
6147
|
+
|
|
6148
|
+
let filtered = "";
|
|
6149
|
+
let invalidCount = 0;
|
|
6150
|
+
|
|
6151
|
+
/** @type {Set<string>} */
|
|
6152
|
+
const invalidSet = new Set();
|
|
6153
|
+
|
|
6154
|
+
/**
|
|
6155
|
+
* グラフェムの配列
|
|
6156
|
+
* @type {Grapheme[]}
|
|
6157
|
+
*/
|
|
6158
|
+
const graphemeArray = Mojix.toMojiArrayFromString(v);
|
|
6159
|
+
|
|
6160
|
+
// JS の文字列イテレータはコードポイント単位で回るので Array.from は不要
|
|
6161
|
+
for (const g of graphemeArray) {
|
|
6162
|
+
const s = Mojix.toStringFromMojiArray([g]);
|
|
6163
|
+
if (isAllowed(g, s)) {
|
|
6164
|
+
filtered += s;
|
|
6165
|
+
} else {
|
|
6166
|
+
invalidCount++;
|
|
6167
|
+
if (invalidSet.size < maxInvalidChars) {
|
|
6168
|
+
invalidSet.add(s);
|
|
6169
|
+
}
|
|
6170
|
+
}
|
|
6171
|
+
}
|
|
6172
|
+
|
|
6173
|
+
return {
|
|
6174
|
+
filtered,
|
|
6175
|
+
invalidCount,
|
|
6176
|
+
invalidChars: Array.from(invalidSet)
|
|
6177
|
+
};
|
|
6178
|
+
};
|
|
6179
|
+
|
|
6180
|
+
/**
|
|
6181
|
+
* filter ルールを生成する
|
|
6182
|
+
* - mode="drop": 不要文字を落とすだけ
|
|
6183
|
+
* - mode="error": 文字は落とさず validate でエラーを積む
|
|
6184
|
+
*
|
|
6185
|
+
* @param {FilterRuleOptions} [options]
|
|
6186
|
+
* @returns {Rule}
|
|
6187
|
+
*/
|
|
6188
|
+
function filter(options = {}) {
|
|
6189
|
+
/** @type {FilterRuleOptions} */
|
|
6190
|
+
const opt = {
|
|
6191
|
+
mode: options.mode ?? "drop",
|
|
6192
|
+
category: options.category ?? [],
|
|
6193
|
+
allow: options.allow,
|
|
6194
|
+
allowFlags: options.allowFlags,
|
|
6195
|
+
deny: options.deny,
|
|
6196
|
+
denyFlags: options.denyFlags
|
|
6197
|
+
};
|
|
6198
|
+
|
|
6199
|
+
const categoryList = opt.category;
|
|
6200
|
+
const categoryTest = createCategoryTester(categoryList);
|
|
6201
|
+
|
|
6202
|
+
const allowRe = toSafeRegExp(opt.allow, opt.allowFlags);
|
|
6203
|
+
const denyRe = toSafeRegExp(opt.deny, opt.denyFlags);
|
|
6204
|
+
|
|
6205
|
+
const isAllowed = createAllowedTester(categoryList, categoryTest, allowRe, denyRe);
|
|
6206
|
+
|
|
6207
|
+
const hasAny = categoryList.length > 0 || allowRe != null || denyRe != null;
|
|
6208
|
+
|
|
6209
|
+
return {
|
|
6210
|
+
name: "filter",
|
|
6211
|
+
targets: ["input", "textarea"],
|
|
6212
|
+
|
|
6213
|
+
/**
|
|
6214
|
+
* 許可集合で落とす(drop モードのみ)
|
|
6215
|
+
* @param {string} value
|
|
6216
|
+
* @param {GuardContext} ctx
|
|
6217
|
+
* @returns {string}
|
|
6218
|
+
*/
|
|
6219
|
+
normalizeChar(value, ctx) {
|
|
6220
|
+
if (!hasAny) {
|
|
6221
|
+
return value;
|
|
6222
|
+
}
|
|
6223
|
+
|
|
6224
|
+
// error モードは何も落とさない(全て通す)
|
|
6225
|
+
if (opt.mode === "error") {
|
|
6226
|
+
return value;
|
|
6227
|
+
}
|
|
6228
|
+
|
|
6229
|
+
return scanByAllowed(value, isAllowed).filtered;
|
|
6230
|
+
},
|
|
6231
|
+
|
|
6232
|
+
/**
|
|
6233
|
+
* 不正文字が含まれていたらエラーを積む(error モードのみ)
|
|
6234
|
+
* @param {string} value
|
|
6235
|
+
* @param {GuardContext} ctx
|
|
6236
|
+
* @returns {void}
|
|
6237
|
+
*/
|
|
6238
|
+
validate(value, ctx) {
|
|
6239
|
+
if (!hasAny) {
|
|
6240
|
+
return;
|
|
6241
|
+
}
|
|
6242
|
+
if (opt.mode !== "error") {
|
|
6243
|
+
return;
|
|
6244
|
+
}
|
|
6245
|
+
|
|
6246
|
+
const v = String(value);
|
|
6247
|
+
if (v === "") {
|
|
6248
|
+
return;
|
|
6249
|
+
}
|
|
6250
|
+
|
|
6251
|
+
const r = scanByAllowed(v, isAllowed);
|
|
6252
|
+
if (r.invalidCount > 0) {
|
|
6253
|
+
ctx.pushError({
|
|
6254
|
+
code: "filter.invalid_char",
|
|
6255
|
+
rule: "filter",
|
|
6256
|
+
phase: "validate",
|
|
6257
|
+
detail: {
|
|
6258
|
+
count: r.invalidCount,
|
|
6259
|
+
chars: r.invalidChars,
|
|
6260
|
+
category: categoryList,
|
|
6261
|
+
hasAllow: allowRe != null,
|
|
6262
|
+
hasDeny: denyRe != null
|
|
6263
|
+
}
|
|
6264
|
+
});
|
|
6265
|
+
}
|
|
6266
|
+
}
|
|
6267
|
+
};
|
|
6268
|
+
}
|
|
6269
|
+
|
|
6270
|
+
/**
|
|
6271
|
+
* datasetから filter ルールを生成する
|
|
6272
|
+
* - data-tig-rules-filter が無ければ null
|
|
6273
|
+
*
|
|
6274
|
+
* 対応する data 属性(dataset 名)
|
|
6275
|
+
* - data-tig-rules-filter -> dataset.tigRulesFilter
|
|
6276
|
+
* - data-tig-rules-filter-mode -> dataset.tigRulesFilterMode ("drop"|"error")
|
|
6277
|
+
* - data-tig-rules-filter-category -> dataset.tigRulesFilterCategory ("a,b,c")
|
|
6278
|
+
* - data-tig-rules-filter-allow -> dataset.tigRulesFilterAllow
|
|
6279
|
+
* - data-tig-rules-filter-allow-flags -> dataset.tigRulesFilterAllowFlags
|
|
6280
|
+
* - data-tig-rules-filter-deny -> dataset.tigRulesFilterDeny
|
|
6281
|
+
* - data-tig-rules-filter-deny-flags -> dataset.tigRulesFilterDenyFlags
|
|
6282
|
+
*
|
|
6283
|
+
* @param {DOMStringMap} dataset
|
|
6284
|
+
* @param {HTMLInputElement|HTMLTextAreaElement} _el
|
|
6285
|
+
* @returns {Rule|null}
|
|
6286
|
+
*/
|
|
6287
|
+
filter.fromDataset = function fromDataset(dataset, _el) {
|
|
6288
|
+
if (dataset.tigRulesFilter == null) {
|
|
6289
|
+
return null;
|
|
6290
|
+
}
|
|
6291
|
+
|
|
6292
|
+
/** @type {FilterRuleOptions} */
|
|
6293
|
+
const options = {};
|
|
6294
|
+
|
|
6295
|
+
const mode = parseDatasetEnum(dataset.tigRulesFilterMode, ["drop", "error"]);
|
|
6296
|
+
if (mode != null) {
|
|
6297
|
+
options.mode = mode;
|
|
6298
|
+
}
|
|
6299
|
+
|
|
6300
|
+
const category = parseDatasetEnumList(dataset.tigRulesFilterCategory, FILTER_CATEGORIES);
|
|
6301
|
+
if (category != null) {
|
|
6302
|
+
options.category = category;
|
|
6303
|
+
}
|
|
6304
|
+
|
|
6305
|
+
if (dataset.tigRulesFilterAllow != null) {
|
|
6306
|
+
const s = String(dataset.tigRulesFilterAllow).trim();
|
|
6307
|
+
if (s !== "") {
|
|
6308
|
+
options.allow = s;
|
|
6309
|
+
}
|
|
6310
|
+
}
|
|
6311
|
+
|
|
6312
|
+
if (dataset.tigRulesFilterAllowFlags != null) {
|
|
6313
|
+
const s = String(dataset.tigRulesFilterAllowFlags).trim();
|
|
6314
|
+
if (s !== "") {
|
|
6315
|
+
options.allowFlags = s;
|
|
6316
|
+
}
|
|
6317
|
+
}
|
|
6318
|
+
|
|
6319
|
+
if (dataset.tigRulesFilterDeny != null) {
|
|
6320
|
+
const s = String(dataset.tigRulesFilterDeny).trim();
|
|
6321
|
+
if (s !== "") {
|
|
6322
|
+
options.deny = s;
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
|
|
6326
|
+
if (dataset.tigRulesFilterDenyFlags != null) {
|
|
6327
|
+
const s = String(dataset.tigRulesFilterDenyFlags).trim();
|
|
6328
|
+
if (s !== "") {
|
|
6329
|
+
options.denyFlags = s;
|
|
6330
|
+
}
|
|
6331
|
+
}
|
|
6332
|
+
|
|
6333
|
+
return filter(options);
|
|
6334
|
+
};
|
|
6335
|
+
|
|
6336
|
+
/**
|
|
6337
|
+
* The script is part of TextInputGuard.
|
|
6338
|
+
*
|
|
6339
|
+
* AUTHOR:
|
|
6340
|
+
* natade-jp (https://github.com/natade-jp)
|
|
6341
|
+
*
|
|
6342
|
+
* LICENSE:
|
|
6343
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
6344
|
+
*/
|
|
6345
|
+
|
|
6346
|
+
|
|
6347
|
+
/**
|
|
6348
|
+
* length ルールのオプション
|
|
6349
|
+
* @typedef {Object} LengthRuleOptions
|
|
6350
|
+
* @property {number} [max] - 最大長(グラフェム数)。未指定なら制限なし
|
|
6351
|
+
* @property {"block"|"error"} [overflowInput="block"] - 入力中に最大長を超えたときの挙動
|
|
6352
|
+
* @property {"grapheme"|"utf-16"|"utf-32"} [unit="grapheme"] - 長さの単位
|
|
6353
|
+
*
|
|
6354
|
+
* block : 最大長を超える部分を切る
|
|
6355
|
+
* error : エラーを積むだけ(値は変更しない)
|
|
6356
|
+
*/
|
|
6357
|
+
|
|
6358
|
+
/**
|
|
6359
|
+
* グラフェム(1グラフェムは、UTF-32の配列)
|
|
6360
|
+
* @typedef {number[]} Grapheme
|
|
6361
|
+
*/
|
|
6362
|
+
|
|
6363
|
+
/**
|
|
6364
|
+
* グラフェム/UTF-16コード単位/UTF-32コード単位の長さを調べる
|
|
6365
|
+
* @param {string} text
|
|
6366
|
+
* @param {"grapheme"|"utf-16"|"utf-32"} unit
|
|
6367
|
+
* @returns {number}
|
|
6368
|
+
*/
|
|
6369
|
+
const getTextLengthByUnit = function(text, unit) {
|
|
6370
|
+
if (unit === "grapheme") {
|
|
6371
|
+
return Mojix.toMojiArrayFromString(text).length;
|
|
6372
|
+
} else if (unit === "utf-16") {
|
|
6373
|
+
return Mojix.toUTF16Array(text).length;
|
|
6374
|
+
} else if (unit === "utf-32") {
|
|
6375
|
+
return Mojix.toUTF32Array(text).length;
|
|
6376
|
+
} else {
|
|
6377
|
+
// ここには来ない
|
|
6378
|
+
throw new Error(`Invalid unit: ${unit}`);
|
|
6379
|
+
}
|
|
6380
|
+
};
|
|
6381
|
+
|
|
6382
|
+
/**
|
|
6383
|
+
* グラフェム/UTF-16コード単位/UTF-32コード単位でテキストを切る
|
|
6384
|
+
* @param {string} text
|
|
6385
|
+
* @param {"grapheme"|"utf-16"|"utf-32"} unit
|
|
6386
|
+
* @param {number} max
|
|
6387
|
+
* @returns {string}
|
|
6388
|
+
*/
|
|
6389
|
+
const cutTextByUnit = function(text, unit, max) {
|
|
6390
|
+
/**
|
|
6391
|
+
* グラフェムの配列
|
|
6392
|
+
* @type {Grapheme[]}
|
|
6393
|
+
*/
|
|
6394
|
+
const graphemeArray = Mojix.toMojiArrayFromString(text);
|
|
6395
|
+
|
|
6396
|
+
/**
|
|
6397
|
+
* 現在の位置
|
|
6398
|
+
*/
|
|
6399
|
+
let count = 0;
|
|
6400
|
+
|
|
6401
|
+
/**
|
|
6402
|
+
* グラフェムの配列(出力用)
|
|
6403
|
+
* @type {Grapheme[]}
|
|
6404
|
+
*/
|
|
6405
|
+
const outputGraphemeArray = [];
|
|
6406
|
+
|
|
6407
|
+
for (let i = 0; i < graphemeArray.length; i++) {
|
|
6408
|
+
const g = graphemeArray[i];
|
|
6409
|
+
|
|
6410
|
+
// 1グラフェムあたりの長さ
|
|
6411
|
+
let graphemeCount = 0;
|
|
6412
|
+
if (unit === "grapheme") {
|
|
6413
|
+
graphemeCount = 1;
|
|
6414
|
+
} else if (unit === "utf-16") {
|
|
6415
|
+
graphemeCount = 0;
|
|
6416
|
+
for (let i = 0; i < g.length; i++) {
|
|
6417
|
+
graphemeCount += (g[i] > 0xFFFF) ? 2 : 1;
|
|
6418
|
+
}
|
|
6419
|
+
} else if (unit === "utf-32") {
|
|
6420
|
+
graphemeCount = g.length;
|
|
6421
|
+
}
|
|
6422
|
+
|
|
6423
|
+
if (count + graphemeCount > max) {
|
|
6424
|
+
// 空配列を渡すとNUL文字を返すため、空配列のときは空文字を返す
|
|
6425
|
+
if (outputGraphemeArray.length === 0) {
|
|
6426
|
+
return "";
|
|
6427
|
+
}
|
|
6428
|
+
// 超える前の位置で文字列化して返す
|
|
6429
|
+
return Mojix.toStringFromMojiArray(outputGraphemeArray);
|
|
6430
|
+
}
|
|
6431
|
+
|
|
6432
|
+
count += graphemeCount;
|
|
6433
|
+
outputGraphemeArray.push(g);
|
|
6434
|
+
}
|
|
6435
|
+
|
|
6436
|
+
// 全部入るなら元の text を返す
|
|
6437
|
+
return text;
|
|
6438
|
+
};
|
|
6439
|
+
|
|
6440
|
+
/**
|
|
6441
|
+
* 元のテキストと追加のテキストの合計が max を超える場合、追加のテキストを切って合計が max に収まるようにする
|
|
6442
|
+
* @param {string} beforeText 元のテキスト
|
|
6443
|
+
* @param {string} insertedText 追加するテキスト
|
|
6444
|
+
* @param {"grapheme"|"utf-16"|"utf-32"} unit
|
|
6445
|
+
* @param {number} max
|
|
6446
|
+
* @returns {string} 追加するテキストを切ったもの(切る必要がない場合は insertedText をそのまま返す)
|
|
6447
|
+
*/
|
|
6448
|
+
const cutLength = function(beforeText, insertedText, unit, max) {
|
|
6449
|
+
const orgLen = getTextLengthByUnit(beforeText, unit);
|
|
6450
|
+
|
|
6451
|
+
// すでに最大長を超えている場合は追加のテキストを全て切る
|
|
6452
|
+
if (orgLen >= max) { return ""; }
|
|
6453
|
+
|
|
6454
|
+
const addLen = getTextLengthByUnit(insertedText, unit);
|
|
6455
|
+
const totalLen = orgLen + addLen;
|
|
6456
|
+
|
|
6457
|
+
if (totalLen <= max) {
|
|
6458
|
+
// 今回の追加で範囲内に収まるなら何もしない
|
|
6459
|
+
return insertedText;
|
|
6460
|
+
}
|
|
6461
|
+
|
|
6462
|
+
// 超える場合は追加のテキストを切る
|
|
6463
|
+
const allowedAddLen = max - orgLen;
|
|
6464
|
+
return cutTextByUnit(insertedText, unit, allowedAddLen);
|
|
6465
|
+
};
|
|
6466
|
+
|
|
6467
|
+
/**
|
|
6468
|
+
* length ルールを生成する
|
|
6469
|
+
* @param {LengthRuleOptions} [options]
|
|
6470
|
+
* @returns {Rule}
|
|
6471
|
+
*/
|
|
6472
|
+
function length(options = {}) {
|
|
6473
|
+
/** @type {LengthRuleOptions} */
|
|
6474
|
+
const opt = {
|
|
6475
|
+
max: typeof options.max === "number" ? options.max : undefined,
|
|
6476
|
+
overflowInput: options.overflowInput ?? "block",
|
|
6477
|
+
unit: options.unit ?? "grapheme"
|
|
6478
|
+
};
|
|
6479
|
+
|
|
6480
|
+
return {
|
|
6481
|
+
name: "length",
|
|
6482
|
+
targets: ["input", "textarea"],
|
|
6483
|
+
|
|
6484
|
+
normalizeChar(value, ctx) {
|
|
6485
|
+
// block 以外は何もしない
|
|
6486
|
+
if (opt.overflowInput !== "block") {
|
|
6487
|
+
return value;
|
|
6488
|
+
}
|
|
6489
|
+
// max 未指定なら制限なし
|
|
6490
|
+
if (typeof opt.max !== "number") {
|
|
6491
|
+
return value;
|
|
6492
|
+
}
|
|
6493
|
+
|
|
6494
|
+
const beforeText = ctx.beforeText;
|
|
6495
|
+
const insertedText = ctx.insertedText;
|
|
6496
|
+
if (insertedText === "") {
|
|
6497
|
+
return value;
|
|
6498
|
+
}
|
|
6499
|
+
|
|
6500
|
+
const cutText = cutLength(beforeText, insertedText, opt.unit, opt.max);
|
|
6501
|
+
return cutText;
|
|
6502
|
+
},
|
|
6503
|
+
|
|
6504
|
+
validate(value, ctx) {
|
|
6505
|
+
// error 以外は何もしない
|
|
6506
|
+
if (opt.overflowInput !== "error") {
|
|
6507
|
+
return value;
|
|
6508
|
+
}
|
|
6509
|
+
// max 未指定なら制限なし
|
|
6510
|
+
if (typeof opt.max !== "number") {
|
|
6511
|
+
return;
|
|
6512
|
+
}
|
|
6513
|
+
|
|
6514
|
+
const len = getTextLengthByUnit(value, opt.unit);
|
|
6515
|
+
if (len > opt.max) {
|
|
6516
|
+
ctx.pushError({
|
|
6517
|
+
code: "length.max_overflow",
|
|
6518
|
+
rule: "length",
|
|
6519
|
+
phase: "validate",
|
|
6520
|
+
detail: { max: opt.max, actual: len }
|
|
6521
|
+
});
|
|
6522
|
+
}
|
|
6523
|
+
}
|
|
6524
|
+
};
|
|
6525
|
+
}
|
|
6526
|
+
|
|
6527
|
+
/**
|
|
6528
|
+
* datasetから length ルールを生成する
|
|
6529
|
+
* - data-tig-rules-length が無ければ null
|
|
6530
|
+
* - オプションは data-tig-rules-length-xxx から読む
|
|
6531
|
+
*
|
|
6532
|
+
* 対応する data 属性(dataset 名)
|
|
6533
|
+
* - data-tig-rules-length -> dataset.tigRulesLength
|
|
6534
|
+
* - data-tig-rules-length-max -> dataset.tigRulesLengthMax
|
|
6535
|
+
* - data-tig-rules-length-overflow-input -> dataset.tigRulesLengthOverflowInput
|
|
6536
|
+
* - data-tig-rules-length-unit -> dataset.tigRulesLengthUnit
|
|
6537
|
+
*
|
|
6538
|
+
* @param {DOMStringMap} dataset
|
|
6539
|
+
* @param {HTMLInputElement|HTMLTextAreaElement} _el
|
|
6540
|
+
* @returns {Rule|null}
|
|
6541
|
+
*/
|
|
6542
|
+
length.fromDataset = function fromDataset(dataset, _el) {
|
|
6543
|
+
// ON判定
|
|
6544
|
+
if (dataset.tigRulesLength == null) {
|
|
6545
|
+
return null;
|
|
6546
|
+
}
|
|
6547
|
+
|
|
6548
|
+
/** @type {LengthRuleOptions} */
|
|
6549
|
+
const options = {};
|
|
6550
|
+
|
|
6551
|
+
const max = parseDatasetNumber(dataset.tigRulesLengthMax);
|
|
6552
|
+
if (max != null) {
|
|
6553
|
+
options.max = max;
|
|
6554
|
+
}
|
|
6555
|
+
|
|
6556
|
+
const overflowInput = parseDatasetEnum(
|
|
6557
|
+
dataset.tigRulesLengthOverflowInput,
|
|
6558
|
+
["block", "error"]
|
|
6559
|
+
);
|
|
6560
|
+
if (overflowInput != null) {
|
|
6561
|
+
options.overflowInput = overflowInput;
|
|
6562
|
+
}
|
|
6563
|
+
|
|
6564
|
+
const unit = parseDatasetEnum(
|
|
6565
|
+
dataset.tigRulesLengthUnit,
|
|
6566
|
+
["grapheme", "utf-16", "utf-32"]
|
|
6567
|
+
);
|
|
6568
|
+
if (unit != null) {
|
|
6569
|
+
options.unit = unit;
|
|
6570
|
+
}
|
|
6571
|
+
|
|
6572
|
+
return length(options);
|
|
6573
|
+
};
|
|
6574
|
+
|
|
6575
|
+
/**
|
|
6576
|
+
* The script is part of TextInputGuard.
|
|
6577
|
+
*
|
|
6578
|
+
* AUTHOR:
|
|
6579
|
+
* natade-jp (https://github.com/natade-jp)
|
|
6580
|
+
*
|
|
6581
|
+
* LICENSE:
|
|
6582
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
6583
|
+
*/
|
|
6584
|
+
|
|
6585
|
+
/**
|
|
6586
|
+
* prefix ルールのオプション
|
|
6587
|
+
* @typedef {Object} PrefixRuleOptions
|
|
6588
|
+
* @property {string} text - 先頭に付ける文字列
|
|
6589
|
+
* @property {boolean} [showWhenEmpty=false] - 値が空でも表示するか
|
|
6590
|
+
*/
|
|
6591
|
+
|
|
6592
|
+
/**
|
|
6593
|
+
* 先頭装飾(prefix)ルール
|
|
6594
|
+
* - 表示用として先頭に文字列を付与する
|
|
6595
|
+
* - 手動入力された同文字列は normalizeStructure で除去する
|
|
6596
|
+
*
|
|
6597
|
+
* @param {PrefixRuleOptions} options
|
|
6598
|
+
* @returns {Rule}
|
|
6599
|
+
*/
|
|
6600
|
+
function prefix(options) {
|
|
6601
|
+
/** @type {PrefixRuleOptions} */
|
|
6602
|
+
const opt = {
|
|
6603
|
+
text: options?.text ?? "",
|
|
6604
|
+
showWhenEmpty: options?.showWhenEmpty ?? false
|
|
6605
|
+
};
|
|
6606
|
+
|
|
6607
|
+
return {
|
|
6608
|
+
name: "prefix",
|
|
6609
|
+
targets: ["input"],
|
|
6610
|
+
|
|
6611
|
+
/**
|
|
6612
|
+
* 手動入力された prefix を除去
|
|
6613
|
+
* @param {string} value
|
|
6614
|
+
* @returns {string}
|
|
6615
|
+
*/
|
|
6616
|
+
normalizeStructure(value) {
|
|
6617
|
+
if (!opt.text) { return value; }
|
|
6618
|
+
|
|
6619
|
+
let s = String(value);
|
|
6620
|
+
|
|
6621
|
+
while (s.startsWith(opt.text)) {
|
|
6622
|
+
s = s.slice(opt.text.length);
|
|
6623
|
+
}
|
|
6624
|
+
|
|
6625
|
+
return s;
|
|
6626
|
+
},
|
|
6627
|
+
|
|
6628
|
+
/**
|
|
6629
|
+
* 表示用整形
|
|
6630
|
+
* @param {string} value
|
|
6631
|
+
* @returns {string}
|
|
6632
|
+
*/
|
|
6633
|
+
format(value) {
|
|
6634
|
+
if (!opt.text) { return value; }
|
|
6635
|
+
|
|
6636
|
+
if (!value) {
|
|
6637
|
+
return opt.showWhenEmpty ? opt.text : value;
|
|
6638
|
+
}
|
|
6639
|
+
|
|
6640
|
+
return opt.text + value;
|
|
6641
|
+
}
|
|
6642
|
+
};
|
|
6643
|
+
}
|
|
6644
|
+
|
|
6645
|
+
/**
|
|
6646
|
+
* datasetから prefix ルールを生成する
|
|
6647
|
+
* - data-tig-rules-prefix が無ければ null
|
|
6648
|
+
*
|
|
6649
|
+
* 対応する data 属性(dataset 名)
|
|
6650
|
+
* - data-tig-rules-prefix -> dataset.tigRulesPrefix
|
|
6651
|
+
* - data-tig-rules-prefix-text -> dataset.tigRulesPrefixText
|
|
6652
|
+
* - data-tig-rules-prefix-show-when-empty -> dataset.tigRulesPrefixShowWhenEmpty
|
|
6653
|
+
*
|
|
6654
|
+
* @param {DOMStringMap} dataset
|
|
6655
|
+
* @param {HTMLInputElement|HTMLTextAreaElement} _el
|
|
6656
|
+
* @returns {Rule|null}
|
|
6657
|
+
*/
|
|
6658
|
+
prefix.fromDataset = function fromDataset(dataset, _el) {
|
|
6659
|
+
if (dataset.tigRulesPrefix == null) {
|
|
6660
|
+
return null;
|
|
6661
|
+
}
|
|
6662
|
+
|
|
6663
|
+
return prefix({
|
|
6664
|
+
text: dataset.tigRulesPrefixText ?? "",
|
|
6665
|
+
showWhenEmpty: dataset.tigRulesPrefixShowWhenEmpty === "true"
|
|
6666
|
+
});
|
|
6667
|
+
};
|
|
6668
|
+
|
|
6669
|
+
/**
|
|
6670
|
+
* The script is part of TextInputGuard.
|
|
6671
|
+
*
|
|
6672
|
+
* AUTHOR:
|
|
6673
|
+
* natade-jp (https://github.com/natade-jp)
|
|
6674
|
+
*
|
|
6675
|
+
* LICENSE:
|
|
6676
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
6677
|
+
*/
|
|
6678
|
+
|
|
6679
|
+
/**
|
|
6680
|
+
* suffix ルールのオプション
|
|
6681
|
+
* @typedef {Object} SuffixRuleOptions
|
|
6682
|
+
* @property {string} text - 末尾に付ける文字列
|
|
6683
|
+
* @property {boolean} [showWhenEmpty=false] - 値が空でも表示するか
|
|
6684
|
+
*/
|
|
6685
|
+
|
|
6686
|
+
/**
|
|
6687
|
+
* 末尾装飾(suffix)ルール
|
|
6688
|
+
* - 表示用として末尾に文字列を付与する
|
|
6689
|
+
* - 手動入力された同文字列は normalizeStructure で除去する
|
|
6690
|
+
*
|
|
6691
|
+
* @param {SuffixRuleOptions} options
|
|
6692
|
+
* @returns {Rule}
|
|
6693
|
+
*/
|
|
6694
|
+
function suffix(options) {
|
|
6695
|
+
/** @type {SuffixRuleOptions} */
|
|
6696
|
+
const opt = {
|
|
6697
|
+
text: options?.text ?? "",
|
|
6698
|
+
showWhenEmpty: options?.showWhenEmpty ?? false
|
|
6699
|
+
};
|
|
6700
|
+
|
|
6701
|
+
return {
|
|
6702
|
+
name: "suffix",
|
|
6703
|
+
targets: ["input"],
|
|
6704
|
+
|
|
6705
|
+
/**
|
|
6706
|
+
* 手動入力された suffix を除去
|
|
6707
|
+
* @param {string} value
|
|
6708
|
+
* @returns {string}
|
|
6709
|
+
*/
|
|
6710
|
+
normalizeStructure(value) {
|
|
6711
|
+
if (!opt.text) { return value; }
|
|
6712
|
+
|
|
6713
|
+
let s = String(value);
|
|
6714
|
+
|
|
6715
|
+
while (s.endsWith(opt.text)) {
|
|
6716
|
+
s = s.slice(0, -opt.text.length);
|
|
6717
|
+
}
|
|
6718
|
+
|
|
6719
|
+
return s;
|
|
6720
|
+
},
|
|
6721
|
+
|
|
6722
|
+
/**
|
|
6723
|
+
* 表示用整形
|
|
6724
|
+
* @param {string} value
|
|
6725
|
+
* @returns {string}
|
|
6726
|
+
*/
|
|
6727
|
+
format(value) {
|
|
6728
|
+
if (!opt.text) { return value; }
|
|
6729
|
+
|
|
6730
|
+
if (!value) {
|
|
6731
|
+
return opt.showWhenEmpty ? opt.text : value;
|
|
6732
|
+
}
|
|
6733
|
+
|
|
6734
|
+
return value + opt.text;
|
|
6735
|
+
}
|
|
6736
|
+
};
|
|
6737
|
+
}
|
|
6738
|
+
|
|
6739
|
+
/**
|
|
6740
|
+
* datasetから suffix ルールを生成する
|
|
6741
|
+
* - data-tig-rules-suffix が無ければ null
|
|
6742
|
+
*
|
|
6743
|
+
* 対応する data 属性(dataset 名)
|
|
6744
|
+
* - data-tig-rules-suffix -> dataset.tigRulesSuffix
|
|
6745
|
+
* - data-tig-rules-suffix-text -> dataset.tigRulesSuffixText
|
|
6746
|
+
* - data-tig-rules-suffix-show-when-empty -> dataset.tigRulesSuffixShowWhenEmpty
|
|
6747
|
+
*
|
|
6748
|
+
* @param {DOMStringMap} dataset
|
|
6749
|
+
* @param {HTMLInputElement|HTMLTextAreaElement} _el
|
|
6750
|
+
* @returns {Rule|null}
|
|
6751
|
+
*/
|
|
6752
|
+
suffix.fromDataset = function fromDataset(dataset, _el) {
|
|
6753
|
+
if (dataset.tigRulesSuffix == null) {
|
|
6754
|
+
return null;
|
|
6755
|
+
}
|
|
6756
|
+
|
|
6757
|
+
return suffix({
|
|
6758
|
+
text: dataset.tigRulesSuffixText ?? "",
|
|
6759
|
+
showWhenEmpty: dataset.tigRulesSuffixShowWhenEmpty === "true"
|
|
6760
|
+
});
|
|
6761
|
+
};
|
|
6762
|
+
|
|
6763
|
+
/**
|
|
6764
|
+
* The script is part of TextInputGuard.
|
|
6765
|
+
*
|
|
6766
|
+
* AUTHOR:
|
|
6767
|
+
* natade-jp (https://github.com/natade-jp)
|
|
6768
|
+
*
|
|
6769
|
+
* LICENSE:
|
|
6770
|
+
* The MIT license https://opensource.org/licenses/MIT
|
|
6771
|
+
*/
|
|
6772
|
+
|
|
6773
|
+
/**
|
|
6774
|
+
* トリムするルール
|
|
6775
|
+
* @returns {Rule}
|
|
6776
|
+
*/
|
|
6777
|
+
function trim() {
|
|
6778
|
+
return {
|
|
6779
|
+
name: "trim",
|
|
6780
|
+
targets: ["input", "textarea"],
|
|
6781
|
+
|
|
6782
|
+
/**
|
|
6783
|
+
* 確定時に整える
|
|
6784
|
+
* @param {string} value
|
|
6785
|
+
* @returns {string}
|
|
6786
|
+
*/
|
|
6787
|
+
fix(value) {
|
|
6788
|
+
return value.trim();
|
|
6789
|
+
}
|
|
6790
|
+
};
|
|
6791
|
+
}
|
|
6792
|
+
|
|
6793
|
+
/**
|
|
6794
|
+
* datasetから trim ルールを生成する
|
|
6795
|
+
* - data-tig-rules-trim が無ければ null
|
|
6796
|
+
*
|
|
6797
|
+
* 対応する data 属性(dataset 名)
|
|
6798
|
+
* - data-tig-rules-trim -> dataset.tigRulesTrim
|
|
6799
|
+
*
|
|
6800
|
+
* @param {DOMStringMap} dataset
|
|
6801
|
+
* @param {HTMLInputElement|HTMLTextAreaElement} _el
|
|
6802
|
+
* @returns {Rule|null}
|
|
6803
|
+
*/
|
|
6804
|
+
trim.fromDataset = function fromDataset(dataset, _el) {
|
|
6805
|
+
// ON判定:data-tig-rules-trim が無ければ対象外
|
|
6806
|
+
if (dataset.tigRulesTrim == null) {
|
|
6807
|
+
return null;
|
|
6808
|
+
}
|
|
6809
|
+
return trim();
|
|
6810
|
+
};
|
|
6811
|
+
|
|
2381
6812
|
/**
|
|
2382
6813
|
* TextInputGuard - Public Entry
|
|
2383
6814
|
* - ESM/CJS: named exports (attach / autoAttach / rules / numeric / digits / comma / version)
|
|
@@ -2395,7 +6826,14 @@ comma.fromDataset = function fromDataset(dataset, _el) {
|
|
|
2395
6826
|
const auto = new InputGuardAutoAttach(attach, [
|
|
2396
6827
|
{ name: "numeric", fromDataset: numeric.fromDataset },
|
|
2397
6828
|
{ name: "digits", fromDataset: digits.fromDataset },
|
|
2398
|
-
{ name: "comma", fromDataset: comma.fromDataset }
|
|
6829
|
+
{ name: "comma", fromDataset: comma.fromDataset },
|
|
6830
|
+
{ name: "kana", fromDataset: kana.fromDataset },
|
|
6831
|
+
{ name: "ascii", fromDataset: ascii.fromDataset },
|
|
6832
|
+
{ name: "filter", fromDataset: filter.fromDataset },
|
|
6833
|
+
{ name: "length", fromDataset: length.fromDataset },
|
|
6834
|
+
{ name: "prefix", fromDataset: prefix.fromDataset },
|
|
6835
|
+
{ name: "suffix", fromDataset: suffix.fromDataset },
|
|
6836
|
+
{ name: "trim", fromDataset: trim.fromDataset }
|
|
2399
6837
|
]);
|
|
2400
6838
|
|
|
2401
6839
|
/**
|
|
@@ -2410,22 +6848,36 @@ const autoAttach = (root) => auto.autoAttach(root);
|
|
|
2410
6848
|
const rules = {
|
|
2411
6849
|
numeric,
|
|
2412
6850
|
digits,
|
|
2413
|
-
comma
|
|
6851
|
+
comma,
|
|
6852
|
+
kana,
|
|
6853
|
+
ascii,
|
|
6854
|
+
filter,
|
|
6855
|
+
length,
|
|
6856
|
+
prefix,
|
|
6857
|
+
suffix,
|
|
6858
|
+
trim
|
|
2414
6859
|
};
|
|
2415
6860
|
|
|
2416
6861
|
/**
|
|
2417
6862
|
* バージョン(ビルド時に置換したいならここを差し替える)
|
|
2418
|
-
* 例: rollup replace で ""0.1.
|
|
6863
|
+
* 例: rollup replace で ""0.1.5"" を package.json の version に置換
|
|
2419
6864
|
*/
|
|
2420
6865
|
// @ts-ignore
|
|
2421
6866
|
// eslint-disable-next-line no-undef
|
|
2422
|
-
const version = "0.1.
|
|
6867
|
+
const version = "0.1.5" ;
|
|
2423
6868
|
|
|
6869
|
+
exports.ascii = ascii;
|
|
2424
6870
|
exports.attach = attach;
|
|
2425
6871
|
exports.attachAll = attachAll;
|
|
2426
6872
|
exports.autoAttach = autoAttach;
|
|
2427
6873
|
exports.comma = comma;
|
|
2428
6874
|
exports.digits = digits;
|
|
6875
|
+
exports.filter = filter;
|
|
6876
|
+
exports.kana = kana;
|
|
6877
|
+
exports.length = length;
|
|
2429
6878
|
exports.numeric = numeric;
|
|
6879
|
+
exports.prefix = prefix;
|
|
2430
6880
|
exports.rules = rules;
|
|
6881
|
+
exports.suffix = suffix;
|
|
6882
|
+
exports.trim = trim;
|
|
2431
6883
|
exports.version = version;
|