VulpesOS/gaia/apps/keyboard/js/imes/jshangul/jshangul.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

299 lines
9.1 KiB
JavaScript
Executable file

/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* global KeyEvent, InputMethods */
'use strict';
(function() {
var keyboard, ime, composingText, JangmiIME;
var BACKSPACE = KeyEvent.DOM_VK_BACK_SPACE;
/*
JangmiIME
https://github.com/sangpire/jangmiIME
Copyright 2013, BYUN Sangpil. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
var isCompatibleJamoKeyCode;
var UTF8_HANGUL_SYLLABLES_START = 0xAC00;
var UTF8_HANGUL_COMPATIBILITY_START = 0x3131;
var UTF8_CHOSEONG_DIFF = '까'.charCodeAt(0) - '가'.charCodeAt(0);
var UTF8_JUNGSEONG_DIFF = '개'.charCodeAt(0) - '가'.charCodeAt(0);
var COMPATIBILITY_MULTI_JAMO_MAP = {
'ㄱㄱ' : 0x1, 'ㄷㄷ' : 0x7, 'ㅂㅂ' : 0x12, 'ㅅㅅ' : 0x15,
'ㅈㅈ' : 0x18, 'ㅗㅏ' : 0x27, 'ㅗㅐ' : 0x28, 'ㅗㅣ' : 0x29,
'ㅜㅓ' : 0x2C, 'ㅜㅔ' : 0x2E, 'ㅜㅣ' : 0x2E, 'ㅡㅣ' : 0x31
};
var CHO_SEONG_MAP = {
'ㄱ' : 0, 'ㄱㄱ' : 1, 'ㄲ' : 1, 'ㄴ' : 2, 'ㄷ' : 3,
'ㄷㄷ' : 4, 'ㄸ' : 4, 'ㄹ' : 5, 'ㅁ' : 6, 'ㅂ' : 7,
'ㅂㅂ' : 8, 'ㅃ' : 8, 'ㅅ' : 9, 'ㅅㅅ' : 10, 'ㅆ' : 10,
'ㅇ' : 11, 'ㅈ' : 12, 'ㅈㅈ' : 13, 'ㅉ' : 13, 'ㅊ' : 14,
'ㅋ' : 15, 'ㅌ' : 16, 'ㅍ' : 17, 'ㅎ' : 18
};
var JUNG_SEONG_MAP = {
'ㅏ' : 0, 'ㅐ' : 1, 'ㅑ' : 2, 'ㅒ' : 3, 'ㅓ' : 4,
'ㅔ' : 5, 'ㅕ' : 6, 'ㅖ' : 7, 'ㅗ' : 8, 'ㅘ' : 9,
'ㅗㅏ' : 9, 'ㅙ' : 10, 'ㅗㅐ' : 10, 'ㅚ' : 11, 'ㅗㅣ' : 11,
'ㅛ' : 12, 'ㅜ' : 13, 'ㅝ' : 14, 'ㅜㅓ' : 14, 'ㅞ' : 15,
'ㅜㅔ' : 15, 'ㅟ' : 16, 'ㅜㅣ' : 16, 'ㅠ' : 17, 'ㅡ' : 18,
'ㅢ' : 19, 'ㅡㅣ' : 19, 'ㅣ' : 20
};
var JONG_SEONG_MAP = {
'ㄱ' : 1, 'ㄲ' : 2, 'ㄱㅅ' : 3, 'ㄴ' : 4, 'ㄴㅈ' : 5,
'ㄴㅎ' : 6, 'ㄷ' : 7, 'ㄹ' : 8, 'ㄹㄱ' : 9, 'ㄹㅁ' : 10,
'ㄹㅂ' : 11, 'ㄹㅅ' : 12, 'ㄹㅌ' : 13, 'ㄹㅍ' : 14, 'ㄹㅎ' : 15,
'ㅁ' : 16, 'ㅂ' : 17, 'ㅂㅅ' : 18, 'ㅅ' : 19, 'ㅆ' : 20,
'ㅇ' : 21, 'ㅈ' : 22, 'ㅊ' : 23, 'ㅋ' : 24, 'ㅌ' : 25,
'ㅍ' : 26, 'ㅎ' : 27
};
isCompatibleJamoKeyCode = function(keyCode) {
return (0x3131 <= keyCode && keyCode <= 0x318e);
};
JangmiIME = (function() {
/*
@handler =
passed: (keyCode) ->
added: (gul) ->
changed: (gul) ->
*/
function JangmiIME(handler) {
this.handler = handler;
this.reset();
}
/*
Reset Current State.
*/
JangmiIME.prototype.reset = function() {
this.curJamo = null;
this.choSeong = [];
this.jungSeong = [];
this.jongSeong = [];
};
JangmiIME.prototype.added = function() {
var curGul;
curGul = this.peep();
if (curGul != null) {
return this.handler.added(curGul);
}
};
JangmiIME.prototype.changed = function() {
var curGul;
curGul = this.peep();
if (curGul != null) {
return this.handler.changed(curGul);
}
};
JangmiIME.prototype.isEmpty = function() {
return (this.choSeong.length +
this.jungSeong.length +
this.jongSeong.length) === 0;
};
/*
Add KeyCode
*/
JangmiIME.prototype.add = function(keyCode) {
var curJamo, jamo, temp;
if (!isCompatibleJamoKeyCode(keyCode)) {
this.reset();
return this.handler.passed(keyCode);
} else {
jamo = String.fromCharCode(keyCode);
if (this.curJamo == null) {
if (CHO_SEONG_MAP.hasOwnProperty(jamo)) {
this.curJamo = this.choSeong;
}
if (JUNG_SEONG_MAP.hasOwnProperty(jamo)) {
this.curJamo = this.jungSeong;
}
this.curJamo.push(jamo);
return this.added();
} else {
curJamo = this.curJamo.join('') + jamo;
switch (this.curJamo) {
case this.choSeong:
if (CHO_SEONG_MAP.hasOwnProperty(curJamo)) {
this.curJamo.push(jamo);
return this.changed();
} else {
if (JUNG_SEONG_MAP.hasOwnProperty(jamo)) {
this.jungSeong.push(jamo);
this.curJamo = this.jungSeong;
return this.changed();
} else {
this.reset();
return this.add(keyCode);
}
}
break;
case this.jungSeong:
if (JUNG_SEONG_MAP.hasOwnProperty(curJamo)) {
this.curJamo.push(jamo);
return this.changed();
} else {
if (this.choSeong.length > 0 &&
JONG_SEONG_MAP.hasOwnProperty(jamo)) {
this.jongSeong.push(jamo);
this.curJamo = this.jongSeong;
return this.changed();
} else {
this.reset();
return this.add(keyCode);
}
}
break;
case this.jongSeong:
if (JONG_SEONG_MAP.hasOwnProperty(curJamo)) {
this.curJamo.push(jamo);
return this.changed();
} else {
if (JUNG_SEONG_MAP.hasOwnProperty(jamo)) {
temp = this.curJamo.pop();
this.changed();
this.reset();
this.choSeong.push(temp);
this.jungSeong.push(jamo);
this.curJamo = this.jungSeong;
return this.added();
} else {
this.reset();
this.curJamo = null;
return this.add(keyCode);
}
}
}
}
}
};
/*
Delete Last Input
*/
JangmiIME.prototype.back = function() {
if (this.curJamo.length > 0) {
this.curJamo.pop();
return this.changed();
} else {
switch (this.curJamo) {
case this.jungSeong:
this.curJamo = this.choSeong;
return this.back();
case this.jongSeong:
this.curJamo = this.jungSeong;
return this.back();
}
}
};
/*
peep current hangul
*/
JangmiIME.prototype.peep = function() {
var code;
if (this.choSeong.length > 0 && this.jungSeong.length > 0) {
code = UTF8_HANGUL_SYLLABLES_START +
CHO_SEONG_MAP[this.choSeong.join('')] * UTF8_CHOSEONG_DIFF;
code += JUNG_SEONG_MAP[this.jungSeong.join('')] * UTF8_JUNGSEONG_DIFF;
if (this.jongSeong.length > 0) {
code += JONG_SEONG_MAP[this.jongSeong.join('')];
}
} else {
if (this.choSeong.length === 1) {
code = this.choSeong[0].charCodeAt(0);
} else if (this.choSeong.length > 1) {
code = UTF8_HANGUL_COMPATIBILITY_START +
COMPATIBILITY_MULTI_JAMO_MAP[this.choSeong.join('')];
} else if (this.jungSeong.length === 1) {
code = this.jungSeong[0].charCodeAt(0);
} else if (this.jungSeong.length > 1) {
code = UTF8_HANGUL_COMPATIBILITY_START +
COMPATIBILITY_MULTI_JAMO_MAP[this.jungSeong.join('')];
} else {
return '';
}
}
return String.fromCharCode(code);
};
return JangmiIME;
})();
})();
InputMethods.jshangul = {
init: init,
click: click,
deactivate: deactivate
};
function setComposition(gul) {
composingText = gul;
keyboard.setComposition(gul);
}
function endComposition() {
if (composingText.length > 0) {
keyboard.endComposition(composingText);
composingText = '';
}
}
function init(interfaceObject) {
keyboard = interfaceObject;
composingText = '';
ime = new JangmiIME({
passed: function(keyCode) {
endComposition();
keyboard.sendKey(keyCode);
},
added: function(gul) {
endComposition();
setComposition(gul);
},
changed: function(gul) {
setComposition(gul);
}
});
}
function deactivate() {
endComposition();
ime.reset();
}
function click(keycode, x, y) {
if (keycode === BACKSPACE && !ime.isEmpty()) {
ime.back();
} else {
ime.add(keycode);
}
keyboard.setUpperCase({
isUpperCase: false
});
}
})();