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