1 // -* coding: utf-8; -*
5 // URL of the input method server.
6 MIM.server = "http://www.m17n.org/common/mim-js",
7 // Boolean flag to tell if MIM is active or not.
9 // Boolean flag to tell if MIM is running in debug mode or not.
11 // List of registered input methods.
12 MIM.list = new Array ();
13 // Currently selected input method.
14 MIM.current_im = false;
16 MIM.im = function (lang, name, filename)
18 this.status = 0; /* 0: not-yet-loaded, 1:loading, 2:loaded, -1:error */
19 this.url = MIM.server + "/" + filename;
25 function add_keystring (map, keystring, str)
29 var intermediate_string = "";
31 for (i = 0; i < keystring.length; i++)
33 c = keystring.charAt (i);
37 if ('_target_text' in map)
38 intermediate_string = map['_target_text'];
40 intermediate_string += c;
44 newmap = new Array ();
46 map['_has_child'] = true;
48 intermediate_string += c;
49 map['_target_text'] = intermediate_string;
52 map['_target_text'] = str;
55 this.lookup = function (keyseq, limit)
57 var map = this.keymap;
59 if (limit > keyseq.length)
60 limit = keyseq.length;
61 for (var i = 0; i < limit; i++)
71 this.load_map = function (mapdef)
73 this.keymap = new Array ();
74 for (var keystring in mapdef)
75 add_keystring (this.keymap, keystring, mapdef[keystring]);
78 this.load_map_node = function ()
80 this.keymap = new Array ();
81 var maps = this.body.getElementsByTagName ('map');
83 var rules = map.getElementsByTagName ('rule');
84 for (var i = 0; i < rules.length; i++)
87 var keyseq_elm = MIM.first_element (rule);
88 var keystring = keyseq_elm.attributes[0].nodeValue;
89 var insert_elm = MIM.next_element (rule);
90 var str = insert_elm.attributes[0].nodeValue;
91 add_keystring (this.keymap, keystring, str);
96 MIM.error_return = function (msg, ret)
102 MIM.first_element = function (node)
105 return MIM.next_element (node);
108 MIM.next_element = function (node)
110 var element = node.childNodes[node.mim_index++];
111 while (element && element.nodeType != 1)
112 element = node.childNodes[node.mim_index++];
116 MIM.check_map = function (im, map)
118 var rules = map.getElementsByTagName ('rule');
119 var len = rules.length;
121 for (var i = 0; i < len; i++)
124 var elm = MIM.first_element (rule);
126 if (!elm || elm.nodeName != 'keyseq')
128 while ((elm = MIM.next_element (rule)))
129 if (elm.nodeName != 'insert')
135 MIM.check_state = function (im, state)
137 var branches = state.getElementsByTagName ('branch');
138 var len = branches.length;
140 for (var i = 0; i < len; i++)
142 var branch = branches[i];
143 var elm = MIM.first_element (branch);
151 MIM.parse = function (im)
153 var maps = im.body.getElementsByTagName ('map');
154 var states = im.body.getElementsByTagName ('state');
158 if (! maps || maps.length == 0)
159 MIM.error_return ('No map', false);
161 MIM.error_return ('No state', false);
162 for (i = 0; i < maps.length; i++)
163 if (! MIM.check_map (im, maps[i]))
164 MIM.error_return ('Unsupported directive in map', false);
165 for (var i = 0; i < states.length; i++)
166 if (! MIM.check_state (im, states[i]))
167 MIM.error_return ('Unsupported directive in state', false);
172 MIM.register = function (lang, name, url)
174 var im = new MIM.im (lang, name, url);
175 if (! (lang in MIM.list))
176 MIM.list[lang] = new Array ();
177 MIM.list[lang][name] = im;
181 MIM.find = function (lang, name)
183 if (! (lang in MIM.list))
185 if (! (name in MIM.list[lang]))
187 return MIM.list[lang][name];
190 MIM.load_async = function (im)
192 var obj = (window.XMLHttpRequest ? new XMLHttpRequest ()
193 : window.ActiveXObject ? new ActiveXObject ("Msxml2.XMLHTTP")
197 alert ("XMLHttpRequest not supported");
198 obj.open ('GET', im.url, true);
199 im.status = 1; /* loading */
200 obj.onreadystatechange = function () {
201 if (obj.readyState == 4)
204 eval (obj.responseText);
205 im.status = 2; /* loaded */
207 alert ("load error:" + e.message + " at " + e.lineNumber
208 + " " + obj.responseText);
209 im.status = -1; /* load fail */
217 MIM.load_sync = function (im)
219 var obj = (window.XMLHttpRequest ? new XMLHttpRequest ()
220 : window.ActiveXObject ? new ActiveXObject ("Msxml2.XMLHTTP")
224 alert ("XMLHttpRequest not supported");
227 obj.open ('GET', im.url, false);
229 eval (obj.responseText);
231 alert ("obj.responseText");
236 obj.open ('GET', 'latn-post.mimx', false);
237 obj.overrideMimeType ('text/xml');
239 im.body = obj.responseXML;
240 document.AnXml = im.body;
243 alert (im.parse_error);
248 MIM.load = function (im)
250 var s = document.createElement ('script');
254 document.body.appendChild (s);
255 document.body.removeChild (s);
262 MIM.add_event_listener
263 = (window.addEventListener
264 ? function (target, type, listener) {
265 target.addEventListener (type, listener, false);
268 ? function (target, type, listener) {
269 target.attachEvent ('on' + type,
271 listener.call (target, window.event);
274 : function (target, type, listener) {
276 = function (e) { listener.call (target, e || window.event); };
280 var keys = new Array ();
282 keys[0x08] = 'backspace';
283 keys[0x0D] = 'return';
284 keys[0x1B] = 'escape';
285 keys[0x20] = 'space';
286 keys[0x21] = 'pageup';
287 keys[0x22] = 'pagedown';
292 keys[0x27] = 'right';
294 keys[0x2D] = 'insert';
295 keys[0x2E] = 'delete';
296 for (var i = 1; i <= 12; i++)
297 keys[111 + i] = "f" + i;
298 keys[0x90] = "numlock";
299 keys[0xF0] = "capslock";
300 MIM.special_key = keys;
303 MIM.decode_key = function (event)
305 var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
306 : event.charCode ? event.charCode
310 if (event.type == 'keydown')
312 key = MIM.special_key[key];
315 if (event.shiftKey) key = "S-" + key ;
318 key = String.fromCharCode (key);
319 if (event.altKey) key = "A-" + key ;
320 if (event.ctrlKey) key = "C-" + key ;
324 MIM.debug_print = function (event, ic)
328 if (! MIM.debug_nodes)
330 MIM.debug_nodes = new Array ();
331 MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
332 MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
333 MIM.debug_nodes['status0'] = document.getElementById ('status0');
334 MIM.debug_nodes['status1'] = document.getElementById ('status1');
335 MIM.debug_nodes['keyseq0'] = document.getElementById ('keyseq0');
336 MIM.debug_nodes['keyseq1'] = document.getElementById ('keyseq1');
337 MIM.debug_nodes['range0'] = document.getElementById ('range0');
338 MIM.debug_nodes['range1'] = document.getElementById ('range1');
340 var target = event.target;
341 var code = event.keyCode;
342 var ch = event.type == 'keydown' ? 0 : event.charCode;
343 var key = MIM.decode_key (event);
347 MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
348 index = (event.type == 'keydown' ? '0' : '1');
349 MIM.debug_nodes['status' + index].innerHTML = ic.im.status;
350 for (var i = 0; i < ic.keyseq.length; i++)
351 keyseq += ic.keyseq[i];
352 MIM.debug_nodes['keyseq' + index].innerHTML
353 = keyseq + ":" + ic.keyseq.length;
354 MIM.debug_nodes['range' + index].innerHTML
355 = "" + ic.range[0] + ":" + ic.range[1];
358 MIM.get_range = function (target, range)
360 if (target.selectionStart != null) // for Mozilla
362 range[0] = target.selectionStart;
363 range[1] = target.selectionEnd;
367 var r = document.selection.createRange ();
368 var rr = r.duplicate ();
370 rr.moveToElementText (target);
371 rr.setEndPoint ('EndToEnd', range);
372 range[0] = rr.text.length - r.text.length;
373 range[1] = rr.text.length;
377 MIM.set_caret = function (target, pos)
379 if (target.selectionStart != null) // Mozilla
382 target.setSelectionRange (pos, pos);
386 var range = target.createTextRange ();
387 range.move ('character', pos);
392 MIM.ic = function (im, target)
395 this.target = target;
397 this.keyseq = new Array ();
398 this.range = new Array (0, 0);
402 MIM.ic.prototype.reset = function ()
405 while (this.keyseq.length > 0)
409 MIM.ic.prototype.check_range = function ()
411 var from = this.range[0], to = this.range[1];
413 MIM.get_range (this.target, this.range);
414 if (this.range[0] != this.range[1] || to != this.range[1])
417 this.range[0] = from;
420 MIM.insert = function (ic, insert)
422 var text = ic.target.value;
423 ic.target.value = (text.substring (0, ic.range[0])
425 + text.substring (ic.range[1]));
426 ic.range[1] = ic.range[0] + insert.length;
427 MIM.set_caret (ic.target, ic.range[1]);
430 function keyseq_string (keyseq)
433 for (var i = 0; i < keyseq.length; i++)
438 MIM.handle_keyseq = function (event, ic)
440 var map = ic.im.lookup (ic.keyseq, 1000);
441 if (map instanceof Array)
443 MIM.insert (ic, map['_target_text']);
444 if (! ('_has_child' in map))
447 ic.range[0] = ic.range[1];
449 event.preventDefault ();
450 //document.getElementById ('text').value
451 //= keyseq_string (ic.keyseq) + " handled";
455 MIM.insert (ic, ic.im.lookup (ic.keyseq, map)['_target_text']);
461 ic.range[0] = ic.range[1];
462 if (ic.keyseq.length > 0)
463 MIM.handle_keyseq (event, ic);
468 //document.getElementById ('text').value
469 //= keyseq_string (ic.keyseq) + " unhandled";
473 MIM.reset_ic = function (event)
475 var ic = event.target.mim_ic;
480 MIM.keydown = function (event)
482 var target = event.target;
483 if (! (target.type == "text" || target.type == "textarea"))
486 var ic = target.mim_ic;
487 if (! ic || ic.im != MIM.current_im)
489 ic = new MIM.ic (MIM.current_im, target);
491 MIM.add_event_listener (target, 'blur', MIM.reset_ic);
493 if (ic.im.status < 0)
496 MIM.debug_print (event, ic);
497 ic.key = MIM.decode_key (event);
500 MIM.keypress = function (event)
502 if (! (event.target.type == "text" || event.target.type == "textarea"))
505 var ic = event.target.mim_ic;
509 if (ic.im.status < 0)
512 ic.key = MIM.decode_key (event);
518 ic.keyseq.push (ic.key);
519 if (ic.im.status == 1) // Still loading.
521 MIM.handle_keyseq (event, ic);
523 MIM.debug_print (event, ic);
528 MIM.select_im = function (event)
530 var target = event.target.parentNode;
531 while (target.tagName != "SELECT")
532 target = target.parentNode;
535 for (var lang in MIM.list)
536 for (var name in MIM.list[lang])
537 if (idx++ == target.selectedIndex)
539 im = MIM.list[lang][name];
542 document.getElementsByTagName ('body')[0].removeChild (target);
543 target.target.focus ();
544 if (im && im != MIM.current_im)
545 MIM.current_im = MIM.load_sync (im);
548 MIM.destroy_menu = function (event)
550 if (event.target.tagName == "SELECT")
551 document.getElementsByTagName ('body')[0].removeChild (event.target);
554 MIM.select_menu = function (event)
556 var target = event.target;
558 if (! ((target.type == "text" || target.type == "textarea")
559 && event.which == 1 && event.ctrlKey))
562 var sel = document.createElement ('select');
563 sel.onclick = MIM.select_im;
564 sel.onmouseout = MIM.destroy_menu;
565 sel.style.position='absolute';
566 sel.style.left = (event.clientX - 10) + "px";
567 sel.style.top = (event.clientY - 10) + "px";
570 for (var lang in MIM.list)
571 for (var name in MIM.list[lang])
573 var option = document.createElement ('option');
574 var imname = lang + "-" + name;
575 option.appendChild (document.createTextNode (imname));
576 option.value = imname;
577 sel.appendChild (option);
578 if (MIM.list[lang][name] == MIM.current_im)
579 sel.selectedIndex = idx;
583 document.getElementsByTagName ('body')[0].appendChild (sel);
586 MIM.textinput = function (event)
590 for (var i = 0; i < event.data.length; i++)
592 if (event.data.charAt (i) == 'あ')
594 str += 'ア'; changed = true;
597 str += event.data.charAt (i);
602 var e = document.createEvent ('TextEvent');
603 e.initTextEvent ('textInput', event.canBuggle, event.cancelable, event.view,
604 str, event.inputMode);
605 document.getElementById ('text').value = str;
606 event.target.dispatchEvent (e);
610 MIM.init = function ()
612 MIM.add_event_listener (window, 'keydown', MIM.keydown);
613 MIM.add_event_listener (window, 'keypress', MIM.keypress);
614 MIM.add_event_listener (window, 'mousedown', MIM.select_menu);
615 MIM.add_event_listener (window, 'textInput', MIM.textinput);
616 if (window.location == 'http://localhost/mim/index.html')
617 MIM.server = 'http://localhost/mim';
618 MIM.current_im = MIM.register ('latin', 'post', 'latn-post.js');
619 MIM.register ('th', 'kesmanee', 'th-kesmanee.js');
620 MIM.load_sync (MIM.current_im);
623 MIM.init_debug = function ()