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]);
79 MIM.error_return = function (msg, ret)
85 MIM.first_element = function (node)
88 return MIM.next_element (node);
91 MIM.next_element = function (node)
93 var element = node.childNodes[node.mim_index++];
94 while (element && element.nodeType != 1)
95 element = node.childNodes[node.mim_index++];
99 MIM.check_map = function (im, map)
101 var rules = map.getElementsByTagName ('rule');
102 var len = rules.length;
104 for (var i = 0; i < len; i++)
107 var elm = MIM.first_element (rule);
109 if (!elm || elm.nodeName != 'keyseq')
111 while ((elm = MIM.next_element (rule)))
112 if (elm.nodeName != 'insert')
118 MIM.check_state = function (im, state)
120 var branches = state.getElementsByTagName ('branch');
121 var len = branches.length;
123 for (var i = 0; i < len; i++)
125 var branch = branches[i];
126 var elm = MIM.first_element (branch);
134 MIM.parse = function (im)
136 var maps = im.body.getElementsByTagName ('map');
137 var states = im.body.getElementsByTagName ('state');
141 if (! maps || maps.length == 0)
142 MIM.error_return ('No map', false);
144 MIM.error_return ('No state', false);
145 for (i = 0; i < maps.length; i++)
146 if (! MIM.check_map (im, maps[i]))
147 MIM.error_return ('Unsupported directive in map', false);
148 for (var i = 0; i < states.length; i++)
149 if (! MIM.check_state (im, states[i]))
150 MIM.error_return ('Unsupported directive in state', false);
154 MIM.register = function (lang, name, url)
156 var im = new MIM.im (lang, name, url);
157 if (! (lang in MIM.list))
158 MIM.list[lang] = new Array ();
159 MIM.list[lang][name] = im;
163 MIM.find = function (lang, name)
165 if (! (lang in MIM.list))
167 if (! (name in MIM.list[lang]))
169 return MIM.list[lang][name];
172 MIM.load_async = function (im)
174 var obj = (window.XMLHttpRequest ? new XMLHttpRequest ()
175 : window.ActiveXObject ? new ActiveXObject ("Msxml2.XMLHTTP")
179 alert ("XMLHttpRequest not supported");
180 obj.open ('GET', im.url, true);
181 im.status = 1; /* loading */
182 obj.onreadystatechange = function () {
183 if (obj.readyState == 4)
186 eval (obj.responseText);
187 im.status = 2; /* loaded */
189 alert ("load error:" + e.message + " at " + e.lineNumber
190 + " " + obj.responseText);
191 im.status = -1; /* load fail */
199 MIM.load_sync = function (im)
201 var obj = (window.XMLHttpRequest ? new XMLHttpRequest ()
202 : window.ActiveXObject ? new ActiveXObject ("Msxml2.XMLHTTP")
206 alert ("XMLHttpRequest not supported");
209 obj.open ('GET', im.url, false);
210 im.status = 1; /* loading */
213 eval (obj.responseText);
214 im.status = 2; /* loaded */
216 alert ("load error:" + e.message + " at " + e.lineNumber
217 + " " + obj.responseText);
218 im.status = -1; /* load fail */
223 obj.open ('GET', 'latn-post.mimx', false);
224 obj.overrideMimeType ('text/xml');
226 im.body = obj.responseXML;
227 document.AnXml = im.body;
228 if (! MIM.parse (im))
230 alert (im.parse_error);
234 var doc = document.implementation.createDocument ("", "", null);
236 doc.contentType = "text/xml";
237 doc.load ('latn-post.mimx');
238 document.AnXml = doc;
245 MIM.load = function (im)
247 var s = document.createElement ('script');
251 document.body.appendChild (s);
252 document.body.removeChild (s);
257 MIM.add_event_listener
258 = (window.addEventListener
259 ? function (target, type, listener) {
260 target.addEventListener (type, listener, false);
263 ? function (target, type, listener) {
264 target.attachEvent ('on' + type,
266 listener.call (target, window.event);
269 : function (target, type, listener) {
271 = function (e) { listener.call (target, e || window.event); };
275 var keys = new Array ();
277 keys[0x08] = 'backspace';
278 keys[0x0D] = 'return';
279 keys[0x1B] = 'escape';
280 keys[0x20] = 'space';
281 keys[0x21] = 'pageup';
282 keys[0x22] = 'pagedown';
287 keys[0x27] = 'right';
289 keys[0x2D] = 'insert';
290 keys[0x2E] = 'delete';
291 for (var i = 1; i <= 12; i++)
292 keys[111 + i] = "f" + i;
293 keys[0x90] = "numlock";
294 keys[0xF0] = "capslock";
295 MIM.special_key = keys;
298 MIM.decode_key = function (event)
300 var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
301 : event.charCode ? event.charCode
305 if (event.type == 'keydown')
307 key = MIM.special_key[key];
310 if (event.shiftKey) key = "S-" + key ;
313 key = String.fromCharCode (key);
314 if (event.altKey) key = "A-" + key ;
315 if (event.ctrlKey) key = "C-" + key ;
319 MIM.debug_print = function (event, ic)
323 if (! MIM.debug_nodes)
325 MIM.debug_nodes = new Array ();
326 MIM.debug_nodes['status'] = document.getElementById ('status');
327 MIM.debug_nodes['range'] = document.getElementById ('range');
328 MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
329 MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
330 MIM.debug_nodes['keyseq'] = document.getElementById ('keyseq');
332 var target = event.target;
333 var code = event.keyCode;
334 var ch = event.type == 'keydown' ? 0 : event.charCode;
335 var key = MIM.decode_key (event);
338 MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
339 MIM.debug_nodes['status'].innerHTML = ic.im.status;
340 for (var i = 0; i < ic.keyseq.length; i++)
341 keyseq += ic.keyseq[i];
342 MIM.debug_nodes['keyseq'].innerHTML = keyseq + ":" + ic.keyseq.length;
343 MIM.debug_nodes['range'].innerHTML = "" + ic.range[0] + "," + ic.range[1];;
346 MIM.get_range = function (target, range)
348 if (target.selectionStart != null)
351 range[0] = target.selectionStart;
352 range[1] = target.selectionEnd;
355 if (document.selection != null)
359 var range = document.selection.createRange ();
360 var bookmark = range.getBookmark ();
361 var value = target.value;
362 var saved_value = value;
363 var marker = "_#_MARKER_#_";
364 while (value.indexOf (marker) != -1)
366 var parent = range.parentElement ();
367 if (parent == null || parent.type != "textarea")
369 range[0] = range[1] = 0;
373 range.text = marker + range.text + marker;
374 contents = this.element.value;
375 range[0] = contents.indexOf (marker);
376 contents = contents.replace(marker, "");
377 range[1] = contents.indexOf(marker);
378 target.value = originalContents;
379 range.moveToBookmark (bookmark);
387 MIM.set_caret = function (target, pos)
389 if(/*@cc_on ! @*/ false) // IE
391 var range = target.createTextRange ();
392 range.move ('character', pos);
396 if (target.selectionStart != null) // Mozilla
399 target.setSelectionRange (pos, pos);
407 MIM.ic = function (im, target)
410 this.target = target;
412 this.keyseq = new Array ();
413 this.range = new Array (-1, -1);
417 MIM.ic.prototype.reset = function ()
420 while (this.keyseq.length > 0)
422 this.range[0] = this.range[1] = -1;
425 MIM.ic.prototype.check_caret = function ()
427 var from = this.range[0];
428 var to = this.range[1];
430 MIM.get_range (this.target, this.range);
433 if (this.range[0] != this.range[1] || to != this.range[0])
436 this.range[0] = from;
440 MIM.insert = function (ic, insert)
442 var text = ic.target.value;
443 ic.target.value = (text.substring (0, ic.range[0])
445 + text.substring (ic.range[1]));
446 ic.range[1] = ic.range[0] + insert.length;
447 MIM.set_caret (ic.target, ic.range[1]);
450 function keyseq_string (keyseq)
453 for (var i = 0; i < keyseq.length; i++)
458 MIM.handle_keyseq = function (event, ic)
460 var map = ic.im.lookup (ic.keyseq, 1000);
461 if (map instanceof Array)
463 MIM.insert (ic, map['_target_text']);
464 if (! ('_has_child' in map))
466 event.preventDefault ();
467 //document.getElementById ('text').value
468 //= keyseq_string (ic.keyseq) + " handled";
472 MIM.insert (ic, ic.im.lookup (ic.keyseq, map)['_target_text']);
478 ic.range[0] = ic.range[1];
479 if (ic.keyseq.length > 0)
480 MIM.handle_keyseq (event, ic);
485 //document.getElementById ('text').value
486 //= keyseq_string (ic.keyseq) + " unhandled";
490 MIM.reset_ic = function (event)
492 var ic = event.target.mim_ic;
497 MIM.keydown = function (event)
499 if (! (event.target.type == "text" || event.target.type == "textarea"))
502 var ic = event.target.mim_ic;
503 if (! ic || ic.im != MIM.current_im)
505 ic = new MIM.ic (MIM.current_im, event.target);
506 event.target.mim_ic = ic;
508 MIM.add_event_listener (event.target, 'blur', MIM.reset_ic);
509 MIM.debug_print (event, ic);
510 if (ic.im.status < 0)
513 ic.key = MIM.decode_key (event);
516 MIM.keypress = function (event)
518 if (! (event.target.type == "text" || event.target.type == "textarea"))
521 var ic = event.target.mim_ic;
524 MIM.debug_print (event, ic);
525 if (ic.im.status < 0)
528 ic.key = MIM.decode_key (event);
534 ic.keyseq.push (ic.key);
535 if (ic.im.status == 1) // Still loading.
537 MIM.handle_keyseq (event, ic);
541 MIM.select_im = function (event)
543 var target = event.target.parentNode;
544 while (target.tagName != "SELECT")
545 target = target.parentNode;
548 for (var lang in MIM.list)
549 for (var name in MIM.list[lang])
550 if (idx++ == target.selectedIndex)
552 im = MIM.list[lang][name];
555 document.getElementsByTagName ('body')[0].removeChild (target);
556 target.target.focus ();
557 if (im && im != MIM.current_im)
558 MIM.current_im = MIM.load (im);
561 MIM.destroy_menu = function (event)
563 if (event.target.tagName == "SELECT")
564 document.getElementsByTagName ('body')[0].removeChild (event.target);
567 MIM.select_menu = function (event)
569 var target = event.target;
571 if (! ((target.type == "text" || target.type == "textarea")
572 && event.which == 1 && event.ctrlKey))
575 var sel = document.createElement ('select');
576 sel.onclick = MIM.select_im;
577 sel.onmouseout = MIM.destroy_menu;
578 sel.style.position='absolute';
579 sel.style.left = (event.clientX - 10) + "px";
580 sel.style.top = (event.clientY - 10) + "px";
583 for (var lang in MIM.list)
584 for (var name in MIM.list[lang])
586 var option = document.createElement ('option');
587 var imname = lang + "-" + name;
588 option.appendChild (document.createTextNode (imname));
589 option.value = imname;
590 sel.appendChild (option);
591 if (MIM.list[lang][name] == MIM.current_im)
592 sel.selectedIndex = idx;
596 document.getElementsByTagName ('body')[0].appendChild (sel);
599 MIM.init = function ()
601 MIM.add_event_listener (window, 'keydown', MIM.keydown);
602 MIM.add_event_listener (window, 'keypress', MIM.keypress);
603 MIM.add_event_listener (window, 'mousedown', MIM.select_menu);
604 if (window.location == 'http://localhost/mim/index.html')
605 MIM.server = 'http://localhost/mim';
606 MIM.current_im = MIM.register ('latin', 'post', 'latn-post.js');
607 MIM.register ('th', 'kesmanee', 'th-kesmanee.js');
608 MIM.load_sync (MIM.current_im);
611 MIM.init_debug = function ()