hangul-unicode-composer 1.6.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/README.md +101 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/keyboard/key/KeyStatus.d.ts +6 -0
- package/dist/keyboard/key/KeyStatus.js +6 -0
- package/dist/keyboard/keyMeta/KeyInfo.d.ts +5 -0
- package/dist/keyboard/keyMeta/KeyInfo.js +5 -0
- package/dist/keyboard/keyMeta/KeyType.d.ts +14 -0
- package/dist/keyboard/keyMeta/KeyType.js +14 -0
- package/dist/text/HangleTextEvent.d.ts +24 -0
- package/dist/text/HangleTextEvent.js +29 -0
- package/dist/text/HangleUnicodeComposer.d.ts +133 -0
- package/dist/text/HangleUnicodeComposer.js +629 -0
- package/dist/text/HangulTextEvent.d.ts +24 -0
- package/dist/text/HangulTextEvent.js +29 -0
- package/dist/text/HangulUnicodeComposer.d.ts +133 -0
- package/dist/text/HangulUnicodeComposer.js +629 -0
- package/package.json +35 -0
- package/src/index.ts +5 -0
- package/src/keyboard/key/KeyStatus.ts +6 -0
- package/src/keyboard/keyMeta/KeyInfo.ts +5 -0
- package/src/keyboard/keyMeta/KeyType.ts +16 -0
- package/src/text/HangulTextEvent.ts +34 -0
- package/src/text/HangulUnicodeComposer.ts +641 -0
- package/src/text/__tests__/HangulUnicodeComposer.test.ts +162 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
import { HangulTextEvent } from "./HangulTextEvent.js";
|
|
2
|
+
class ArchivePack {
|
|
3
|
+
key = "";
|
|
4
|
+
compositionString = "";
|
|
5
|
+
extra = "";
|
|
6
|
+
_instant = [];
|
|
7
|
+
get instant() {
|
|
8
|
+
return this._instant;
|
|
9
|
+
}
|
|
10
|
+
set instant(value) {
|
|
11
|
+
this._instant = [...value];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* HangulUnicodeComposer
|
|
16
|
+
* Ports the AS3 한글 조합 라이브러리 to TypeScript.
|
|
17
|
+
* Composes Korean Jamo characters into Unicode Hangul syllables.
|
|
18
|
+
*/
|
|
19
|
+
export class HangulUnicodeComposer extends EventTarget {
|
|
20
|
+
/**
|
|
21
|
+
* Initial consonant (초성) Unicode values
|
|
22
|
+
*/
|
|
23
|
+
static INITIAL = [
|
|
24
|
+
0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, 0x3142, 0x3143, 0x3145,
|
|
25
|
+
0x3146, 0x3147, 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Medial vowel (중성) Unicode values
|
|
29
|
+
*/
|
|
30
|
+
static MEDIAL = [
|
|
31
|
+
0x314f, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158,
|
|
32
|
+
0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160, 0x3161, 0x3162,
|
|
33
|
+
0x3163
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Final consonant (종성) Unicode values. Index 0 is empty (no final consonant).
|
|
37
|
+
*/
|
|
38
|
+
static FINAL = [
|
|
39
|
+
"", 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3139, 0x313a,
|
|
40
|
+
0x313b, 0x313c, 0x313d, 0x313e, 0x313f, 0x3140, 0x3141, 0x3142, 0x3144, 0x3145,
|
|
41
|
+
0x3146, 0x3147, 0x3148, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e
|
|
42
|
+
];
|
|
43
|
+
static used = false;
|
|
44
|
+
/**
|
|
45
|
+
* Helper to make one letter by composing 3 syllables.
|
|
46
|
+
*/
|
|
47
|
+
static getString3Syllables(init, mid, fin) {
|
|
48
|
+
const uni = new HangulUnicodeComposer();
|
|
49
|
+
return uni.combine3Syllables(init, mid, fin);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns true if the character is a Hangeul Jaeum (consonant) in the range 3131 ~ 314E.
|
|
53
|
+
*/
|
|
54
|
+
static isHangulJaeum(char) {
|
|
55
|
+
const uni = new HangulUnicodeComposer();
|
|
56
|
+
return uni.compatibleJaeum(char);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns true if the character is a Hangeul Jamo in the range 3130 ~ 318F.
|
|
60
|
+
*/
|
|
61
|
+
static isHangulJamo(char) {
|
|
62
|
+
const uni = new HangulUnicodeComposer();
|
|
63
|
+
return uni.compatibleHangulJamo(char);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns true if the character is a Hangeul Moeum (vowel) in the range 314F ~ 3163.
|
|
67
|
+
*/
|
|
68
|
+
static isHangulMoeum(char) {
|
|
69
|
+
const uni = new HangulUnicodeComposer();
|
|
70
|
+
return uni.compatibleMoeum(char);
|
|
71
|
+
}
|
|
72
|
+
compositionString = "";
|
|
73
|
+
extra = "";
|
|
74
|
+
restrict = 3000;
|
|
75
|
+
instant = [];
|
|
76
|
+
archives = [];
|
|
77
|
+
// Callbacks for easy JS integration
|
|
78
|
+
onUpdate;
|
|
79
|
+
onLimited;
|
|
80
|
+
onError;
|
|
81
|
+
constructor() {
|
|
82
|
+
super();
|
|
83
|
+
if (HangulUnicodeComposer.used)
|
|
84
|
+
return;
|
|
85
|
+
HangulUnicodeComposer.used = true;
|
|
86
|
+
const re = "* hangul unicode composer for TS lib : ver- " + this.ver() + "\n" +
|
|
87
|
+
"* homepage-blog.hansune.com : maker-han hyon soo\n";
|
|
88
|
+
console.log(re);
|
|
89
|
+
}
|
|
90
|
+
ver() {
|
|
91
|
+
return "1.6";
|
|
92
|
+
}
|
|
93
|
+
get instantChars() {
|
|
94
|
+
return this.instant;
|
|
95
|
+
}
|
|
96
|
+
set instantChars(value) {
|
|
97
|
+
this.instant = [...value];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Input Jaeum or Moeum to compose.
|
|
101
|
+
*/
|
|
102
|
+
addJamo(char) {
|
|
103
|
+
if (!this.compatibleHangulJamo(char))
|
|
104
|
+
return;
|
|
105
|
+
this.instant.push(char);
|
|
106
|
+
this.instantUpdate();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Save current composed strings by key.
|
|
110
|
+
*/
|
|
111
|
+
archive(key, txt = null) {
|
|
112
|
+
let match = false;
|
|
113
|
+
for (const ap of this.archives) {
|
|
114
|
+
if (ap.key === key) {
|
|
115
|
+
if (txt !== null) {
|
|
116
|
+
ap.compositionString = txt;
|
|
117
|
+
ap.instant = [];
|
|
118
|
+
ap.extra = "";
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
ap.compositionString = this.compositionString;
|
|
122
|
+
ap.instant = this.instant;
|
|
123
|
+
ap.extra = this.extra;
|
|
124
|
+
}
|
|
125
|
+
match = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!match) {
|
|
129
|
+
const newA = new ArchivePack();
|
|
130
|
+
if (txt !== null) {
|
|
131
|
+
newA.compositionString = txt;
|
|
132
|
+
newA.instant = [];
|
|
133
|
+
newA.extra = "";
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
newA.compositionString = this.compositionString;
|
|
137
|
+
newA.instant = [...this.instant];
|
|
138
|
+
newA.extra = this.extra;
|
|
139
|
+
}
|
|
140
|
+
newA.key = key;
|
|
141
|
+
this.archives.push(newA);
|
|
142
|
+
}
|
|
143
|
+
return this.archives.length;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Restore composed state from the saved key.
|
|
147
|
+
*/
|
|
148
|
+
restore(key) {
|
|
149
|
+
let match = false;
|
|
150
|
+
for (const ap of this.archives) {
|
|
151
|
+
if (ap.key === key) {
|
|
152
|
+
this.compositionString = ap.compositionString;
|
|
153
|
+
this.instant = [...ap.instant];
|
|
154
|
+
this.extra = ap.extra;
|
|
155
|
+
match = true;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!match) {
|
|
160
|
+
return "";
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
return this.compositionString + this.extra;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Logic engine to process the composition of characters in instant array.
|
|
168
|
+
*/
|
|
169
|
+
instantUpdate() {
|
|
170
|
+
switch (this.instant.length) {
|
|
171
|
+
case 0:
|
|
172
|
+
this.extra = "";
|
|
173
|
+
break;
|
|
174
|
+
case 1:
|
|
175
|
+
if (this.compatibleMoeum(this.instant[0])) {
|
|
176
|
+
this.compositionString += this.instant[0];
|
|
177
|
+
this.instant = [];
|
|
178
|
+
this.extra = "";
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this.extra = this.instant[0];
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
case 2:
|
|
185
|
+
if (this.compatibleMoeum(this.instant[1])) {
|
|
186
|
+
if (this.isMedialJamo(this.instant[1])) {
|
|
187
|
+
this.extra = this.combine(this.instant[0], this.instant[1]);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
this.compositionString += this.instant[0];
|
|
191
|
+
this.extra = this.instant[1];
|
|
192
|
+
this.instant.shift();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
this.compositionString += this.instant[0];
|
|
197
|
+
this.extra = this.instant[1];
|
|
198
|
+
this.instant.shift();
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
case 3:
|
|
202
|
+
if (this.compatibleMoeum(this.instant[2])) {
|
|
203
|
+
if (this.combine(this.instant[1], this.instant[2]) !== "") {
|
|
204
|
+
this.instant[1] = this.combine(this.instant[1], this.instant[2]);
|
|
205
|
+
this.instant.pop();
|
|
206
|
+
this.extra = this.combine3Syllables(this.instant[0], this.instant[1], "");
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
this.compositionString += this.combine(this.instant[0], this.instant[1]);
|
|
210
|
+
this.compositionString += this.instant[2];
|
|
211
|
+
this.extra = "";
|
|
212
|
+
this.instant = [];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
if (this.isFinalJamo(this.instant[2])) {
|
|
217
|
+
if (this.isCombinableFinalJamo(this.instant[2]) || this.isInitialJamo(this.instant[2])) {
|
|
218
|
+
this.extra = this.combine3Syllables(this.instant[0], this.instant[1], this.instant[2]);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.instant[1], this.instant[2]);
|
|
222
|
+
this.extra = "";
|
|
223
|
+
this.instant = [];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
this.compositionString += this.combine(this.instant[0], this.instant[1]);
|
|
228
|
+
this.extra = this.instant[2];
|
|
229
|
+
this.instant.shift();
|
|
230
|
+
this.instant.shift();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
case 4:
|
|
235
|
+
if (this.compatibleMoeum(this.instant[3])) {
|
|
236
|
+
if (this.compatibleMoeum(this.instant[2])) {
|
|
237
|
+
this.compositionString += this.combine(this.instant[0], this.combine(this.instant[1], this.instant[2]));
|
|
238
|
+
this.extra = this.instant[3];
|
|
239
|
+
this.instant.shift();
|
|
240
|
+
this.instant.shift();
|
|
241
|
+
this.instant.shift();
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
if (this.isMedialJamo(this.instant[3])) {
|
|
245
|
+
this.compositionString += this.combine(this.instant[0], this.instant[1]);
|
|
246
|
+
this.extra = this.combine(this.instant[2], this.instant[3]);
|
|
247
|
+
this.instant.shift();
|
|
248
|
+
this.instant.shift();
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.instant[1], this.instant[2]);
|
|
252
|
+
this.extra = this.instant[3];
|
|
253
|
+
this.instant.shift();
|
|
254
|
+
this.instant.shift();
|
|
255
|
+
this.instant.shift();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
if (this.compatibleMoeum(this.instant[2])) {
|
|
261
|
+
if (this.isCombinableFinalJamo(this.instant[3]) || this.isInitialJamo(this.instant[3])) {
|
|
262
|
+
this.extra = this.combine3Syllables(this.instant[0], this.combine(this.instant[1], this.instant[2]), this.instant[3]);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.combine(this.instant[1], this.instant[2]), this.instant[3]);
|
|
266
|
+
this.extra = "";
|
|
267
|
+
this.instant = [];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
if (this.combine(this.instant[2], this.instant[3]) !== "") {
|
|
272
|
+
if (this.isInitialJamo(this.instant[3])) {
|
|
273
|
+
this.extra = this.combine3Syllables(this.instant[0], this.instant[1], this.combine(this.instant[2], this.instant[3]));
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.instant[1], this.combine(this.instant[2], this.instant[3]));
|
|
277
|
+
this.extra = "";
|
|
278
|
+
this.instant = [];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.instant[1], this.instant[2]);
|
|
283
|
+
this.extra = this.instant[3];
|
|
284
|
+
this.instant.shift();
|
|
285
|
+
this.instant.shift();
|
|
286
|
+
this.instant.shift();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
break;
|
|
291
|
+
case 5:
|
|
292
|
+
if (this.compatibleMoeum(this.instant[4])) {
|
|
293
|
+
if (this.combine(this.instant[1], this.instant[2]) !== "") {
|
|
294
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.combine(this.instant[1], this.instant[2]), "");
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.instant[1], this.instant[2]);
|
|
298
|
+
}
|
|
299
|
+
this.extra = this.combine(this.instant[3], this.instant[4]);
|
|
300
|
+
this.instant.shift();
|
|
301
|
+
this.instant.shift();
|
|
302
|
+
this.instant.shift();
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
if (this.compatibleJaeum(this.instant[4])) {
|
|
306
|
+
if (this.isInitialJamo(this.instant[4])) {
|
|
307
|
+
this.compositionString += this.extra;
|
|
308
|
+
this.extra = this.instant[4];
|
|
309
|
+
this.instant.shift();
|
|
310
|
+
this.instant.shift();
|
|
311
|
+
this.instant.shift();
|
|
312
|
+
this.instant.shift();
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
this.compositionString += this.extra;
|
|
316
|
+
this.compositionString += this.instant[3];
|
|
317
|
+
this.extra = "";
|
|
318
|
+
this.instant.shift();
|
|
319
|
+
this.instant.shift();
|
|
320
|
+
this.instant.shift();
|
|
321
|
+
this.instant.shift();
|
|
322
|
+
this.instant.shift();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.instant[1], this.combine(this.instant[2], this.instant[3]));
|
|
327
|
+
this.compositionString += this.instant[4];
|
|
328
|
+
this.extra = "";
|
|
329
|
+
this.instant = [];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
case 6:
|
|
334
|
+
if (this.combine(this.instant[4], this.instant[5]) !== "") {
|
|
335
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.combine(this.instant[1], this.instant[2]), this.instant[3]);
|
|
336
|
+
this.extra = this.combine(this.instant[4], this.instant[5]);
|
|
337
|
+
this.instant.shift();
|
|
338
|
+
this.instant.shift();
|
|
339
|
+
this.instant.shift();
|
|
340
|
+
this.instant.shift();
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
this.compositionString += this.combine3Syllables(this.instant[0], this.combine(this.instant[1], this.instant[2]), this.combine(this.instant[3], this.instant[4]));
|
|
344
|
+
this.extra = this.instant[5];
|
|
345
|
+
this.instant.shift();
|
|
346
|
+
this.instant.shift();
|
|
347
|
+
this.instant.shift();
|
|
348
|
+
this.instant.shift();
|
|
349
|
+
this.instant.shift();
|
|
350
|
+
}
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
if (this.compositionString.length + this.extra.length > this.restrict) {
|
|
354
|
+
this.backSpace();
|
|
355
|
+
this.dispatchComposerEvent(HangulTextEvent.LIMITED, this.compositionString + this.extra);
|
|
356
|
+
}
|
|
357
|
+
// console.log("<instant : " + this.instant.join(",") + ">");
|
|
358
|
+
this.dispatchComposerEvent(HangulTextEvent.UPDATE, this.compositionString + this.extra);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Helper to dispatch events and invoke corresponding callbacks.
|
|
362
|
+
*/
|
|
363
|
+
dispatchComposerEvent(type, text) {
|
|
364
|
+
const event = new HangulTextEvent(type, text);
|
|
365
|
+
this.dispatchEvent(event);
|
|
366
|
+
if (type === HangulTextEvent.UPDATE && this.onUpdate) {
|
|
367
|
+
this.onUpdate(text);
|
|
368
|
+
}
|
|
369
|
+
else if (type === HangulTextEvent.LIMITED && this.onLimited) {
|
|
370
|
+
this.onLimited(text);
|
|
371
|
+
}
|
|
372
|
+
else if (type === HangulTextEvent.ERROR && this.onError) {
|
|
373
|
+
this.onError(text);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Insert character that is not available to compose (e.g. English, numbers, symbols).
|
|
378
|
+
*/
|
|
379
|
+
addSpecialChar(char, at = -1) {
|
|
380
|
+
if (this.compositionString.length + this.extra.length + 1 > this.restrict) {
|
|
381
|
+
this.dispatchComposerEvent(HangulTextEvent.LIMITED, this.compositionString + this.extra);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
this.compositionString += this.extra;
|
|
385
|
+
if (at === -1)
|
|
386
|
+
at = this.compositionString.length;
|
|
387
|
+
const strA = this.compositionString.slice(0, at);
|
|
388
|
+
const strB = this.compositionString.slice(at);
|
|
389
|
+
this.compositionString = strA + char + strB;
|
|
390
|
+
this.extra = "";
|
|
391
|
+
this.instant = [];
|
|
392
|
+
this.dispatchComposerEvent(HangulTextEvent.UPDATE, this.compositionString);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Add Jamo by Unicode character code.
|
|
396
|
+
*/
|
|
397
|
+
addJamoUnicode(code) {
|
|
398
|
+
this.addJamo(String.fromCharCode(code));
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Delete character by backspace.
|
|
402
|
+
*/
|
|
403
|
+
backSpace(at = -1) {
|
|
404
|
+
if (this.instant.length > 0) {
|
|
405
|
+
this.instant.pop();
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
this.compositionString += this.extra;
|
|
409
|
+
if (at === -1)
|
|
410
|
+
at = this.compositionString.length;
|
|
411
|
+
this.compositionString =
|
|
412
|
+
this.compositionString.slice(0, at - 1) + this.compositionString.slice(at);
|
|
413
|
+
this.extra = "";
|
|
414
|
+
this.instant = [];
|
|
415
|
+
}
|
|
416
|
+
this.instantUpdate();
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Composes two Jamos and returns array with composed character or split ones.
|
|
420
|
+
*/
|
|
421
|
+
compare2Jamo(charA, charB) {
|
|
422
|
+
const re = [];
|
|
423
|
+
if (this.combine(charA, charB) !== "") {
|
|
424
|
+
re.push(this.combine(charA, charB));
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
re.push(charA);
|
|
428
|
+
re.push(charB);
|
|
429
|
+
}
|
|
430
|
+
return re;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Composes three Jamos.
|
|
434
|
+
*/
|
|
435
|
+
compare3Syllables(init, mid, fin) {
|
|
436
|
+
let re = [];
|
|
437
|
+
if (this.isInitialJamo(init) && this.isMedialJamo(mid) && this.isFinalJamo(fin)) {
|
|
438
|
+
re.push(this.combine3Syllables(init, mid, fin));
|
|
439
|
+
}
|
|
440
|
+
else if (this.isInitialJamo(init) && this.isMedialJamo(mid) && !this.isFinalJamo(fin)) {
|
|
441
|
+
re.push(this.combine3Syllables(init, mid, ""));
|
|
442
|
+
re.push(fin);
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
re = [init, mid, fin];
|
|
446
|
+
}
|
|
447
|
+
return re;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Checks if string is a valid Hangul syllable or Jamo.
|
|
451
|
+
*/
|
|
452
|
+
compatibleHangulJamo(char) {
|
|
453
|
+
if (char.length !== 1) {
|
|
454
|
+
throw new Error("1개의 글자가 필요합니다. only one character is available.");
|
|
455
|
+
}
|
|
456
|
+
const code = char.charCodeAt(0);
|
|
457
|
+
return code >= 0x3131 && code <= 0x3163;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Checks if character is a Jaeum.
|
|
461
|
+
*/
|
|
462
|
+
compatibleJaeum(char) {
|
|
463
|
+
const code = char.charCodeAt(0);
|
|
464
|
+
return code > 0x3130 && code < 0x314f;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Checks if character is a Moeum.
|
|
468
|
+
*/
|
|
469
|
+
compatibleMoeum(char) {
|
|
470
|
+
const code = char.charCodeAt(0);
|
|
471
|
+
return code > 0x314e && code < 0x3164;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Deletes characters at target index.
|
|
475
|
+
*/
|
|
476
|
+
del(at = -1) {
|
|
477
|
+
this.compositionString += this.extra;
|
|
478
|
+
const str = this.compositionString;
|
|
479
|
+
if (at === -1)
|
|
480
|
+
at = this.compositionString.length;
|
|
481
|
+
this.compositionString = str.slice(0, at) + str.slice(at + 2);
|
|
482
|
+
this.extra = "";
|
|
483
|
+
this.instant = [];
|
|
484
|
+
this.dispatchComposerEvent(HangulTextEvent.UPDATE, this.compositionString);
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Resets composer state.
|
|
488
|
+
*/
|
|
489
|
+
reset() {
|
|
490
|
+
this.compositionString = "";
|
|
491
|
+
this.extra = "";
|
|
492
|
+
this.instant = [];
|
|
493
|
+
this.archives = [];
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Checks if character is in FINAL.
|
|
497
|
+
*/
|
|
498
|
+
isFinalJamo(char) {
|
|
499
|
+
const code = char.charCodeAt(0);
|
|
500
|
+
return HangulUnicodeComposer.FINAL.indexOf(code) >= 0;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Checks if character is in INITIAL.
|
|
504
|
+
*/
|
|
505
|
+
isInitialJamo(char) {
|
|
506
|
+
const code = char.charCodeAt(0);
|
|
507
|
+
return HangulUnicodeComposer.INITIAL.indexOf(code) >= 0;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Checks if character is in MEDIAL.
|
|
511
|
+
*/
|
|
512
|
+
isMedialJamo(char) {
|
|
513
|
+
const code = char.charCodeAt(0);
|
|
514
|
+
return HangulUnicodeComposer.MEDIAL.indexOf(code) >= 0;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Adds space.
|
|
518
|
+
*/
|
|
519
|
+
space(at = -1) {
|
|
520
|
+
if (this.compositionString.length + this.extra.length + 1 > this.restrict) {
|
|
521
|
+
this.dispatchComposerEvent(HangulTextEvent.LIMITED, this.compositionString + this.extra);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
this.compositionString += this.extra;
|
|
525
|
+
if (at === -1)
|
|
526
|
+
at = this.compositionString.length;
|
|
527
|
+
const strA = this.compositionString.slice(0, at);
|
|
528
|
+
const strB = this.compositionString.slice(at);
|
|
529
|
+
this.compositionString = strA + " " + strB;
|
|
530
|
+
this.extra = "";
|
|
531
|
+
this.instant = [];
|
|
532
|
+
this.dispatchComposerEvent(HangulTextEvent.UPDATE, this.compositionString);
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Combines initial, medial, and final into a single Hangul syllable.
|
|
536
|
+
*/
|
|
537
|
+
combine3Syllables(init, mid, fin) {
|
|
538
|
+
const initCode = HangulUnicodeComposer.INITIAL.indexOf(init.charCodeAt(0));
|
|
539
|
+
const midCode = HangulUnicodeComposer.MEDIAL.indexOf(mid.charCodeAt(0));
|
|
540
|
+
const finCode = fin === "" ? 0 : HangulUnicodeComposer.FINAL.indexOf(fin.charCodeAt(0));
|
|
541
|
+
return String.fromCharCode((initCode * 588 + midCode * 28 + finCode) + 44032);
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Combine two characters (Jaeum + Jaeum, Moeum + Moeum, or Initial + Medial)
|
|
545
|
+
*/
|
|
546
|
+
combine(charA, charB) {
|
|
547
|
+
let re = "";
|
|
548
|
+
let compCode = 0;
|
|
549
|
+
const charCodeA = charA.charCodeAt(0);
|
|
550
|
+
const charCodeB = charB.charCodeAt(0);
|
|
551
|
+
if (this.compatibleJaeum(charA) && this.compatibleJaeum(charB)) {
|
|
552
|
+
switch (charCodeA) {
|
|
553
|
+
case 0x3131: // ㄱ
|
|
554
|
+
if (charCodeB === 0x3145)
|
|
555
|
+
compCode = 0x3133; // ㄳ
|
|
556
|
+
break;
|
|
557
|
+
case 0x3134: // ㄴ
|
|
558
|
+
if (charCodeB === 0x3148)
|
|
559
|
+
compCode = 0x3135; // ㄵ
|
|
560
|
+
if (charCodeB === 0x314e)
|
|
561
|
+
compCode = 0x3136; // ㄶ
|
|
562
|
+
break;
|
|
563
|
+
case 0x3139: // ㄹ
|
|
564
|
+
if (charCodeB === 0x3131)
|
|
565
|
+
compCode = 0x313a; // ㄺ
|
|
566
|
+
if (charCodeB === 0x3141)
|
|
567
|
+
compCode = 0x313b; // ㄻ
|
|
568
|
+
if (charCodeB === 0x3142)
|
|
569
|
+
compCode = 0x313c; // ㄼ
|
|
570
|
+
if (charCodeB === 0x3145)
|
|
571
|
+
compCode = 0x313d; // ㄽ
|
|
572
|
+
if (charCodeB === 0x314c)
|
|
573
|
+
compCode = 0x313e; // ㄾ
|
|
574
|
+
if (charCodeB === 0x314d)
|
|
575
|
+
compCode = 0x313f; // ㄿ
|
|
576
|
+
if (charCodeB === 0x314e)
|
|
577
|
+
compCode = 0x3140; // ㅀ
|
|
578
|
+
break;
|
|
579
|
+
case 0x3142: // ㅂ
|
|
580
|
+
if (charCodeB === 0x3145)
|
|
581
|
+
compCode = 0x3144; // ㅄ
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
else if (this.compatibleMoeum(charA) && this.compatibleMoeum(charB)) {
|
|
586
|
+
switch (charCodeA) {
|
|
587
|
+
case 0x3157: // ㅗ
|
|
588
|
+
if (charCodeB === 0x314f)
|
|
589
|
+
compCode = 0x3158; // ㅘ
|
|
590
|
+
if (charCodeB === 0x3150)
|
|
591
|
+
compCode = 0x3159; // ㅙ
|
|
592
|
+
if (charCodeB === 0x3163)
|
|
593
|
+
compCode = 0x315a; // ㅚ
|
|
594
|
+
break;
|
|
595
|
+
case 0x315c: // ㅜ
|
|
596
|
+
if (charCodeB === 0x3153)
|
|
597
|
+
compCode = 0x315d; // ㅝ
|
|
598
|
+
if (charCodeB === 0x3154)
|
|
599
|
+
compCode = 0x315e; // ㅞ
|
|
600
|
+
if (charCodeB === 0x3163)
|
|
601
|
+
compCode = 0x315f; // ㅟ
|
|
602
|
+
break;
|
|
603
|
+
case 0x3161: // ㅡ
|
|
604
|
+
if (charCodeB === 0x3163)
|
|
605
|
+
compCode = 0x3162; // ㅢ
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
else if (this.isInitialJamo(charA) && this.isMedialJamo(charB)) {
|
|
610
|
+
re = this.combine3Syllables(charA, charB, "");
|
|
611
|
+
}
|
|
612
|
+
if (compCode !== 0) {
|
|
613
|
+
return String.fromCharCode(compCode);
|
|
614
|
+
}
|
|
615
|
+
return re;
|
|
616
|
+
}
|
|
617
|
+
isCombinableFinalJamo(char) {
|
|
618
|
+
let re = false;
|
|
619
|
+
switch (char.charCodeAt(0)) {
|
|
620
|
+
case 0x3131: // ㄱ
|
|
621
|
+
case 0x3134: // ㄴ
|
|
622
|
+
case 0x3139: // ㄹ
|
|
623
|
+
case 0x3142: // ㅂ
|
|
624
|
+
re = true;
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
return re;
|
|
628
|
+
}
|
|
629
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hangul-unicode-composer",
|
|
3
|
+
"version": "1.6.0",
|
|
4
|
+
"description": "TypeScript port of HUC (Hangul Unicode Composer) for composing Korean characters",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"test:watch": "vitest"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"hangul",
|
|
15
|
+
"hangle",
|
|
16
|
+
"composer",
|
|
17
|
+
"korean",
|
|
18
|
+
"keyboard",
|
|
19
|
+
"unicode"
|
|
20
|
+
],
|
|
21
|
+
"author": "brownsoo",
|
|
22
|
+
"license": "Apache-2.0",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/brownsoo/Hangul-Unicode-Composer.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/brownsoo/Hangul-Unicode-Composer/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/brownsoo/Hangul-Unicode-Composer#readme",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "^5.4.5",
|
|
33
|
+
"vitest": "^1.6.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { HangulTextEvent } from "./text/HangulTextEvent.js";
|
|
2
|
+
export { HangulUnicodeComposer } from "./text/HangulUnicodeComposer.js";
|
|
3
|
+
export { KeyInfo } from "./keyboard/keyMeta/KeyInfo.js";
|
|
4
|
+
export { KeyType } from "./keyboard/keyMeta/KeyType.js";
|
|
5
|
+
export { KeyStatus } from "./keyboard/key/KeyStatus.js";
|