/* -*- 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 }); } })();