1 // -* coding: utf-8; -*
3 /* URL to scan JavaScript files defining MIM (M17N Input Method). */
4 var MIM_url = "http://www.m17n.org/common/mim-js";
6 /* Boolean flag to tell if MIM is active or not. */
7 var MIM_enabled = true;
11 var MIM_list = new Array ();
13 function M17N_im (url, lang, name)
15 this.status = 0; /* 0: not-yet-loaded, 1:loading, 2:loaded, -1:error */
21 function add_keystring (map, keystring, str)
25 var intermediate_string = "";
27 for (i = 0; i < keystring.length; i++)
29 c = keystring.charAt (i);
33 if ('_target_text' in map)
34 intermediate_string = map['_target_text'];
36 intermediate_string += c;
40 newmap = new Array ();
42 map['_has_child'] = true;
44 intermediate_string += c;
45 map['_target_text'] = intermediate_string;
48 map['_target_text'] = str;
51 function lookup_keyseq (keyseq, limit)
53 var map = this.keymap;
55 if (limit > keyseq.length)
56 limit = keyseq.length;
57 for (var i = 0; i < limit; i++)
67 function load_map (mapdef)
69 this.keymap = new Array ();
70 for (var keystring in mapdef)
71 add_keystring (this.keymap, keystring, mapdef[keystring]);
74 this.load_mapdef = load_map;
75 this.lookup = lookup_keyseq;
78 function MIM_register (url, lang, name)
80 var im = new M17N_im (url, lang, name);
81 if (! (lang in MIM_list))
82 MIM_list[lang] = new Array ();
83 MIM_list[lang][name] = im;
87 function MIM_find (lang, name)
89 if (! (lang in MIM_list))
91 if (! (name in MIM_list[lang]))
93 return MIM_list[lang][name];
97 function MIM_load_async (im)
99 var filename = MIM_url + "/" + im.url;
100 var obj = (window.XMLHttpRequest ? new XMLHttpRequest ()
101 : window.ActiveXObject ? new ActiveXObject ("Msxml2.XMLHTTP")
105 alert ("XMLHttpRequest not supported");
106 alert ("loading " + filename);
107 obj.open ('GET', filename, true);
108 im.status = 1; /* loading */
109 obj.onreadystatechange = function () {
110 alert (obj.readyState + ":" + obj.statusText);
111 if (obj.readyState == 4)
114 eval (obj.responseText);
115 im.status = 2; /* loaded */
116 alert ("loaded:"+obj.responseXML);
118 alert ("load error:" + e.message + " at " + e.lineNumber
119 + " " + obj.responseText);
120 im.status = -1; /* load fail */
128 function MIM_load (im)
130 var filename = MIM_url + "/" + im.url;
131 var s = document.createElement ('script');
135 document.body.appendChild (s);
137 //document.body.removeChild (s);
142 function MIM_add_event_listener (target, type, listener)
144 if (target.addEventListener)
145 target.addEventListener (type, listener, false);
146 else if (target.attachEvent)
147 target.attachEvent ('on' + type,
148 function() { listener.call (target, window.event); });
151 = function(e) { listener.call (target, e || window.event); };
154 var MIM_key = new Array ();
155 MIM_key[0x09] = 'tab';
156 MIM_key[0x08] = 'backspace';
157 MIM_key[0x0D] = 'return';
158 MIM_key[0x1B] = 'escape';
159 MIM_key[0x20] = 'space';
160 MIM_key[0x21] = 'pageup';
161 MIM_key[0x22] = 'pagedown';
162 MIM_key[0x23] = 'end';
163 MIM_key[0x24] = 'home';
164 MIM_key[0x25] = 'left';
165 MIM_key[0x26] = 'up';
166 MIM_key[0x27] = 'right';
167 MIM_key[0x28] = 'down';
168 MIM_key[0x2D] = 'insert';
169 MIM_key[0x2E] = 'delete';
170 for (var i = 1; i <= 12; i++)
171 MIM_key[111 + i] = "f" + i;
172 MIM_key[0x90] = "numlock";
173 MIM_key[0xF0] = "capslock";
175 function MIM_decode_key (event)
177 var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
178 : event.charCode ? event.charCode
182 if (event.type == 'keydown')
187 if (event.shiftKey) key = "S-" + key ;
190 key = String.fromCharCode (key);
191 if (event.altKey) key = "A-" + key ;
192 if (event.ctrlKey) key = "C-" + key ;
196 function debug_print (event, ic)
198 var target = event.target;
199 var code = event.keyCode;
200 var char = event.type == 'keydown' ? "?" : event.charCode;
201 var key = MIM_decode_key (event);
203 document.getElementById (event.type).innerHTML = "" + code + "/" + char + " : " + key;
204 document.getElementById ('status').innerHTML = ic.im.status;
206 for (var i = 0; i < ic.keyseq.length; i++)
207 keyseq += ic.keyseq[i];
208 document.getElementById ('keyseq').innerHTML = keyseq + ":" + ic.keyseq.length;
209 document.getElementById ('range').innerHTML = "" + ic.range[0] + "," + ic.range[1];;
212 function MIM_get_range (target, range)
214 if (target.selectionStart != null)
217 range[0] = target.selectionStart;
218 range[1] = target.selectionEnd;
221 if (document.selection != null)
225 var range = document.selection.createRange ();
226 var bookmark = range.getBookmark ();
227 var value = target.value;
228 var saved_value = value;
229 var marker = "_#_MARKER_#_";
230 while (value.indexOf (marker) != -1)
232 var parent = range.parentElement ();
233 if (parent == null || parent.type != "textarea")
235 range[0] = range[1] = 0;
239 range.text = marker + range.text + marker;
240 contents = this.element.value;
241 range[0] = contents.indexOf (marker);
242 contents = contents.replace(marker, "");
243 range[1] = contents.indexOf(marker);
244 target.value = originalContents;
245 range.moveToBookmark (bookmark);
253 function MIM_set_caret (target, pos)
255 if (target.selectionStart != null)
259 target.setSelectionRange (pos, pos);
262 if (target.createTextRange != null)
265 var range = target.createTextRange ();
266 range.move ('character', pos);
275 function MIM_ic (im, target)
278 this.target = target;
280 this.keyseq = new Array ();
281 this.range = new Array (-1, -1);
285 MIM_ic.prototype.reset = function ()
288 while (this.keyseq.length > 0)
290 this.range[0] = this.range[1] = -1;
293 MIM_ic.prototype.check_caret = function ()
295 var from = this.range[0];
296 var to = this.range[1];
298 MIM_get_range (this.target, this.range);
301 if (this.range[0] != this.range[1] || to != this.range[0])
304 this.range[0] = from;
308 function MIM_insert (ic, insert)
310 var text = ic.target.value;
311 ic.target.value = (text.substring (0, ic.range[0])
313 + text.substring (ic.range[1]));
314 ic.range[1] = ic.range[0] + insert.length;
315 MIM_set_caret (ic.target, ic.range[1]);
318 function keyseq_string (keyseq)
321 for (var i = 0; i < keyseq.length; i++)
326 function MIM_handle_keyseq (event, ic)
328 var map = ic.im.lookup (ic.keyseq, 1000);
329 if (map instanceof Array)
331 MIM_insert (ic, map['_target_text']);
332 if (! ('_has_child' in map))
334 event.preventDefault ();
335 //document.getElementById ('text').value
336 //= keyseq_string (ic.keyseq) + " handled";
340 MIM_insert (ic, ic.im.lookup (ic.keyseq, map)['_target_text']);
346 ic.range[0] = ic.range[1];
347 if (ic.keyseq.length > 0)
348 MIM_handle_keyseq (event, ic);
353 document.getElementById ('text').value
354 = keyseq_string (ic.keyseq) + " unhandled";
358 function MIM_reset_ic (event)
360 var ic = event.target.mim_ic;
365 function MIM_keydown (event)
367 if (! (event.target.type == "text" || event.target.type == "textarea"))
370 var ic = event.target.mim_ic;
371 if (! ic || ic.im != MIM_current_im)
373 ic = new MIM_ic (MIM_current_im, event.target);
374 event.target.mim_ic = ic;
376 MIM_add_event_listener (event.target, 'blur', MIM_reset_ic);
377 debug_print (event, ic);
378 if (ic.im.status < 0)
381 ic.key = MIM_decode_key (event);
384 function MIM_keypress (event)
386 if (! (event.target.type == "text" || event.target.type == "textarea"))
389 var ic = event.target.mim_ic;
392 debug_print (event, ic);
393 if (ic.im.status < 0)
396 ic.key = MIM_decode_key (event);
402 ic.keyseq.push (ic.key);
403 if (ic.im.status == 1) // Still loading.
405 MIM_handle_keyseq (event, ic);
409 function MIM_select_im (event)
411 var target = event.target.parentNode;
412 while (target.tagName != "SELECT")
413 target = target.parentNode;
416 for (var lang in MIM_list)
417 for (var name in MIM_list[lang])
418 if (idx++ == target.selectedIndex)
420 im = MIM_list[lang][name];
423 document.getElementsByTagName ('body')[0].removeChild (target);
424 target.target.focus ();
425 if (im && im != MIM_current_im)
426 MIM_current_im = MIM_load (im);
429 function MIM_destroy_menu (event)
431 if (event.target.tagName == "SELECT")
432 document.getElementsByTagName ('body')[0].removeChild (event.target);
435 function MIM_select_menu (event)
437 var target = event.target;
439 if (! ((target.type == "text" || target.type == "textarea")
440 && event.which == 1 && event.ctrlKey))
443 var sel = document.createElement ('select');
444 sel.onclick = MIM_select_im;
445 sel.onmouseout = MIM_destroy_menu;
446 sel.style.position='absolute';
447 sel.style.left = (event.clientX - 10) + "px";
448 sel.style.top = (event.clientY - 10) + "px";
451 for (var lang in MIM_list)
452 for (var name in MIM_list[lang])
454 var option = document.createElement ('option');
455 var imname = lang + "-" + name;
456 option.appendChild (document.createTextNode (imname));
457 option.value = imname;
458 sel.appendChild (option);
459 if (MIM_list[lang][name] == MIM_current_im)
460 sel.selectedIndex = idx;
464 document.getElementsByTagName ('body')[0].appendChild (sel);
467 MIM_current_im = MIM_register ('latn-post.js', 'latin', 'post');
468 MIM_register ('th-kesmanee.js', 'th', 'kesmanee');
472 MIM_add_event_listener (window, 'keydown', MIM_keydown);
473 MIM_add_event_listener (window, 'keypress', MIM_keypress);
474 MIM_add_event_listener (window, 'mousedown', MIM_select_menu);
475 MIM_load (MIM_current_im);