text-input-guard 1.0.1 → 1.1.0
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 +187 -94
- package/dist/cjs/text-input-guard.min.cjs +1 -1
- package/dist/esm/text-input-guard.js +187 -95
- package/dist/esm/text-input-guard.min.js +1 -1
- package/dist/types/text-input-guard.d.ts +57 -28
- package/dist/umd/text-input-guard.js +187 -94
- package/dist/umd/text-input-guard.min.js +1 -1
- package/package.json +1 -1
|
@@ -419,6 +419,7 @@
|
|
|
419
419
|
* @property {string} [invalidClass="is-invalid"] - エラー時に付けるclass名
|
|
420
420
|
* @property {SeparateValueOptions} [separateValue] - 表示値と内部値の分離設定
|
|
421
421
|
* @property {(result: ValidateResult) => void} [onValidate] - 評価完了時の通知(input/commitごと)
|
|
422
|
+
* @property {(result: Guard) => void} [onChange] - フォーカスが外れた値が変更されていた場合の通知
|
|
422
423
|
*/
|
|
423
424
|
|
|
424
425
|
/**
|
|
@@ -584,6 +585,18 @@
|
|
|
584
585
|
*/
|
|
585
586
|
this.onValidate = options.onValidate;
|
|
586
587
|
|
|
588
|
+
/**
|
|
589
|
+
* blur時に値が変更されていた場合の通知
|
|
590
|
+
* @type {((result: Guard) => void) | undefined}
|
|
591
|
+
*/
|
|
592
|
+
this.onChange = options.onChange;
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* onChange 判定のための直前の値(blur時にこれと比較して変化を検知する)
|
|
596
|
+
* @type {string}
|
|
597
|
+
*/
|
|
598
|
+
this.previousValue = "";
|
|
599
|
+
|
|
587
600
|
/**
|
|
588
601
|
* 実際に送信を担う要素(swap時は hidden(raw) 側)
|
|
589
602
|
* swapしない場合は originalElement と同一
|
|
@@ -741,6 +754,11 @@
|
|
|
741
754
|
*/
|
|
742
755
|
this.beforeInputSnapshot = null;
|
|
743
756
|
|
|
757
|
+
/**
|
|
758
|
+
* onBeforeInput イベントが発生したか否か
|
|
759
|
+
*/
|
|
760
|
+
this.existBeforeInputEvent = false;
|
|
761
|
+
|
|
744
762
|
/**
|
|
745
763
|
* ルールからのrevert要求
|
|
746
764
|
* @type {RevertRequest|null}
|
|
@@ -759,6 +777,8 @@
|
|
|
759
777
|
this.bindEvents();
|
|
760
778
|
// 初期値を評価
|
|
761
779
|
this.evaluateCommit();
|
|
780
|
+
// 初期値を記録
|
|
781
|
+
this.previousValue = this.getDisplayValue();
|
|
762
782
|
}
|
|
763
783
|
|
|
764
784
|
/**
|
|
@@ -991,18 +1011,56 @@
|
|
|
991
1011
|
if (this.warn) ;
|
|
992
1012
|
}
|
|
993
1013
|
|
|
1014
|
+
/**
|
|
1015
|
+
* 変更前後の文字列から置換範囲と挿入文字列を推測
|
|
1016
|
+
* @param {string} beforeText
|
|
1017
|
+
* @param {string} afterText
|
|
1018
|
+
* @returns {{ replaceStart: number, replaceEnd: number, insertedText: string }}
|
|
1019
|
+
*/
|
|
1020
|
+
detectTextDiff(beforeText, afterText) {
|
|
1021
|
+
let start = 0;
|
|
1022
|
+
|
|
1023
|
+
while (
|
|
1024
|
+
start < beforeText.length &&
|
|
1025
|
+
start < afterText.length &&
|
|
1026
|
+
beforeText[start] === afterText[start]
|
|
1027
|
+
) {
|
|
1028
|
+
start++;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
let beforeEnd = beforeText.length;
|
|
1032
|
+
let afterEnd = afterText.length;
|
|
1033
|
+
|
|
1034
|
+
while (
|
|
1035
|
+
beforeEnd > start &&
|
|
1036
|
+
afterEnd > start &&
|
|
1037
|
+
beforeText[beforeEnd - 1] === afterText[afterEnd - 1]
|
|
1038
|
+
) {
|
|
1039
|
+
beforeEnd--;
|
|
1040
|
+
afterEnd--;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
return {
|
|
1044
|
+
replaceStart: start,
|
|
1045
|
+
replaceEnd: beforeEnd,
|
|
1046
|
+
insertedText: afterText.slice(start, afterEnd)
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
|
|
994
1050
|
/**
|
|
995
1051
|
* ルール実行に渡すコンテキストを作る(pushErrorで errors に積める)
|
|
996
1052
|
* @returns {GuardContext}
|
|
997
1053
|
*/
|
|
998
1054
|
createCtx({ useSnapshot = true } = {}) {
|
|
1055
|
+
// 入力後のテキストを取得
|
|
1056
|
+
const afterText = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement).value;
|
|
999
1057
|
const snap = useSnapshot ? this.beforeInputSnapshot : null;
|
|
1000
|
-
|
|
1001
|
-
|
|
1058
|
+
let inputType = snap?.inputType ?? "";
|
|
1059
|
+
let insertedText = snap?.insertedText ?? "";
|
|
1002
1060
|
|
|
1003
1061
|
// 受理済み(正規化済み)の全文を「今回の編集の基準」として使う
|
|
1004
1062
|
// display.value はブラウザ側の編集結果が混ざるので、差分再構成の基準にはしない
|
|
1005
|
-
|
|
1063
|
+
let beforeText = this.lastAcceptedValue ?? "";
|
|
1006
1064
|
|
|
1007
1065
|
// selection は2系統ある:
|
|
1008
1066
|
// - snapSel: beforeinput 時点で取得した selection(今回の編集の基準点になり得る)
|
|
@@ -1038,13 +1096,45 @@
|
|
|
1038
1096
|
baseSel = lastSel;
|
|
1039
1097
|
}
|
|
1040
1098
|
|
|
1099
|
+
// オートコンプリートの処理
|
|
1100
|
+
// inputType が取得できないため existBeforeInputEvent 情報で判断
|
|
1101
|
+
// 差分再構成の基準が「前回の受理値」しかないため、そこから今回の編集内容を推測する必要がある。
|
|
1102
|
+
if (beforeText.length === 0 || !this.existBeforeInputEvent) {
|
|
1103
|
+
// 前回の値がとれないものの、何かしら入力情報がある状態
|
|
1104
|
+
if (afterText.length > 0) {
|
|
1105
|
+
// 文字列の先頭が前回の受理値と同じなら、末尾に何かしら入力されたと考えられる(オートコンプリート等)
|
|
1106
|
+
if (afterText.toLocaleLowerCase().startsWith(beforeText.toLocaleLowerCase())) {
|
|
1107
|
+
if (!afterText.startsWith(beforeText)) {
|
|
1108
|
+
// 文字は同じだが、大文字と小文字の情報が替わっているなどのパターン
|
|
1109
|
+
// 差し代わりが起きているため、前回値は基準にならないと判断して、差分全体を insertedText とする
|
|
1110
|
+
beforeText = "";
|
|
1111
|
+
insertedText = afterText;
|
|
1112
|
+
} else {
|
|
1113
|
+
// 末尾に追加されたと考えられる部分を insertedText とする
|
|
1114
|
+
// 例: beforeText="abc" → afterText="abcde" なら、"de" が insertedText
|
|
1115
|
+
insertedText = afterText.slice(beforeText.length);
|
|
1116
|
+
}
|
|
1117
|
+
// キャレットは前回値の末尾にあると推測する
|
|
1118
|
+
baseSel = /** @type {SelectionState} */ {
|
|
1119
|
+
start: beforeText.length,
|
|
1120
|
+
end: beforeText.length,
|
|
1121
|
+
direction: "none"
|
|
1122
|
+
};
|
|
1123
|
+
inputType = "insertText";
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
// existBeforeInputEvent は、少なくとも1回 beforeinput が発生したかどうかのフラグ
|
|
1128
|
+
// これが false の場合、上記のような「beforeinputがない環境での推測ロジック」を走らせる。
|
|
1129
|
+
this.existBeforeInputEvent = false;
|
|
1130
|
+
|
|
1041
1131
|
let replaceStart = baseSel.start ?? 0;
|
|
1042
1132
|
let replaceEnd = baseSel.end ?? 0;
|
|
1043
1133
|
|
|
1134
|
+
// 削除操作の特殊処理
|
|
1044
1135
|
// Backspace / Delete は「挿入文字がない(dataがnull)」ことが多い。
|
|
1045
1136
|
// そのままだと差分再構成で “何も変わらない” 扱いになって削除が効かなくなるため、
|
|
1046
1137
|
// 選択範囲が無い場合は「削除される1文字ぶん」の置換範囲をここで作る。
|
|
1047
|
-
//
|
|
1048
1138
|
// ※ 選択範囲がある削除は replaceStart!=replaceEnd なので補正不要(その範囲を消すだけでよい)
|
|
1049
1139
|
if (replaceStart === replaceEnd) {
|
|
1050
1140
|
if (inputType === "deleteContentBackward") {
|
|
@@ -1060,6 +1150,14 @@
|
|
|
1060
1150
|
// deleteWordBackward / deleteWordForward / deleteByCut / deleteSoftLineBackward ... etc
|
|
1061
1151
|
}
|
|
1062
1152
|
|
|
1153
|
+
// アンドゥリドゥの特殊処理
|
|
1154
|
+
if (inputType === "historyUndo" || inputType === "historyRedo") {
|
|
1155
|
+
const diff = this.detectTextDiff(beforeText, afterText);
|
|
1156
|
+
replaceStart = diff.replaceStart;
|
|
1157
|
+
replaceEnd = diff.replaceEnd;
|
|
1158
|
+
insertedText = diff.insertedText;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1063
1161
|
return {
|
|
1064
1162
|
hostElement: this.hostElement,
|
|
1065
1163
|
displayElement: this.displayElement,
|
|
@@ -1073,7 +1171,7 @@
|
|
|
1073
1171
|
replaceStart,
|
|
1074
1172
|
replaceEnd,
|
|
1075
1173
|
insertedText,
|
|
1076
|
-
afterText
|
|
1174
|
+
afterText,
|
|
1077
1175
|
pushError: (e) => this.errors.push(e),
|
|
1078
1176
|
requestRevert: (req) => {
|
|
1079
1177
|
// 1回でもrevert要求が出たら採用(最初の理由を保持)
|
|
@@ -1263,9 +1361,7 @@
|
|
|
1263
1361
|
try {
|
|
1264
1362
|
this.evaluateInput();
|
|
1265
1363
|
} finally {
|
|
1266
|
-
|
|
1267
|
-
// 古い snapshot を使い回さないよう、1イベントごとに破棄する
|
|
1268
|
-
this.beforeInputSnapshot = null;
|
|
1364
|
+
this.existBeforeInputEvent = false;
|
|
1269
1365
|
}
|
|
1270
1366
|
}
|
|
1271
1367
|
|
|
@@ -1285,7 +1381,11 @@
|
|
|
1285
1381
|
/** @type {string|null} */
|
|
1286
1382
|
const inputType = typeof e.inputType === "string" ? e.inputType : null;
|
|
1287
1383
|
/** @type {string|null} */
|
|
1288
|
-
|
|
1384
|
+
let insertedText = typeof e.data === "string" ? e.data : null;
|
|
1385
|
+
if (insertedText === null && (inputType === "insertLineBreak" || inputType === "insertParagraph")) {
|
|
1386
|
+
insertedText = "\n";
|
|
1387
|
+
}
|
|
1388
|
+
this.existBeforeInputEvent = true;
|
|
1289
1389
|
this.beforeInputSnapshot = { selection, inputType, insertedText };
|
|
1290
1390
|
}
|
|
1291
1391
|
|
|
@@ -1296,6 +1396,12 @@
|
|
|
1296
1396
|
onBlur() {
|
|
1297
1397
|
// console.log("[text-input-guard] blur");
|
|
1298
1398
|
this.evaluateCommit();
|
|
1399
|
+
if (this.previousValue !== this.getDisplayValue()) {
|
|
1400
|
+
this.previousValue = this.getDisplayValue();
|
|
1401
|
+
if (this.onChange) {
|
|
1402
|
+
this.onChange(this.getGuard());
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1299
1405
|
}
|
|
1300
1406
|
|
|
1301
1407
|
/**
|
|
@@ -1420,49 +1526,7 @@
|
|
|
1420
1526
|
* @returns {GuardContext}
|
|
1421
1527
|
*/
|
|
1422
1528
|
createCtxAndNormalize() {
|
|
1423
|
-
const display = /** @type {HTMLInputElement|HTMLTextAreaElement} */ (this.displayElement);
|
|
1424
|
-
const current = display.value;
|
|
1425
1529
|
const ctx = this.createCtx();
|
|
1426
|
-
ctx.afterText = current;
|
|
1427
|
-
|
|
1428
|
-
/**
|
|
1429
|
-
* 入力値情報のみを使用するフォールバック
|
|
1430
|
-
* @returns {GuardContext}
|
|
1431
|
-
*/
|
|
1432
|
-
const applyFullNormalizeFromCurrent = () => {
|
|
1433
|
-
let newText = current;
|
|
1434
|
-
ctx.beforeText = "";
|
|
1435
|
-
newText = this.runNormalizeChar(newText, ctx);
|
|
1436
|
-
newText = this.runNormalizeStructure(newText, ctx);
|
|
1437
|
-
this.setDisplayValuePreserveCaret(display, newText, ctx);
|
|
1438
|
-
ctx.afterText = newText;
|
|
1439
|
-
return ctx;
|
|
1440
|
-
};
|
|
1441
|
-
|
|
1442
|
-
// beforeinput が取得できない経路(初回評価)では
|
|
1443
|
-
// 差分再構成を行うと lastAcceptedValue 基準で値を落とす可能性があるため、
|
|
1444
|
-
// 現在の全文を正規化して扱うフォールバックへ切り替える。
|
|
1445
|
-
if (!this.beforeInputSnapshot) {
|
|
1446
|
-
return applyFullNormalizeFromCurrent();
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
// オートコンプリート等では beforeinput は来ても data が空のことがあり、
|
|
1450
|
-
// 差分情報だけでは再構成不能になる。表示値がすでに変わっている場合は
|
|
1451
|
-
// 再構成を諦めて current 全体の正規化に切り替える。
|
|
1452
|
-
const isDeleteInput =
|
|
1453
|
-
ctx.inputType === "deleteContentBackward" ||
|
|
1454
|
-
ctx.inputType === "deleteContentForward";
|
|
1455
|
-
const isInsertLikeInput =
|
|
1456
|
-
ctx.inputType === "" ||
|
|
1457
|
-
ctx.inputType?.startsWith("insert");
|
|
1458
|
-
const lacksDelta =
|
|
1459
|
-
ctx.insertedText === "" &&
|
|
1460
|
-
ctx.beforeText !== current &&
|
|
1461
|
-
isInsertLikeInput &&
|
|
1462
|
-
!isDeleteInput;
|
|
1463
|
-
if (lacksDelta) {
|
|
1464
|
-
return applyFullNormalizeFromCurrent();
|
|
1465
|
-
}
|
|
1466
1530
|
|
|
1467
1531
|
// 元のテキスト
|
|
1468
1532
|
const beforeText = ctx.beforeText;
|
|
@@ -1474,7 +1538,7 @@
|
|
|
1474
1538
|
const replaceStart = ctx.replaceStart;
|
|
1475
1539
|
|
|
1476
1540
|
// 現状のテキスト
|
|
1477
|
-
const tempText =
|
|
1541
|
+
const tempText = ctx.afterText;
|
|
1478
1542
|
|
|
1479
1543
|
// 作成する全体のテキスト
|
|
1480
1544
|
let newText = beforeText;
|
|
@@ -1519,7 +1583,7 @@
|
|
|
1519
1583
|
|
|
1520
1584
|
// 画面を更新
|
|
1521
1585
|
this.syncDisplay(newText);
|
|
1522
|
-
this.writeSelection(
|
|
1586
|
+
this.writeSelection(this.displayElement, newSelection);
|
|
1523
1587
|
|
|
1524
1588
|
// CTX の情報を最新の情報へ更新する
|
|
1525
1589
|
ctx.afterText = newText;
|
|
@@ -6984,9 +7048,10 @@
|
|
|
6984
7048
|
/**
|
|
6985
7049
|
* bytes ルールのオプション
|
|
6986
7050
|
* @typedef {Object} BytesRuleOptions
|
|
6987
|
-
* @property {number} [max] -
|
|
7051
|
+
* @property {number} [max] - バイト数。未指定なら制限なし
|
|
6988
7052
|
* @property {"block"|"error"} [mode="block"] - 入力中に最大長を超えたときの挙動
|
|
6989
7053
|
* @property {"utf-8"|"utf-16"|"utf-32"|"sjis"|"cp932"} [unit="utf-8"] - サイズの単位(sjis系を使用する場合はfilterも必須)
|
|
7054
|
+
* @property {"\n"|"\r"|"\r\n"} [newline="\n"] - 改行の扱い(バイト数計算に影響あり)
|
|
6990
7055
|
*/
|
|
6991
7056
|
|
|
6992
7057
|
/**
|
|
@@ -6995,25 +7060,28 @@
|
|
|
6995
7060
|
*/
|
|
6996
7061
|
|
|
6997
7062
|
/**
|
|
6998
|
-
*
|
|
7063
|
+
* テキストのバイト数を調べる
|
|
6999
7064
|
* @param {string} text
|
|
7000
7065
|
* @param {"utf-8"|"utf-16"|"utf-32"|"sjis"|"cp932"} unit
|
|
7066
|
+
* @param {"\n"|"\r"|"\r\n"} newline
|
|
7001
7067
|
* @returns {number}
|
|
7002
7068
|
*/
|
|
7003
|
-
const getTextBytesByUnit = function(text, unit) {
|
|
7069
|
+
const getTextBytesByUnit = function(text, unit, newline) {
|
|
7004
7070
|
if (text.length === 0) {
|
|
7005
7071
|
return 0;
|
|
7006
7072
|
}
|
|
7073
|
+
|
|
7074
|
+
const normalizedText = text.replace(/\r?\n/g, newline);
|
|
7075
|
+
|
|
7007
7076
|
if (unit === "utf-8") {
|
|
7008
|
-
return Mojix.toUTF8Array(
|
|
7077
|
+
return Mojix.toUTF8Array(normalizedText).length;
|
|
7009
7078
|
} else if (unit === "utf-16") {
|
|
7010
|
-
return Mojix.toUTF16Array(
|
|
7079
|
+
return Mojix.toUTF16Array(normalizedText).length * 2;
|
|
7011
7080
|
} else if (unit === "utf-32") {
|
|
7012
|
-
return Mojix.toUTF32Array(
|
|
7081
|
+
return Mojix.toUTF32Array(normalizedText).length * 4;
|
|
7013
7082
|
} else if (unit === "sjis" || unit === "cp932") {
|
|
7014
|
-
return Mojix.encode(
|
|
7083
|
+
return Mojix.encode(normalizedText, "Shift_JIS").length;
|
|
7015
7084
|
} else {
|
|
7016
|
-
// ここには来ない
|
|
7017
7085
|
throw new Error(`Invalid unit: ${unit}`);
|
|
7018
7086
|
}
|
|
7019
7087
|
};
|
|
@@ -7023,9 +7091,10 @@
|
|
|
7023
7091
|
* @param {string} text
|
|
7024
7092
|
* @param {"utf-8"|"utf-16"|"utf-32"|"sjis"|"cp932"} unit
|
|
7025
7093
|
* @param {number} max
|
|
7094
|
+
* @param {"\n"|"\r"|"\r\n"} newline
|
|
7026
7095
|
* @returns {string}
|
|
7027
7096
|
*/
|
|
7028
|
-
const cutTextByUnit = function(text, unit, max) {
|
|
7097
|
+
const cutTextByUnit = function(text, unit, max, newline) {
|
|
7029
7098
|
/**
|
|
7030
7099
|
* グラフェムの配列
|
|
7031
7100
|
* @type {Grapheme[]}
|
|
@@ -7044,19 +7113,10 @@
|
|
|
7044
7113
|
const outputGraphemeArray = [];
|
|
7045
7114
|
|
|
7046
7115
|
for (let i = 0; i < graphemeArray.length; i++) {
|
|
7047
|
-
const g = graphemeArray[i];
|
|
7048
|
-
|
|
7049
7116
|
// 1グラフェムあたりの長さ
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
} else if (unit === "utf-16") {
|
|
7054
|
-
byteCount = Mojix.toUTF16Array(Mojix.toStringFromMojiArray([g])).length * 2;
|
|
7055
|
-
} else if (unit === "utf-32") {
|
|
7056
|
-
byteCount = Mojix.toUTF32Array(Mojix.toStringFromMojiArray([g])).length * 4;
|
|
7057
|
-
} else if (unit === "sjis" || unit === "cp932") {
|
|
7058
|
-
byteCount = Mojix.encode(Mojix.toStringFromMojiArray([g]), "Shift_JIS").length;
|
|
7059
|
-
}
|
|
7117
|
+
const g = graphemeArray[i];
|
|
7118
|
+
const gText = Mojix.toStringFromMojiArray([g]);
|
|
7119
|
+
const byteCount = getTextBytesByUnit(gText, unit, newline);
|
|
7060
7120
|
|
|
7061
7121
|
if (count + byteCount > max) {
|
|
7062
7122
|
// 空配列を渡すとNUL文字を返すため、空配列のときは空文字を返す
|
|
@@ -7081,15 +7141,16 @@
|
|
|
7081
7141
|
* @param {string} insertedText 追加するテキスト
|
|
7082
7142
|
* @param {"utf-8"|"utf-16"|"utf-32"|"sjis"|"cp932"} unit
|
|
7083
7143
|
* @param {number} max
|
|
7144
|
+
* @param {"\n"|"\r"|"\r\n"} newline
|
|
7084
7145
|
* @returns {string} 追加するテキストを切ったもの(切る必要がない場合は insertedText をそのまま返す)
|
|
7085
7146
|
*/
|
|
7086
|
-
const cutBytes = function(beforeText, insertedText, unit, max) {
|
|
7087
|
-
const beforeTextLen = getTextBytesByUnit(beforeText, unit);
|
|
7147
|
+
const cutBytes = function(beforeText, insertedText, unit, max, newline) {
|
|
7148
|
+
const beforeTextLen = getTextBytesByUnit(beforeText, unit, newline);
|
|
7088
7149
|
|
|
7089
7150
|
// すでに最大長を超えている場合は追加のテキストを全て切る
|
|
7090
7151
|
if (beforeTextLen >= max) { return ""; }
|
|
7091
7152
|
|
|
7092
|
-
const insertedTextLen = getTextBytesByUnit(insertedText, unit);
|
|
7153
|
+
const insertedTextLen = getTextBytesByUnit(insertedText, unit, newline);
|
|
7093
7154
|
const totalLen = beforeTextLen + insertedTextLen;
|
|
7094
7155
|
|
|
7095
7156
|
if (totalLen <= max) {
|
|
@@ -7099,7 +7160,7 @@
|
|
|
7099
7160
|
|
|
7100
7161
|
// 超える場合は追加のテキストを切る
|
|
7101
7162
|
const allowedAddLen = max - beforeTextLen;
|
|
7102
|
-
return cutTextByUnit(insertedText, unit, allowedAddLen);
|
|
7163
|
+
return cutTextByUnit(insertedText, unit, allowedAddLen, newline);
|
|
7103
7164
|
};
|
|
7104
7165
|
|
|
7105
7166
|
/**
|
|
@@ -7108,12 +7169,10 @@
|
|
|
7108
7169
|
* @returns {Rule}
|
|
7109
7170
|
*/
|
|
7110
7171
|
function bytes(options = {}) {
|
|
7111
|
-
|
|
7112
|
-
const
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
unit: options.unit ?? "utf-8"
|
|
7116
|
-
};
|
|
7172
|
+
const max = typeof options.max === "number" ? options.max : undefined;
|
|
7173
|
+
const mode = options.mode ?? "block";
|
|
7174
|
+
const unit = options.unit ?? "utf-8";
|
|
7175
|
+
const newline = options.newline ?? "\n";
|
|
7117
7176
|
|
|
7118
7177
|
return {
|
|
7119
7178
|
name: "bytes",
|
|
@@ -7121,35 +7180,35 @@
|
|
|
7121
7180
|
|
|
7122
7181
|
normalizeChar(value, ctx) {
|
|
7123
7182
|
// block 以外は何もしない
|
|
7124
|
-
if (
|
|
7183
|
+
if (mode !== "block") {
|
|
7125
7184
|
return value;
|
|
7126
7185
|
}
|
|
7127
7186
|
// max 未指定なら制限なし
|
|
7128
|
-
if (typeof
|
|
7187
|
+
if (typeof max !== "number") {
|
|
7129
7188
|
return value;
|
|
7130
7189
|
}
|
|
7131
7190
|
|
|
7132
|
-
const cutText = cutBytes(ctx.beforeText, value,
|
|
7191
|
+
const cutText = cutBytes(ctx.beforeText, value, unit, max, newline);
|
|
7133
7192
|
return cutText;
|
|
7134
7193
|
},
|
|
7135
7194
|
|
|
7136
7195
|
validate(value, ctx) {
|
|
7137
7196
|
// error 以外は何もしない
|
|
7138
|
-
if (
|
|
7197
|
+
if (mode !== "error") {
|
|
7139
7198
|
return;
|
|
7140
7199
|
}
|
|
7141
7200
|
// max 未指定なら制限なし
|
|
7142
|
-
if (typeof
|
|
7201
|
+
if (typeof max !== "number") {
|
|
7143
7202
|
return;
|
|
7144
7203
|
}
|
|
7145
7204
|
|
|
7146
|
-
const len = getTextBytesByUnit(value,
|
|
7147
|
-
if (len >
|
|
7205
|
+
const len = getTextBytesByUnit(value, unit, newline);
|
|
7206
|
+
if (len > max) {
|
|
7148
7207
|
ctx.pushError({
|
|
7149
7208
|
code: "bytes.max_overflow",
|
|
7150
7209
|
rule: "bytes",
|
|
7151
7210
|
phase: "validate",
|
|
7152
|
-
detail: { limit:
|
|
7211
|
+
detail: { limit: max, actual: len }
|
|
7153
7212
|
});
|
|
7154
7213
|
}
|
|
7155
7214
|
}
|
|
@@ -7166,6 +7225,7 @@
|
|
|
7166
7225
|
* - data-tig-rules-bytes-max -> dataset.tigRulesBytesMax
|
|
7167
7226
|
* - data-tig-rules-bytes-mode -> dataset.tigRulesBytesMode
|
|
7168
7227
|
* - data-tig-rules-bytes-unit -> dataset.tigRulesBytesUnit
|
|
7228
|
+
* - data-tig-rules-bytes-newline -> dataset.tigRulesBytesNewline
|
|
7169
7229
|
*
|
|
7170
7230
|
* @param {DOMStringMap} dataset
|
|
7171
7231
|
* @param {HTMLInputElement|HTMLTextAreaElement} _el
|
|
@@ -7198,6 +7258,14 @@
|
|
|
7198
7258
|
options.unit = unit;
|
|
7199
7259
|
}
|
|
7200
7260
|
|
|
7261
|
+
const newline = parseDatasetEnum(
|
|
7262
|
+
dataset.tigRulesBytesNewline,
|
|
7263
|
+
["\n", "\r", "\r\n"]
|
|
7264
|
+
);
|
|
7265
|
+
if (newline != null) {
|
|
7266
|
+
options.newline = newline;
|
|
7267
|
+
}
|
|
7268
|
+
|
|
7201
7269
|
return bytes(options);
|
|
7202
7270
|
};
|
|
7203
7271
|
|
|
@@ -7495,12 +7563,37 @@
|
|
|
7495
7563
|
|
|
7496
7564
|
/**
|
|
7497
7565
|
* バージョン(ビルド時に置換したいならここを差し替える)
|
|
7498
|
-
* 例: rollup replace で ""1.0
|
|
7566
|
+
* 例: rollup replace で ""1.1.0"" を package.json の version に置換
|
|
7499
7567
|
*/
|
|
7500
7568
|
// @ts-ignore
|
|
7501
7569
|
// eslint-disable-next-line no-undef
|
|
7502
|
-
const version = "1.0
|
|
7570
|
+
const version = "1.1.0" ;
|
|
7571
|
+
|
|
7572
|
+
/**
|
|
7573
|
+
* UMD公開時のグローバルオブジェクト
|
|
7574
|
+
*/
|
|
7575
|
+
const TextInputGuard = {
|
|
7576
|
+
attach,
|
|
7577
|
+
attachAll,
|
|
7578
|
+
autoAttach,
|
|
7579
|
+
rules,
|
|
7580
|
+
numeric,
|
|
7581
|
+
digits,
|
|
7582
|
+
comma,
|
|
7583
|
+
imeOff,
|
|
7584
|
+
kana,
|
|
7585
|
+
ascii,
|
|
7586
|
+
filter,
|
|
7587
|
+
length,
|
|
7588
|
+
width,
|
|
7589
|
+
bytes,
|
|
7590
|
+
prefix,
|
|
7591
|
+
suffix,
|
|
7592
|
+
trim,
|
|
7593
|
+
version
|
|
7594
|
+
};
|
|
7503
7595
|
|
|
7596
|
+
exports.TextInputGuard = TextInputGuard;
|
|
7504
7597
|
exports.ascii = ascii;
|
|
7505
7598
|
exports.attach = attach;
|
|
7506
7599
|
exports.attachAll = attachAll;
|