*** empty log message ***
[m17n/m17n-lib-js.git] / xex.js
1 // -* coding: utf-8; -*
2
3 var Xex = {};
4
5 Xex.Error = {
6   UnknownError: "unknown-error",
7   WrongArgument: "wrong-argument",
8   // Load time errors.
9   InvalidInteger: "invalid-integer",
10   TermTypeInvalid: "term-type-invalid",
11   FunctionConflict: "function-conflict",
12   VariableTypeConflict: "variable-type-conflict",
13   VariableRangeConflict: "variable-range-conflict",
14   VariableWrongRange: "variable-wrong-range",
15   VariableWrongValue: "variable-wrong-value",
16
17   UnknownFunction: "unknown-function",
18   MacroExpansionError: "macro-expansion-error",
19   NoVariableName: "no-variable-anme",
20
21   // Run time errors.
22   ArithmeticError: "arithmetic-error",
23   WrongType: "wrong-type",
24   IndexOutOfRange: "index-out-of-range",
25   ValueOutOfRange: "value-out-of-range",
26   NoLoopToBreak: "no-loop-to-break",
27   UncaughtThrow: "uncaught-throw"
28 };
29
30 Xex.Variable = function (domain, name, val)
31 {
32   this.domain = domain;
33   this.name = name;
34   this.val = val;
35 }
36
37 Xex.Variable.prototype.clone = function () {
38   return new Xex.Variable (this.domain, this.name, this.value);
39 }
40     
41 Xex.Variable.prototype.Equals = function (obj) {
42   return ((obj instanceof Xex.Variable)
43           && obj.name == this.name);
44 }
45
46 Xex.Variable.prototype.SetValue = function (term) {
47   this.val = term;
48   return term;
49 }
50
51 Xex.Function = function (name, with_var, min_args, max_args) {
52   this.name = name;
53   this.with_var = with_var;
54   this.min_args = min_args;
55   this.max_args = max_args;
56 };  
57
58 Xex.Subrountine = function (builtin, name, with_var, min_args, max_args) {
59   this.name = name;
60   this.with_var = with_var;
61   this.min_args = min_args;
62   this.max_args = max_args;
63   this.builtin = builtin;
64 }
65
66 Xex.Subrountine.prototype.Call = function (domain, vari, args)
67 {
68   newargs = new Array ();
69   for (var i = 0; i < args.length; i++)
70     {
71       newargs[i] = args[i].Eval (domain);
72       if (domain.Thrown ())
73         return newargs[i];
74     }
75   return this.builtin (domain, vari, newargs)
76 }
77
78 Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
79 {
80   this.name = name;
81   this.with_var = with_var;
82   this.min_args = min_args;
83   this.max_args = max_args;
84   this.builtin = builtin;
85 }
86
87 Xex.SpecialForm.prototype.Call = function (domain, vari, args)
88 {
89   return this.builtin (domain, vari, args)
90 }
91
92 Xex.Lambda = function (name, min_args, max_args, args, body)
93 {
94   this.name = name;
95   this.with_var = with_var;
96   this.min_args = min_args;
97   this.max_args = max_args;
98   this.args = args;
99   this.body = body;
100 }
101
102 Xex.Lambda.prototype.Call = function (domain, vari, args)
103 {
104   var current = domain.bindings;
105   var result = Xex.Zero;
106   var limit = max_args >= 0 ? args.length : args.length - 1;
107   var i;
108   
109   try {
110     for (i = 0; i < limit; i++)
111       {
112         result = args[i].Eval (domain);
113         if (domain.Thrown ())
114           return result;
115         domain.Bind (this.args[i], result);
116       }
117     if (max_args < 0)
118       {
119         var list = new Array ();
120         for (i = 0; i < args[limit].length; i++)
121           {
122             result = args[limit].Eval (domain);
123             if (domain.Thrown ())
124               return result;
125             list[i] = result;
126           }
127         domain.Bind (this.args[limit], list);
128       }
129     try {
130       domain.Catch (Xex.CatchTag.Return);
131       for (var term in this.body)
132         {
133           result = term.Eval (domain);
134           if (domain.Thrown ())
135             return result;
136         }
137     } finally {
138       domain.Uncatch ();
139     }
140   } finally {
141     domain.UnboundTo (current);
142   }
143   return result;
144 }
145
146 Xex.Macro = function (name, min_args, max_args, args, body)
147 {
148   this.name = name;
149   this.with_var = with_var;
150   this.min_args = min_args;
151   this.max_args = max_args;
152   this.args = args;
153   this.body = body;
154 }
155
156 Xex.Macro.prototype.Call = function (domain, vari, args)
157 {
158   var current = domain.bindings;
159   var result = Xex.Zero;
160   var i;
161
162   try {
163     for (i = 0; i < args.length; i++)
164       domain.Bind (this.args[i], args[i]);
165     try {
166       domain.Catch (Xex.CatchTag.Return);
167       for (var term in body)
168         {
169           result = term.Eval (domain);
170           if (domain.Thrown ())
171             break;
172         }
173     } finally {
174       domain.Uncatch ();
175     }
176   } finally {
177     domain.UnboundTo (current);
178   }
179   return result;
180 }
181
182 Xex.Bindings = function (vari)
183 {
184   this.vari = vari;
185   this.old_value = vari.val;
186 }
187
188 Xex.Bindings.prototype.UnboundTo = function (boundary)
189 {
190   for (var b = this; b != boundary; b = b.next)
191     b.vari.val = b.old_value;
192   return boundary;
193 }
194
195 Xex.Bind = function (bindings, vari, val)
196 {
197   var b = new Xex.Bindings (vari);
198   b.vari.val = val;
199   b.next = bindings;
200   return b;
201 }
202
203 Xex.CatchTag = {
204   Return: 0,
205   Break: 1
206 }
207
208 Xex.Domain = function (name, parent, context)
209 {
210   this.name = name;
211   this.context = context;
212   this.depth = 0;
213
214   if (name != 'basic' && ! parent)
215     parent = Xex.BasicDomain
216   this.parent = parent;
217   this.termtypes = {};
218   this.functions = {};
219   this.variables = {};
220   if (parent)
221     {
222       var elt;
223       for (elt in parent.termtypes)
224         this.termtypes[elt] = parent.termtypes[elt];
225       for (elt in parent.functions)
226         this.functions[elt] = parent.functions[elt];
227       for (elt in parent.variables)
228         this.variables[elt] = parent.variables[elt];
229     }
230
231   this.call_stack = new Array ();
232   this.bindings = null;
233   this.catch_stack = new Array ();
234   this.catch_count = 0;
235   this.caught = false;
236 };
237
238 Xex.Domain.prototype = {
239   CallStackCount: function () { return this.call_stack.length; },
240   CallStackPush: function (term) { this.call_stack.push (term); },
241   CallStackPop: function () { this.call_stack.pop (); },
242   Bind: function (vari, val)
243   {
244     this.bindings = Xex.Bind (this.bindings, vari, val);
245   },
246   UnboundTo: function (boundary)
247   {
248     if (this.bindings)
249       this.bindings = this.bindings.UnboundTo (boundary);
250   },
251   Catch: function (tag) { this.catch_stack.push (tag); this.catch_count++; },
252   Uncatch: function ()
253   {
254     this.catch_stack.pop ();
255     if (this.catch_count > this.catch_stack.length)
256       this.catch_count--;
257   },
258   Thrown: function ()
259   {
260     if (this.catch_count < this.catch_stack.length)
261       {
262         this.caught = (this.catch_count == this.catch_stack.length - 1);
263         return true;
264       }
265     this.caught = false;
266     return false;
267   },
268   ThrowReturn: function ()
269   {
270     for (var i = this.catch_stack.length - 1; i >= 0; i--)
271       {
272         this.catch_count--;
273         if (this.catch_stack[i] == Xex.CatchTag.Return)
274           break;
275       }
276   },
277   ThrowBreak: function ()
278   {
279     if (this.catch_stack[this.catch_stack.length - 1] != Xex.CatchTag.Break)
280       throw new Xex.ErrTerm (Xex.Error.NoLoopToBreak,
281                            "No surrounding loop to break");
282     this.catch_count--;
283   },
284   ThrowSymbol: function (tag)
285   {
286     var i = this.catch_count;
287     for (var j = this.catch_stack.length - 1; j >= 0; j--)
288       {
289         i--;
290         if (Xex.CatchTag.Matches (this.catch_stack[i], tag))
291           {
292             this.catch_count = i;
293             return;
294           }
295       }
296     throw new Xex.ErrTerm (Xex.Error.UncaughtThrow,
297                          "No corresponding catch: " + tag);
298   },
299   DefType: function (obj)
300   {
301     var type = obj.type;
302     if (this.termtypes[type])
303       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
304                            "Already defined: " + type);
305     if (this.functions[type])
306       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
307                            "Already defined as a funciton or a macro: "
308                            + type);
309     this.termtypes[type] = obj.Parser;
310   },
311   DefSubr: function (builtin, name, with_var, min_args, max_args)
312   {
313     this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
314                                                 min_args, max_args);
315   },
316   DefSpecial: function (builtin, name, with_var, min_args, max_args)
317   {
318     this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
319                                                 min_args, max_args);
320   },
321   Defun: function (name, min_args, max_args, args, body)
322   {
323     this.functions[name] =  new Xex.Lambda (name, min_args, max_args,
324                                             args, body);
325   },
326   DefunByFunc: function (func) { this.functions[func.Name] = func; },
327   Defmacro: function (name, min_args, max_args, args, body)
328   {
329     this.functions[name] = new Xex.Macro (name, min_args, max_args,
330                                           args, body);
331   },
332   DefAlias: function (alias, fname)
333   {
334     var func = this.functions[fname];
335
336     if (this.termtypes[alias])
337       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
338                            "Already defined as a term type: " + alias);
339     if (this.functions[alias])
340       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
341                            "Already defined as a function: " + alias);
342     if (! func)
343       throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname);
344     this.functions[alias] = func;
345   },
346   Defvar: function (name)
347   {
348     if (name instanceof Xex.Variable)
349       {
350         name = name.Clone (this);
351         this.variables[name.name] = name;
352         return name;
353       }
354     var vari = this.variables[name];
355     if (vari)
356       {
357         if (vari.Typed)
358           throw new Xex.ErrTerm (Xex.Error.VariableTypeConflict,
359                                "Not a non-typed variable: " + name);
360       }
361     else
362       {
363         vari = new Xex.Variable (this, name, Xex.Zero);
364         this.variables[name] = vari;
365       }
366     return vari;
367   },
368   GetFunc: function (name)
369   {
370     var func = this.functions[name];
371     if (! func)
372       throw new Xex.ErrTerm (Xex.Error.UnknownFunction,
373                              "Unknown function: " + name);
374     return func;
375   },
376   CopyFunc: function (domain, name)
377   {
378     var func = this.functions[name];
379     domain.DefunByFunc (func);
380     return true;
381   },
382   CopyFuncAll: function (domain)
383   {
384     for (var elt in this.functions)
385       domain.DefunByFunc (this.functions[elt]);
386   },
387   GetVarCreate: function (name)
388   {
389     var vari = this.variables[name];
390     if (! vari)
391       vari = this.variables[name] = new Xex.Variable (this, name, Xex.Zero);
392     return vari;
393   },
394   GetVar: function (name) { return this.variables[name]; },
395   SaveValues: function ()
396   {
397     values = {};
398     for (var elt in this.variables)
399       values[elt] = this.variables[elt].val.Clone ();
400     return values;
401   },
402   RestoreValues: function (values)
403   {
404     var name;
405     for (name in values)
406       {
407         var vari = this.variables[name];
408         vari.val = values[name];
409       }
410   }
411 };
412
413 Xex.Term = function (type) { this.type = type; }
414 Xex.Term.prototype = {
415   IsTrue: function () { return true; },
416   Eval: function (domain) { return this.Clone (); },
417   Clone: function (domain) { return this; },
418   Equals: function (obj)
419   {
420     return (this.type == obj.type
421             && this.val
422             && obj.val == this.val);
423   },
424   Matches: function (obj) { return this.Equals (obj); },
425   toString: function ()
426   {
427     if (this.val != undefined)
428       return '<' + this.type + '>' + this.val + '</' + this.type + '>';
429     return '<' + this.type + '/>';
430   }
431 };
432
433 Xex.ParseTerm = function (domain, node)
434 {
435   var name = node.nodeName;
436   var parser = domain.termtypes[name];
437
438   if (parser)
439     return parser (domain, node);
440   if (name == 'defun' || name == 'defmacro')
441     {
442       name = parse_defun_head (domain, node);
443       parse_defun_body (domain, node);
444       return new Xex.StrTerm (name);
445     }
446   if (name == 'defvar')
447     {
448       name = parse_defvar (doamin, node);
449       return new Xex.StrTerm (nanme);
450     }
451
452   return new Xex.Funcall.prototype.Parser (domain, node);
453 }
454
455 Xex.ParseTermList = function (domain, node)
456 {
457   for (var n = node; n; n = n.nextSibling)
458     if (n.nodeType == 1)
459       {
460         if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
461           Xex.parse_defun_head (domain, n);
462       }
463   var terms = false;
464   for (var n = node; n; n = n.nextSibling)
465     if (n.nodeType == 1)
466       {
467         if (! terms)
468           terms = new Array ();
469         if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
470           Xex.parse_defun_body (domain, n);
471         else if (n.nodeName == 'defvar')
472           Xex.parse_defvar (domain, n);
473         else
474           terms.push (Xex.ParseTerm (domain, n));
475       }
476   return terms;
477 }
478
479 Xex.Varref = function (vname)
480 {
481   this.val = vname;
482 };
483
484 (function () {
485   var proto = new Xex.Term ('varref');
486
487   proto.Clone = function () { return new Xex.Varref (this.val); }
488   proto.Eval = function (domain)
489   {
490     if (! this.vari || this.vari.domain != domain)
491       this.vari = domain.GetVarCreate (this.val);
492     return this.vari.val;
493   }
494
495   proto.Parser = function (domain, node)
496   {
497     return new Xex.Varref (node.attributes['vname'].nodeValue);
498   }
499
500   Xex.Varref.prototype = proto;
501 }) ();
502
503 var null_args = new Array ();
504   
505 Xex.Funcall = function (func, vari, args)
506 {
507   this.func = func;
508   this.vari = vari;
509   this.args = args || null_args;
510 };
511
512 (function () {
513   var proto = new Xex.Term ('funcall');
514
515   proto.Parser = function (domain, node)
516   {
517     var fname = node.nodeName;
518     var attr;
519
520     if (fname == 'funcall')
521       fname = node.attributes['fname']
522     var func = domain.GetFunc (fname);
523     var vari;
524     attr = node.attributes['vname'];
525     vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : false;
526     var args = Xex.ParseTermList (domain, node.firstChild);
527     return new Xex.Funcall (func, vari, args);
528   }
529
530   proto.New = function (domain, fname, vname, args)
531   {
532     var func = domain.GetFunc (fname);
533     var vari = vname ? domain.GetVarCreate (vname) : null;
534     var funcall = new Xex.Funcall (func, vari, args);
535     if (func instanceof Xex.Macro)
536       funcall = funcall.Eval (domain);
537     return funcall;
538   }
539
540   proto.Eval = function (domain)
541   {
542     return this.func.Call (domain, this.vari, this.args);
543   }
544
545   proto.Clone = function ()
546   {
547     return new Xex.Funcall (this.func, this.vari, this.args);
548   }
549
550   proto.Equals = function (obj)
551   {
552     return (obj.type == 'funcall'
553             && obj.func == this.func
554             && obj.vari.Equals (this.vari)
555             && obj.args.length == this.func.length);
556   }
557
558   proto.toString = function ()
559   {
560     var arglist = ''
561     var len = this.args.length;
562     if (len == 0)
563       return '<' + this.func.name + '/>';
564     for (var i = 0; i < len; i++)
565       arglist += this.args[i].toString ();
566     return '<' + this.func.name + '>' + arglist + '</' + this.func.name + '>';
567   }
568
569   Xex.Funcall.prototype = proto;
570 }) ();
571
572 Xex.ErrTerm = function (ename, message, stack)
573 {
574   this.ename = ename;
575   this.message = message;
576   this.stack = stack;
577 };
578
579 (function () {
580   var proto = new Xex.Term ('error');
581
582   proto.IsError = true;
583
584   proto.Parser = function (domain, node)
585   {
586     return new Xex.ErrTerm (node.attributes['ename'].nodeValue,
587                             node.innerText, false);
588   }
589
590   proto.CallStack = function () { return stack; }
591
592   proto.SetCallStack = function (value) { statck = value; }
593
594   proto.Clone = function ()
595   {
596     return new Xex.ErrTerm (ename, message, false);
597   }
598
599   proto.Equals = function (obj)
600   {
601     return (obj.IsError
602             && obj.ename == ename && obj.message == message
603             && (obj.stack ? (stack && stack.length == obj.stack.length)
604                 : ! stack));
605   }
606
607   proto.Matches = function (obj)
608   {
609     return (obj.IsError && obj.ename == ename);
610   }
611
612   proto.toString = function ()
613   {
614     return '<error ename="' + this.ename + '">' + this.message + '</error>';
615   }
616
617   Xex.ErrTerm.prototype = proto;
618 }) ();
619
620 Xex.IntTerm = function (num) { this.val = num; };
621 (function () {
622   var proto = new Xex.Term ('integer');
623   proto.IsInt = true;
624   proto.IsTrue = function () { return this.val != 0; }
625   proto.Clone = function () { return new Xex.IntTerm (this.val); }
626   proto.Parser = function (domain, node)
627   {
628     var str = node.firstChild.nodeValue;
629
630     if (str.charAt (0) == '?' && str.length == 2)
631       return new Xex.IntTerm (str.charCodeAt (1));
632     return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
633   }
634   Xex.IntTerm.prototype = proto;
635 }) ();
636
637 Xex.StrTerm = function (str) { this.val = str; };
638 (function () {
639   var proto = new Xex.Term ('string');
640   proto.IsStr = true;
641   proto.IsTrue = function () { return this.val.length > 0; }
642   proto.Clone = function () { return new Xex.StrTerm (this.val); }
643   proto.Parser = function (domain, node)
644   {
645     return new Xex.StrTerm (node.firstChild.nodeValue);
646   }
647   Xex.StrTerm.prototype = proto;
648 }) ();
649
650 Xex.SymTerm = function (str) { this.val = str; };
651 (function () {
652   var proto = new Xex.Term ('symbol');
653   proto.IsSymbol = true;
654   proto.IsTrue = function () { return this.val != 'nil'; }
655   proto.Clone = function () { return new Xex.SymTerm (this.val); }
656   proto.Parser = function (domain, node)
657   {
658     return new Xex.SymTerm (node.firstChild.nodeValue);
659   }
660   Xex.SymTerm.prototype = proto;
661 }) ();
662
663 Xex.LstTerm = function (list) { this.val = list; };
664 (function () {
665   var proto = new Xex.Term ('list');
666   proto.IsList = true;
667   proto.IsTrue = function () { return this.val.length > 0; }
668   proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); }
669
670   proto.Equals = function (obj)
671   {
672     if (obj.type != 'list' || obj.val.length != this.val.length)
673       return false;
674     var i, len = this.val.length;
675     for (i = 0; i < len; i++)
676       if (! this.val[i].Equals (obj.val[i]))
677         return false;
678     return true;
679   }
680
681   proto.Parser = function (domain, node)
682   {
683     var list = Xex.ParseTermList (domain, node.firstChild);
684     return new Xex.LstTerm (list);
685   }
686
687   proto.toString = function ()
688   {
689     var len = this.val.length;
690
691     if (len == 0)
692       return '<list/>';
693     var str = '<list>';
694     for (var i = 0; i < len; i++)
695       str += this.val[i].toString ();
696     return str + '</list>';
697   }
698   Xex.LstTerm.prototype = proto;
699 }) ();
700
701 (function () {
702   var basic = new Xex.Domain ('basic', null, null);
703
704   function Fset (domain, vari, args)
705   {
706     return vari.SetValue (args[0]);
707   }
708
709   function maybe_set_intvar (vari, n)
710   {
711     var term = new Xex.IntTerm (n);
712     if (vari != null)
713       vari.SetValue (term);
714     return term;
715   }
716
717   function Fadd (domain, vari, args)
718   {
719     var n = vari ? vari.val.val : 0;
720     var len = args.length;
721
722     for (var i = 0; i < len; i++)
723       n += args[i].val;
724     return maybe_set_intvar (vari, n);
725   }
726
727   function Fand (domain, vari, args)
728   {
729     var len = args.length;
730     for (var i = 0; i < len; i++)
731     {
732       var result = args[i].Eval (domain);
733       if (domain.Thrown ())
734         return result;
735       if (! result.IsTrue)
736         return Xex.Zero;
737     }
738     return Xex.One;
739   }
740
741   function For (domain, vari, args)
742   {
743     var len = args.length;
744     for (var i = 0; i < len; i++)
745     {
746       var result = args[i].Eval (domain);
747       if (domain.Thrown ())
748         return result;
749       if (result.IsTrue)
750         return Xex.One;
751     }
752     return Xex.Zero;
753   }
754
755   function Fprogn (domain, vari, args)
756   {
757     var result = Xex.One;
758     var len = args.length;
759
760     for (var i = 0; i < len; i++)
761       {
762         result = args[i].Eval (domain);
763         if (domain.Thrown ())
764           return result;
765       }
766     return result;
767   }
768
769   function Fif (domain, vari, args)
770   {
771     var result = args[0].Eval (domain);
772
773     if (domain.Thrown ())
774       return result;
775     if (result.IsTrue)
776       return args[1].Eval (domain);
777     if (args.length == 2)
778       return Zero;
779     return args[2].Eval (domain);
780   }
781
782   function eval_terms (domain, terms, idx)
783   {
784     var result = Xex.Zero;
785     domain.caught = false;
786     for (var i = idx; i < terms.length; i++)
787       {
788         result = terms[i].Eval (domain);
789         if (domain.Thrown ())
790           return result;
791       }
792     return result;
793   }
794
795   function Fcatch (domain, vari, args)
796   {
797     var caught = false;
798     var result;
799
800     if (args[0].IsError)
801       {
802         try {
803           result = eval_terms (domain, args, 1);
804         } catch (e) {
805           if (e instanceof Xex.ErrTerm)
806             {
807               if (! args[0].Matches (e))
808                 throw e;
809               if (vari)
810                 vari.SetValue (e);
811               return Xex.One;
812             }
813         }
814       }
815     else if (args[0].IsSymbol)
816       {
817         try {
818           domain.Catch (args[0].val);
819           result = eval_terms (domain, args, 1);
820           if (domain.caught)
821             {
822               if (vari != null)
823                 vari.SetValue (result);
824               return Xex.One;
825             }
826           return Xex.Zero;
827         } finally {
828           domain.Uncatch ();
829         }
830       }
831     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
832                            "Not a symbol nor an error: " + args[0]);
833   }
834
835   function Fthrow (domain, vari, args)
836   {
837     if (args[0].IsSymbl)
838       {
839         domain.ThrowSymbol (args[0]);
840         return (args[args.length - 1]);
841       }
842     if (args[0].IsError)
843       {
844         throw args[0];
845       }
846     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
847                            "Not a symbol nor an error:" + args[0]);
848   }
849
850   Xex.BasicDomain = basic;
851
852   basic.DefSubr (Fset, "set", true, 1, 1);
853   basic.DefSubr (Fadd, "add", true, 1, -1);
854   basic.DefSubr (Fthrow, "throw", false, 1, 2);
855
856   basic.DefSpecial (Fand, "and", false, 1, -1);
857   basic.DefSpecial (For, "or", false, 1, -1);
858   basic.DefAlias ("=", "set");
859   basic.DefSpecial (Fprogn, "progn", false, 1, -1);
860   basic.DefSpecial (Fif, "if", false, 2, 3);
861   basic.DefSpecial (Fcatch, "catch", true, 2, -1);
862
863   basic.DefType (Xex.Funcall.prototype);
864   basic.DefType (Xex.Varref.prototype);
865   basic.DefType (Xex.ErrTerm.prototype);
866   basic.DefType (Xex.IntTerm.prototype);
867   basic.DefType (Xex.StrTerm.prototype);
868   basic.DefType (Xex.SymTerm.prototype);
869   basic.DefType (Xex.LstTerm.prototype);
870
871 }) ();
872
873 Xex.Zero = new Xex.IntTerm (0);
874 Xex.One = new Xex.IntTerm (1);
875
876 Xex.Load = function (server, file)
877 {
878   var obj = new XMLHttpRequest ();
879   var url = server ? server + '/' + file : file;
880   obj.open ('GET', url, false);
881   obj.overrideMimeType ('text/xml');
882   obj.send ('');
883   return obj.responseXML.firstChild;
884 }
885
886 var MIM = {
887   // URL of the input method server.
888   server: "http://www.m17n.org/common/mim-js",
889   // Boolean flag to tell if MIM is active or not.
890   enabled: true,
891   // Boolean flag to tell if MIM is running in debug mode or not.
892   debug: false,
893   // List of registered input methods.
894   im_list: {},
895   // Global input method data
896   im_global: null,
897   // Currently selected input method.
898   current: false,
899
900   // enum
901   LoadStatus: { NotLoaded:0, Loading:1, Loaded:2, Error:-1 },
902   ChangedStatus: {
903     None:       0x00,
904     StateTitle: 0x01,
905     PreeditText:0x02,
906     CursorPos:  0x04,
907     CandidateList:0x08,
908     CandidateIndex:0x10,
909     CandidateShow:0x20,
910     Preedit:    0x06,           // PreeditText | CursorPos
911     Candidate:  0x38 // CandidateList | CandidateIndex | CandidateShow
912   },
913   KeyModifier: {
914     SL: 0x00400000,
915     SR: 0x00800000,
916     S:  0x00C00000,
917     CL: 0x01000000,
918     CR: 0x02000000,
919     C:  0x03000000,
920     AL: 0x04000000,
921     AR: 0x08000000,
922     A:  0x0C000000,
923     ML: 0x04000000,
924     MR: 0x08000000,
925     M:  0x0C000000,
926     G:  0x10000000,
927     s:  0x20000000,
928     H:  0x40000000,
929     High:       0x70000000,
930     All:        0x7FC00000
931   },
932   Error: {
933     ParseError: "parse-error"
934   }
935 };
936   
937 (function () {
938   var keysyms = new Array ();
939   keysyms["bs"] = "backspace";
940   keysyms["lf"] = "linefeed";
941   keysyms["cr"] = keysyms["enter"] = "return";
942   keysyms["esc"] = "escape";
943   keysyms["spc"] = "space";
944   keysyms["del"] = "delete";
945
946   function decode_keysym (str) {
947     var parts = str.split ("-");
948     var len = parts.length, i;
949     var has_modifier = len > 1;
950
951     for (i = 0; i < len - 1; i++)
952       if (! MIM.KeyModifier.hasOwnProperty (parts[i]))
953         return false;
954     var key = parts[len - 1];
955     if (key.length > 1)
956       {
957         key = keysyms[key.toLowerCase ()];
958         if (key)
959           {
960             if (len > 1)
961               {
962                 str = parts[0];
963                 for (i = 1; i < len - 1; i++)
964                   str += '-' + parts[i];
965                 str += '-' + key;
966               }
967             else
968               str = key;
969           }
970       }
971     if (has_modifier)
972       {
973         parts = new Array ();
974         parts.push (str);
975         return parts;
976       }
977     return str;
978   }
979
980   MIM.Key = function (val)
981   {
982     this.key;
983     this.has_modifier = false;
984     if (typeof val == 'string' || val instanceof String)
985       {
986         this.key = decode_keysym (val);
987         if (! this.key)
988           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
989         if (this.key instanceof Array)
990           {
991             this.key = this.key[0];
992             this.has_modifier = true;
993           }
994       }
995     else if (typeof val == 'number' || val instanceof Number)
996       this.key = String.fromCharCode (val);
997     else
998       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
999   }
1000
1001   MIM.Key.prototype.toString = function () { return this.key; };
1002 }) ();
1003
1004 (function () {
1005   MIM.KeySeq = function (seq)
1006   {
1007     this.val = new Array ();
1008     this.has_modifier = false;
1009
1010     if (seq)
1011       {
1012         if (seq.IsList)
1013           {
1014             var len = seq.val.length;
1015             for (var i = 0; i < len; i++)
1016               {
1017                 var v = seq.val[i];
1018                 if (v.type != 'string' && v.type != 'integer'
1019                     && v.type != 'symbol')
1020                   throw new Xex.ErrTerm (MIM.Error.ParseError,
1021                                          "Invalid key: " + v);
1022                 var key = new MIM.Key (v.val);
1023                 this.val.push (key);
1024                 if (key.has_modifier)
1025                   this.has_modifier = true;
1026               }
1027           }
1028         else if (seq.IsStr)
1029           {
1030             var len = seq.val.length;
1031             for (var i = 0; i < len; i++)
1032               this.val.push (new MIM.Key (seq.val.charCodeAt (i)));
1033           }
1034         else
1035           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + seq);
1036       }
1037   }
1038
1039   var proto = new Xex.Term ('keyseq');
1040   proto.Clone = function () { return this; }
1041   proto.Parser = function (domain, node)
1042   {
1043     var seq = new Array ();
1044     for (node = node.firstChild; node; node = node.nextSibling)
1045       if (node.nodeType == 1)
1046         {
1047           var term = Xex.ParseTerm (domain, node);
1048           return new MIM.KeySeq (term);
1049         }
1050     throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid keyseq");
1051   }
1052   proto.toString = function ()
1053   {
1054     var len = this.val.length;
1055     if (len == 0)
1056       return '<keyseq/>';
1057     var first = true;
1058     var str = '<keyseq>';
1059     for (var i = 0; i < len; i++)
1060       {
1061         if (first)
1062           first = false;
1063         else if (this.has_modifier)
1064           str += ' ';
1065         str += this.val[i].toString ();
1066       }
1067     return str + '</keyseq>';
1068   }
1069
1070   MIM.KeySeq.prototype = proto;
1071 }) ();
1072
1073 (function () {
1074   MIM.Marker = function () { }
1075   MIM.Marker.prototype = new Xex.Term ('marker');
1076   MIM.Marker.prototype.CharAt = function (ic)
1077   {
1078     var p = this.Position (ic);
1079     if (p < 0)
1080       return ic.GetSurroundingChar (p);
1081     else if (pos >= ic.preedit.length)
1082       return ic.GetSurroundingChar (p - ic.preedit.length);
1083     return ic.preedit.charCodeAt (p);
1084   }
1085
1086   MIM.NamedMarker = function (name) { this.val = name; }
1087   MIM.NamedMarker.prototype = new MIM.Marker ();
1088   MIM.NamedMarker.prototype.Position = function (ic)
1089   {
1090     var p = ic.marker_positions[this.val];
1091     return (p == undefined ? 0 : p);
1092   }
1093   MIM.NamedMarker.prototype.Mark = function (ic)
1094   {
1095     ic.marker_positions[this.val] = ic.cursor_pos;
1096   }
1097
1098   MIM.PredefinedMarker = function (name) { this.val = name; }
1099   MIM.PredefinedMarker.prototype = new MIM.Marker ();
1100   MIM.PredefinedMarker.prototype.Position = function (ic)
1101   {
1102     if (typeof this.pos == 'number')
1103       return this.pos;
1104     return this.pos (ic);
1105   }
1106
1107   var predefined = { }
1108
1109   function def_predefined (name, position)
1110   {
1111     predefined[name] = new MIM.PredefinedMarker (name);
1112     predefined[name].pos = position;
1113   }
1114
1115   def_predefined ('@<', 0);
1116   def_predefined ('@>', function (ic) { return ic.preedit.length; });
1117   def_predefined ('@-', function (ic) { return ic.cursor_pos - 1; });
1118   def_predefined ('@+', function (ic) { return ic.cursor_pos + 1; });
1119   def_predefined ('@[', function (ic) {
1120     if (ic.cursor_pos > 0)
1121       {
1122         var pos = ic.cursor_pos;
1123         return ic.preedit.FindProp ('candidates', pos - 1).from;
1124       }
1125     return 0;
1126   });
1127   def_predefined ('@]', function (ic) {
1128     if (ic.cursor_pos < ic.preedit.length - 1)
1129       {
1130         var pos = ic.cursor_pos;
1131         return ic.preedit.FindProp ('candidates', pos).to;
1132       }
1133     return ic.preedit.length;
1134   });
1135   for (var i = 0; i < 10; i++)
1136     def_predefined ("@" + i, i);
1137   predefined['@first'] = predefined['@<'];
1138   predefined['@last'] = predefined['@>'];
1139   predefined['@previous'] = predefined['@-'];
1140   predefined['@next'] = predefined['@+'];
1141   predefined['@previous-candidate-change'] = predefined['@['];
1142   predefined['@next-candidate-change'] = predefined['@]'];
1143
1144   MIM.SurroundMarker = function (name)
1145   {
1146     this.val = name;
1147     this.distance = parseInt (name.slice (2));
1148     if (isNaN (this.distance))
1149       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name);
1150   }
1151   MIM.SurroundMarker.prototype = new MIM.Marker ();
1152   MIM.SurroundMarker.prototype.Position = function (ic)
1153   {
1154     return ic.cursor_pos + this.distance;
1155   }
1156
1157   MIM.Marker.prototype.Parser = function (domain, node)
1158   {
1159     var name = node.firstChild.nodeValue;
1160     if (name.charAt (0) == '@')
1161       {
1162         var n = predefined[name];
1163         if (n)
1164           return n;
1165         if (name.charAt (1) == '-')
1166           return new MIM.SurroundMarker (name);
1167         throw new Xex.ErrTerm (MIM.Error.ParseError,
1168                                "Invalid marker: " + name);
1169       }
1170     return new MIM.NamedMarker (name);
1171   }
1172 }) ();
1173
1174 MIM.Selector = function (name)
1175 {
1176   this.val = name;
1177 }
1178 MIM.Selector.prototype = new Xex.Term ('selector');
1179 (function () {
1180   var selectors = {};
1181   selectors["@<"] = selectors["@first"] = new MIM.Selector ('@<');
1182   selectors["@="] = selectors["@current"] = new MIM.Selector ('@=');
1183   selectors["@>"] = selectors["@last"] = new MIM.Selector ('@>');
1184   selectors["@-"] = selectors["@previous"] = new MIM.Selector ('@-');
1185   selectors["@+"] = selectors["@next"] = new MIM.Selector ('@+');
1186   selectors["@["] = selectors["@previous-candidate-change"]
1187     = new MIM.Selector ('@[');
1188   selectors["@]"] = selectors["@next-candidate-change"]
1189     = new MIM.Selector ('@]');
1190
1191   MIM.Selector.prototype.Parser = function (domain, node)
1192   {
1193     var name = node.firstChild.nodeValue;
1194     var s = selectors[name];
1195     if (! s)
1196       throw new Xex.ErrTerm (MIM.Error.ParseError,
1197                              "Invalid selector: " + name);
1198     return s;
1199   }
1200 }) ();
1201
1202 MIM.Rule = function (keyseq, actions)
1203 {
1204   this.keyseq = keyseq;
1205   this.actions = actions;
1206 }
1207 MIM.Rule.prototype = new Xex.Term ('rule');
1208 MIM.Rule.prototype.Parser = function (domain, node)
1209 {
1210   var n;
1211   for (n = node.firstChild; n && n.nodeType != 1; n = n.nextSibling);
1212   if (! n)
1213     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1214   var keyseq = Xex.ParseTerm (domain, n);
1215   if (keyseq.type != 'keyseq')
1216     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1217   var actions = Xex.ParseTermList (domain, n.nextSibling);
1218   return new MIM.Rule (keyseq, actions);
1219 }
1220 MIM.Rule.prototype.toString = function ()
1221 {
1222   return '<rule/>';
1223 }
1224
1225 MIM.Map = function (name)
1226 {
1227   this.name = name;
1228   this.rules = new Array ();
1229 };
1230 (function () {
1231   var proto = new Xex.Term ('map');
1232
1233   proto.Parser = function (domain, node)
1234   {
1235     var name = node.attributes['mname'].nodeValue;
1236     if (! name)
1237       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1238     var map = new MIM.Map (name);
1239     for (var n = node.firstChild; n; n = n.nextSibling)
1240       if (n.nodeType == 1)
1241         map.rules.push (Xex.ParseTerm (domain, n));
1242     return map;
1243   }
1244
1245   proto.toString = function ()
1246   {
1247     var str = '<map mname="' + this.name + '">';
1248     var len = this.rules.length;
1249     for (i = 0; i < len; i++)
1250       str += this.rules[i];
1251     return str + '</map>';
1252   }
1253
1254   MIM.Map.prototype = proto;
1255 }) ();
1256
1257 Xex.CatchTag._mimtag = new Xex.SymTerm ('@mimtag');
1258
1259 MIM.Action = function (domain, terms)
1260 {
1261   var args = new Array ();
1262   args.push (Xex.CatchTag_.mimtag);
1263   for (var i = 0; i < terms.length; i++)
1264     args.push (terms[i]);
1265   this.action = Xex.Funcall.prototype.New (domain, 'catch', null, args);
1266 }
1267
1268 MIM.Action.prototype.Run = function (domain)
1269 {
1270   var result = this.action.Eval (domain);
1271   if (result.type == 'error')
1272     {
1273       domain.context.Error = result.toString ();
1274       return false;
1275     }
1276   return (result != Xex.CatchTag._mimtag);
1277 }
1278
1279 MIM.Keymap = function ()
1280 {
1281   this.name = 'TOP';
1282   this.submaps = null;
1283   this.actions = null;
1284 };
1285 (function () {
1286   var proto = {};
1287
1288   function add_rule (keymap, rule)
1289   {
1290     var keyseq = rule.keyseq;
1291     var len = keyseq.val.length;
1292     var name = '';
1293
1294     for (var i = 0; i < len; i++)
1295       {
1296         var key = keyseq.val[i];
1297         var sub = false;
1298
1299         name += key.key;
1300         if (! keymap.submaps)
1301           keymap.submaps = {};
1302         else
1303           sub = keymap.submaps[key.key];
1304         if (! sub)
1305           keymap.submaps[key.key] = sub = new MIM.Keymap ();
1306         keymap = sub;
1307         keymap.name = name;
1308       }
1309     keymap.actions = rule.actions;
1310   }
1311
1312   proto.Add = function (map)
1313   {
1314     var rules = map.rules;
1315     var len = rules.length;
1316
1317     for (var i = 0; i < len; i++)
1318       add_rule (this, rules[i]);
1319   }
1320   proto.Lookup = function (keys, index)
1321   {
1322     var sub;
1323
1324     if (index < keys.val.length && this.submaps
1325         && (sub = this.submaps[keys.val[index].key]))
1326       {
1327         index++;
1328         return sub.Lookup (keys, index);
1329       }
1330     return { map: this, index: index };
1331   }
1332
1333   MIM.Keymap.prototype = proto;
1334 }) ();
1335
1336 MIM.State = function (name)
1337 {
1338   this.name = name;
1339   this.keymap = new MIM.Keymap ();
1340 };
1341 (function () {
1342   var proto = new Xex.Term ('state');
1343
1344   proto.Parser = function (domain, node)
1345   {
1346     var map_list = domain.map_list;
1347     var name = node.attributes['sname'].nodeValue;
1348     if (! name)
1349       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1350     var state = new MIM.State (name);
1351     for (node = node.firstChild; node; node = node.nextSibling)
1352       {
1353         if (node.nodeType != 1)
1354           continue;
1355         if (node.nodeName == 'branch')
1356           {
1357             state.keymap.Add (map_list[node.attributes['mname'].nodeValue]);
1358             state.keymap.actions = Xex.ParseTermList (domain, node.firstChild);
1359           }
1360         else if (node.nodeName == 'state-hook')
1361           state.enter_actions = Xex.ParseTermList (domain, node.firstChild);
1362         else if (node.nodeName == 'catch-all-branch')
1363           state.fallback_actions = Xex.ParseTermList (domain, node.firstChild);
1364         else if (node.nodeName == 'title')
1365           state.title = node.firstChild.nodeValue;
1366       }
1367     return state;
1368   }
1369
1370   proto.toString = function ()
1371   {
1372     return '<state sname="' + this.name + '">' + this.keymap + '</state>';
1373   }
1374
1375   MIM.State.prototype = proto;
1376 }) ();
1377
1378 MIM.im_domain = new Xex.Domain ('input-method', null, null);
1379 MIM.im_domain.DefType (MIM.KeySeq.prototype);
1380 MIM.im_domain.DefType (MIM.Marker.prototype);
1381 MIM.im_domain.DefType (MIM.Selector.prototype);
1382 MIM.im_domain.DefType (MIM.Rule.prototype);
1383 MIM.im_domain.DefType (MIM.Map.prototype);
1384 MIM.im_domain.DefType (MIM.State.prototype);
1385
1386 (function () {
1387   var im_domain = MIM.im_domain;
1388
1389   function Finsert (domain, vari, args)
1390   {
1391     var text;
1392     if (args[0].type == 'integer')
1393       text = String.fromCharCode (args[0].val);
1394     else
1395       text = args[0].val;
1396     domain.context.insert (text, null);
1397   }
1398
1399   function Finsert_candidates (domain, vari, args)
1400   {
1401     var ic = domain.context;
1402     var candidates = new Candidates (args, column);
1403     ic.insert (candidates.Current (), candidates);
1404     return args[0];
1405   }
1406
1407   im_domain.DefSubr (Finsert, "insert", false, 1, 1);
1408 }) ();
1409
1410 (function () {
1411   var parsers = { };
1412   parsers['description'] = function (node)
1413   {
1414     this.description = node.firstChild.nodeValue;
1415   }
1416   parses['variable-list'] = function (node)
1417   {
1418     for (node = node.firstChild; node; node = node.nextSibling)
1419       {
1420         if (node.nodeType != 1)
1421           continue;
1422         var name = node.attributes['vname'].nodeValue;
1423         var vari = get_global_bar (name);
1424         if (vari != null)
1425           domain.Defvar (name);
1426         Xex.Parse (domain, node);
1427       }
1428   }
1429   parsers['title'] = function (node)
1430   {
1431     this.title = node.firstChild.nodeValue;
1432   }
1433   parsers['map-list'] = function (node)
1434   {
1435     for (node = node.firstChild; node; node = node.nextSibling)
1436       {
1437         if (node.nodeType != 1 || node.nodeName != 'map')
1438           continue;
1439         var map = Xex.ParseTerm (this.domain, node);
1440         this.map_list[map.name] = map;
1441       }
1442   }
1443   parsers['state-list'] = function (node)
1444   {
1445     this.domain.map_list = this.map_list;
1446     for (node = node.firstChild; node; node = node.nextSibling)
1447       {
1448         if (node.nodeType != 1 || node.nodeName != 'state')
1449           continue;
1450         var state = Xex.ParseTerm (this.domain, node);
1451         if (! state.title)
1452           state.title = this.title;
1453         if (! this.initial_state)
1454           this.initial_state = state;
1455         this.state_list[state.name] = state;
1456       }
1457     delete this.domain.map_list;
1458   }
1459
1460   MIM.IM = function (lang, name, extra_id, file)
1461   {
1462     this.lang = lang;
1463     this.name = name;
1464     this.extra_id = extra_id;
1465     this.file = file;
1466     this.load_status = MIM.LoadStatus.NotLoaded;
1467     this.domain = new Xex.Domain (this.lang + '-' + this.name,
1468                                   MIM.im_domain, null);
1469   }
1470
1471   var proto = {
1472     Load: function ()
1473     {
1474       var node = Xex.Load (null, this.file);
1475       if (! node)
1476         {
1477           this.load_status = MIM.LoadStatus.Error;
1478           return false;
1479         }
1480       this.map_list = {};
1481       this.initial_state = null;
1482       this.state_list = {};
1483       for (node = node.firstChild; node; node = node.nextSibling)
1484         {
1485           if (node.nodeType != 1)
1486             continue;
1487           var name = node.nodeName;
1488           var parser = parsers[name];
1489           if (parser)
1490             parser.call (this, node);
1491         }
1492       this.load_status = MIM.LoadStatus.Loaded;
1493       return true;
1494     }
1495   }
1496
1497   MIM.IM.prototype = proto;
1498
1499   MIM.IC = function (im)
1500   {
1501     if (im.load_status == MIM.LoadStatus.NotLoaded)
1502       im.Load ();
1503     if (im.load_status != MIM.LoadStatus.Loaded)
1504       alert ('im:' + im.name + ' error:' + im.load_status);
1505     this.im = im;
1506     this.domain = new Xex.Domain ('context', im.domain, this);
1507     this.active = true;
1508     this.reset ();
1509     this.spot = 0;
1510   }
1511
1512   MIM.CandidateTable = function ()
1513   {
1514     this.table = new Array ();
1515   }
1516
1517   MIM.CandidateTable.prototype.get = function (from)
1518   {
1519     for (var i = 0; i < this.table.length; i++)
1520       {
1521         var elt = this.table[i];
1522         if (elt.from <= from && elt.to > from)
1523           return elt.val;
1524       }
1525   }
1526
1527   MIM.CandidateTable.prototype.put = function (from, to, candidates)
1528   {
1529     for (var i = 0; i < this.table.length; i++)
1530       {
1531         var elt = this.table[i];
1532         if (elt.from >= from && elt.from < to
1533             || elt.to >= from && elt.to < to)
1534           {
1535             elt.from = from;
1536             elt.to = to;
1537             elt.val = candidates;
1538             return;
1539           }
1540       }
1541     this.table.push ({ from: from, to: to, val: candidates });
1542   }
1543
1544   MIM.CandidateTable.prototype.adjust = function (from, to, inserted)
1545   {
1546     var diff = inserted - (to - from);
1547     for (var i = 0; i < this.table.length; i++)
1548       {
1549         var elt = this.table[i];
1550         if (elt.from >= to)
1551           {
1552             elt.from += diff;
1553             elt.to += diff;
1554           }
1555       }
1556   }
1557
1558   MIM.CandidateTable.prototype.clear = function ()
1559   {
1560     this.table.length = 0;
1561   }
1562
1563   function Block (index, term)
1564   {
1565     this.Index = index;
1566     if (term.IsStr)
1567       this.Data = term.val;
1568     else if (term.IsList)
1569       {
1570         this.Data = new Array ();
1571         for (var i = 0; i < term.val.length; i++)
1572           this.Data.push (term.val[i].val);
1573       }
1574   }
1575
1576   Block.prototype.Count = function () { return this.Data.length; }
1577   Block.prototype.get = function (i)
1578   {
1579     return (this.Data instanceof Array ? this.Data[i] : this.Data.charAt (i));
1580   }
1581
1582   function fill_group (start)
1583   {
1584     var nitems = this.group.length;
1585     var r = this.row;
1586     var b = this.blocks[r];
1587
1588     if (start < b.Index)
1589       while (start < b.Index)
1590         b = this.blocks[--r];
1591     else
1592       while (start >= b.Index + b.Count ())
1593         b = this.blocks[++r];
1594     this.row = r;
1595
1596     var count = b.Count ();
1597     start -= b.Index;
1598     for (var i = 0; i < nitems; i++, start++)
1599       {
1600         if (start >= count)
1601           {
1602             r++;
1603             if (r == this.blocks.Length)
1604               return i;
1605             b = this.blocks[r];
1606             count = b.Count ();
1607             start = 0;
1608           }
1609         this.group[i] = b.get (start);
1610       }
1611     return nitems;
1612   }
1613
1614   function Candidates (candidates, column)
1615   {
1616     this.column = column;
1617     this.row = 0;
1618     this.index = 0;
1619     this.total = 0;
1620     this.blocks = new Array ();
1621
1622     for (var i = 0; i < candidates.length; i++)
1623       {
1624         var block = new Block (this.total, candidates[i]);
1625         this.blocks.push (block);
1626         this.total += block.Count ();
1627       }
1628   }
1629
1630   Candidates.prototype.Column = function ()
1631   {
1632     return (this.column > 0 ? this.index % this.column
1633             : this.index - this.blocks[this.row].Index);
1634   }
1635
1636   Candidates.prototype.GroupLength = function ()
1637   {
1638     if (this.column > 0)
1639       {
1640         var start = this.index - (this.index % this.column);
1641         return (start + this.column <= this.total ? this.column
1642                 : this.total - start);
1643       }
1644     return this.blocks[this.row].Count;
1645   }
1646
1647   Candidates.prototype.Current = function ()
1648   {
1649     var b = this.blocks[this.row];
1650     return b.get (this.index - b.Index);
1651   }
1652
1653   Candidates.prototype.PrevGroup ()
1654   {
1655     var col = this.Column ();
1656     var nitems;
1657     if (this.column > 0)
1658       {
1659         this.index -= this.column;
1660         if (this.index >= 0)
1661           nitems = this.column;
1662         else
1663           {
1664             var lastcol = (this.total - 1) % this.column;
1665             this.index = (col < lastcol ? this.total - lastcol + col
1666                           : this.total - 1);
1667             this.row = this.blocks.length - 1;
1668             nitems = lastcol + 1;
1669           }
1670         while (this.blocks[this.row].Index > this.index)
1671           this.row--;
1672       }
1673     else
1674       {
1675         this.row = this.row > 0 ? this.row - 1 : this.blocks.length - 1;
1676         nitems = this.blocks[this.row].Count ();
1677         this.index = (this.blocks[this.row].Index
1678                       + (col < nitems ? col : nitems - 1));
1679       }
1680     return nitems;
1681   }
1682
1683   Candidates.prototype.NextGroup = function ()
1684   {
1685     var col = this.Column ();
1686     var nitems;
1687     if (this.column > 0)
1688       {
1689         this.index += this.column - col;
1690         if (this.index < this.total)
1691           {
1692             if (this.index + col >= this.total)
1693               {
1694                 nitems = this.total - this.index;
1695                 this.index = this.total - 1;
1696               }
1697             else
1698               {
1699                 nitems = this.column;
1700                 this.index += col;
1701               }
1702           }
1703         else
1704           {
1705             this.index = col;
1706             this.row = 0;
1707           }
1708         while (this.blocks[this.row].Index > this.index)
1709           this.row++;
1710       }
1711     else
1712       {
1713         this.row = this.row < this.blocks.length - 1 ? this.row + 1 : 0;
1714         nitems = this.blocks[this.row].Count ();
1715         this.index = (this.blocks[this.row].Index
1716                       + (col < nitems ? col : nitems - 1));
1717       }
1718     return nitems;
1719   }
1720
1721   Candidates.prototype.Prev = function ()
1722   {
1723     if (this.index == 0)
1724       {
1725         this.index = this.total - 1;
1726         this.row = this.blocks.length - 1;
1727       }
1728     else
1729       {
1730         this.index--;
1731         if (this.blocks[this.row].Index > this.index)
1732           this.row--;
1733       }
1734     }
1735
1736   Candidates.prototype.Next = function ()
1737   {
1738     this.index++;
1739     if (this.index == this.total)
1740       {
1741         this.index = 0;
1742         this.row = 0;
1743       }
1744     else
1745       {
1746         var b = this.blocks[this.row];
1747         if (this.index == b.Index + b.Count ())
1748           this.row++;
1749       }
1750   }
1751
1752   Candidates.prototype.First = function ()
1753   {
1754     this.index -= this.Column ();
1755     while (this.blocks[this.row].Index > this.index)
1756       this.row--;
1757   }
1758
1759   Candidates.prototype.Last = function ()
1760   {
1761     var b = this.blocks[this.row];
1762     if (this.column > 0)
1763       {
1764         if (this.index + 1 < this.total)
1765           {
1766             this.index += this.column - this.Column () + 1;
1767             while (b.Index + b.Count () <= this.index)
1768               b = this.blocks[++this.row];
1769       }
1770     else
1771       this.index = b.Index + b.Count () - 1;
1772   }
1773
1774   Candidates.prototype.Select = funciton (selector)
1775   {
1776     if (selector instanceof MIM.Selector)
1777       {
1778         switch (selector.val)
1779           {
1780           case '@<': this.First (); break;
1781           case '@>': this.Last (); break;
1782           case '@-': this.Prev (); break;
1783           case '@+': this.Next (); break;
1784           case '@[': this.PrevGroup (); break;
1785           case '@]': this.NextGroup (); break;
1786           default: break;
1787           }
1788         return this.Current ();
1789       }
1790     var col, start, end
1791     if (this.column > 0)
1792       {
1793         col = this.index % this.column;
1794         start = this.index - col;
1795         end = start + this.column;
1796       }
1797     else
1798       {
1799         start = this.blocks[this.row].Index;
1800         col = this.index - start;
1801         end = start + this.blocks[this.row].Count;
1802       }
1803     if (end > this.total)
1804       end = this.total;
1805     this.index += selector - col;
1806     if (this.index >= end)
1807       this.index = end - 1;
1808     if (this.column > 0)
1809       {
1810         if (selector > col)
1811           while (this.blocks[this.row].Index + this.blocks[this.row].Count
1812                  < this.index)
1813             this.row++;
1814         else
1815           while (this.blocks[this.row].Index > this.index)
1816             this.row--;
1817       }
1818     return this.Current ();
1819   }
1820
1821   function detach_candidates (ic)
1822   {
1823     ic.candidate_table.clear ();
1824     ic.candidates = null;
1825     ic.changed |= (MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos
1826                    | ChangedStatus.CandidateList
1827                    | ChangedStatus.CandidateIndex
1828                    | ChangedStatus.CandidateShow);
1829   }
1830
1831   function set_cursor (prefix, pos)
1832   {
1833     this.cursor_pos = pos;
1834     if (pos > 0)
1835       this.candidates = this.candidate_table.get (pos - 1);
1836     else
1837       this.candidates = null;
1838   }
1839
1840   function save_state ()
1841   {
1842     this.state_var_values = this.domain.SaveValues ();
1843     this.state_preedit = this.preedit;
1844     this.state_key_head = this.key_head;
1845     this.state_pos = this.cursor_pos;
1846   }
1847
1848   function restore_state ()
1849   {
1850     this.domain.RestoreValues (this.state_var_values);
1851     this.preedit = this.state_preedit;
1852     set_cursor.call (this, "restore", this.state_pos);
1853   }
1854
1855   function handle_key ()
1856   {
1857     var out = this.keymap.Lookup (this.keys, this.key_head);
1858     var sub = out.map;
1859     var branch_actions = this.state.keymap.actions;
1860
1861     MIM.log ('handling ' + this.keys.val[this.key_head]
1862              + ' in ' + this.state.name + ':' + this.keymap.name);
1863     this.key_head = out.index;
1864     if (sub != this.keymap)
1865       {
1866
1867         restore_state.call (this);
1868         this.keymap = sub;
1869         MIM.log ('submap found');
1870         if (this.keymap.actions)
1871           {
1872             MIM.log ('taking map actions:');
1873             if (! this.take_actions (this.keymap.actions))
1874               return false;
1875           }
1876         else if (this.keymap.submaps)
1877           {
1878             MIM.log ('no map actions');
1879             for (var i = this.state_key_head; i < this.key_head; i++)
1880               {
1881                 MIM.log ('inserting key:' + this.keys.val[i].key);
1882                 this.insert (this.keys.val[i].key, null);
1883               }
1884           }
1885         if (! this.keymap.submaps)
1886           {
1887             MIM.log ('terminal:');
1888             if (this.keymap.branch_actions != null)
1889               {
1890                 MIM.log ('branch actions:');
1891                 if (! this.take_actions (branch_actions))
1892                   return false;
1893               }
1894             if (this.keymap != this.state.keymap)
1895               this.shift (this.state);
1896           }
1897       }
1898     else
1899       {
1900         MIM.log ('no submap');
1901         var current_state = this.state;
1902         var map = this.keymap;
1903
1904         if (branch_actions)
1905           {
1906             MIM.log ('branch actions');
1907             if (! this.take_actions (this.keymap.branch_actions))
1908               return false;
1909           }
1910
1911         if (map == this.keymap)
1912           {
1913             MIM.log ('no state change');
1914             if (map == this.initial_state.keymap
1915                 && this.key_head < this.keys.val.length)
1916               {
1917                 MIM.log ('unhandled');
1918                 return false;
1919               }
1920             if (this.keymap != current_state.keymap)
1921               this.shift (current_state);
1922             else if (this.keymap.actions == null)
1923               this.shift (this.initial_state);
1924           }
1925       }
1926     return true;
1927   }
1928
1929   proto = {
1930     reset: function ()
1931     {
1932       this.produced = null;
1933       this.preedit = '';
1934       this.preedit_saved = '';
1935       this.cursor_pos = 0;
1936       this.marker_positions = {};
1937       this.candidates = null;
1938       this.candidate_show = false;
1939       this.state = null;
1940       this.prev_state = null;
1941       this.initial_state = this.im.initial_state;
1942       this.title = this.initial_state.title;
1943       this.state_preedit = '';
1944       this.state_key_head = 0;
1945       this.state_var_values = {};
1946       this.state_pos = 0;
1947       this.keymap = null;
1948       this.keys = new MIM.KeySeq ();
1949       this.key_head = 0;
1950       this.key_unhandled = false;
1951       this.unhandled_key = null;
1952       this.changed = MIM.ChangedStatus.None;
1953       this.error_message = '';
1954       this.title = this.initial_state.title;
1955       this.produced = '';
1956       this.preedit = '';
1957       this.preedit_saved = '';
1958       this.marker_positions = {};
1959       this.candidate_table = new MIM.CandidateTable ();
1960       this.candidates = null;
1961       this.candidate_show = false;
1962       this.shift (this.initial_state);
1963     },
1964
1965     catch_args: new Array (Xex.CatchTag._mimtag, null),
1966
1967     take_actions: function (actions)
1968     {
1969       var func_progn = this.domain.GetFunc ('progn');
1970       var func_catch = this.domain.GetFunc ('catch');
1971       this.catch_args[1] = new Xex.Funcall (func_progn, null, actions);
1972       var term = new Xex.Funcall (func_catch, null, this.catch_args);
1973       term = term.Eval (this.domain);
1974       return (! term.IsSymbol || term.val != '@mimtag');
1975     },
1976
1977     GetSurroundingChar: function (pos)
1978     {
1979       if (pos < 0 ? this.caret_pos < - pos : this.target.value.length < pos)
1980         return 0;
1981       return this.target.value.charCodeAt (this.caret_pos + pos);
1982     },
1983     
1984     adjust_markers: function (from, to, inserted)
1985     {
1986       var diff = inserted - (to - from);
1987
1988       for (var m in this.marker_positions)
1989         if (this.marker_positions[m] > from)
1990           this.marker_positions[m] = (this.marker_positions[m] >= to
1991                                       ? pos + diff : from);
1992       if (this.cursor_pos >= to)
1993         set_cursor.call (this, 'adjust', this.cursor_pos + diff);
1994       else if (this.cursor_pos > from)
1995         set_cursor.call (this, 'adjust', from)
1996     },
1997
1998     preedit_replace: function (from, to, text, candidates)
1999     {
2000       this.preedit = (this.preedit.substring (0, from)
2001                       + text + this.preedit.substring (to));
2002       this.adjust_markers (from, to, text.length);
2003       this.candidate_table.adjust (from, to, text.length);
2004       if (candidates)
2005         this.candidate_table.put (from, from + text.length, candidates)
2006     },
2007
2008     insert: function (text, candidates)
2009     {
2010       this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates);
2011       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
2012     },
2013
2014     del: function (pos)
2015     {
2016       if (pos < 0)
2017         {
2018           this.DelSurroundText (pos);
2019           pos = 0;
2020         }
2021       else if (pos > this.preedit.length)
2022         {
2023           this.DelSurroundText (pos - this.preedit.length);
2024           pos = this.preedit.length;
2025         }
2026       if  (pos < this.cursor_pos)
2027         this.preedit = (this.predit.substring (0, pos)
2028                         + this.preedit.substring (this.cursor_pos));
2029       else
2030         this.preedit = (this.preedit.substring (0, this.cursor_pos)
2031                         + this.predit.substring (pos));
2032     },
2033
2034     show: function ()
2035     {
2036       this.candidate_show = true;
2037       this.changed |= MIM.ChangedStatus.CandidateShow;
2038     },
2039
2040     hide: function ()
2041     {
2042       this.candidate_show = false;
2043       this.changed |= MIM.ChangedStatus.CandidateShow;
2044     },
2045
2046     move: function (pos)
2047     {
2048       if (pos < 0)
2049         pos = 0;
2050       else if (pos > this.preedit.length)
2051         pos = this.preedit.length;
2052       if (pos != this.cursor_pos)
2053         {
2054           set_cursor.call (this, 'move', pos);
2055           this.changed |= MIM.ChangedStatus.Preedit;
2056         }
2057     },
2058
2059     pushback: function (n)
2060     {
2061       if (n instanceof MIM.KeySeq)
2062         {
2063           if (this.key_head > 0)
2064             this.key_head--;
2065           if (this.key_head < this.keys.val.length)
2066             this.keys.val.splice (this.key_head,
2067                                   this.keys.val.length - this.key_head);
2068           for (var i = 0; i < n.val.length; i++)
2069             this.keys.val.push (n.val[i]);
2070           return;
2071         }
2072       if (n > 0)
2073         {
2074           this.key_head -= n;
2075           if (this.key_head < 0)
2076             this.key_head = 0;
2077         }
2078       else if (n == 0)
2079         this.key_head = 0;
2080       else
2081       {
2082         this.key_head = - n;
2083         if (this.key_head > this.keys.val.length)
2084           this.key_head = this.keys.val.length;
2085       }
2086     },
2087
2088     pop: function ()
2089     {
2090       if (this.key_head < this.keys.val.length)
2091         this.keys.val.splice (this.key_head, 1);
2092     },
2093
2094     commit: function ()
2095     {
2096       if (this.preedit.length > 0)
2097       {
2098         this.candidate_table.clear ();
2099         this.produced += this.preedit;
2100         this.preedit_replace.call (this, 0, this.preedit.length, '', null);
2101       }
2102     },
2103
2104     shift: function (state)
2105     {
2106       if (state == null)
2107         {
2108           MIM.log ("shifting back to previous");
2109           if (this.prev_state == null)
2110             return;
2111           state = this.prev_state;
2112         }
2113       else
2114         MIM.log ("shifting to " + state.name);
2115
2116       if (state == this.initial_state)
2117         {
2118           if (this.state)
2119             {
2120               this.commit ();
2121               this.keys.val.splice (0, this.key_head);
2122               this.key_head = 0;
2123               this.prev_state = null;
2124             }
2125         }
2126       else
2127         {
2128           if (state != this.state)
2129             this.prev_state = this.state;
2130         }
2131       if (state != this.state && state.enter_actions)
2132         take_actions.call (state.enter_actions);
2133       if (! this.state || this.state.title != state.title)
2134         this.changed |= MIM.ChangedStatus.StateTitle;
2135       this.state = state;
2136       this.keymap = state.keymap;
2137       this.state_key_head = this.key_head;
2138       save_state.call (this);
2139     },
2140
2141     Filter: function (key)
2142     {
2143       if (! this.active)
2144         {
2145           this.key_unhandled = true;
2146           this.unhandled_key = key;
2147           return false;
2148         }
2149       if (key.key == '_reload')
2150         return true;
2151       this.changed = MIM.ChangedStatus.None;
2152       this.produced = '';
2153       this.key_unhandled = false;
2154       this.keys.val.push (key);
2155       var count = 0;
2156       while (this.key_head < this.keys.val.length)
2157         {
2158           if (! handle_key.call (this))
2159             {
2160               if (this.key_head < this.keys.val.length)
2161                 {
2162                   this.unhandled_key = this.keys.val[this.key_head];
2163                   this.keys.val.splice (this.key_head, this.key_head + 1);
2164                 }
2165               this.key_unhandled = true;
2166               break;
2167             }
2168           if (++count == 10)
2169             {
2170               this.reset ();
2171               this.key_unhandled = true;
2172               break;
2173             }
2174         }
2175       if (this.key_unhandled)
2176         {
2177           this.keys.val.length = 0;
2178           this.key_head = this.state_key_head = this.commit_key_head = 0;
2179         }
2180       return (! this.key_unhandled
2181               && this.produced.length == 0
2182               && this.preedit.length == 0);
2183     }
2184   }
2185
2186   MIM.IC.prototype = proto;
2187
2188   var node = Xex.Load (null, "imlist.xml");
2189   for (node = node.firstChild; node; node = node.nextSibling)
2190     if (node.nodeName == 'input-method')
2191       {
2192         var lang, name, extra_id, file;
2193
2194         for (var n = node.firstChild; n; n = n.nextSibling)
2195           {
2196             if (n.nodeName == 'language')
2197               lang = n.firstChild.nodeValue;
2198             else if (n.nodeName == 'name')
2199               name = n.firstChild.nodeValue;
2200             else if (n.nodeName == 'extra-id')
2201               extra_id = n.firstChild.nodeValue;
2202             else if (n.nodeName == 'filename')
2203               file = n.firstChild.nodeValue;
2204           }
2205         if (! MIM.im_list[lang])
2206           MIM.im_list[lang] = {};
2207         MIM.im_list[lang][name] = new MIM.IM (lang, name, extra_id, file);
2208       }
2209   node = undefined;
2210 }) ();
2211
2212 (function () {
2213   var keys = new Array ();
2214   keys[0x09] = 'tab';
2215   keys[0x08] = 'backspace';
2216   keys[0x0D] = 'return';
2217   keys[0x1B] = 'escape';
2218   keys[0x20] = 'space';
2219   keys[0x21] = 'pageup';
2220   keys[0x22] = 'pagedown';
2221   keys[0x23] = 'end';
2222   keys[0x24] = 'home';
2223   keys[0x25] = 'left';
2224   keys[0x26] = 'up';
2225   keys[0x27] = 'right';
2226   keys[0x28] = 'down';
2227   keys[0x2D] = 'insert';
2228   keys[0x2E] = 'delete';
2229   for (var i = 1; i <= 12; i++)
2230     keys[111 + i] = "f" + i;
2231   keys[0x90] = "numlock";
2232   keys[0xF0] = "capslock";
2233
2234   MIM.decode_key_event = function (event)
2235   {
2236     var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
2237                : event.charCode ? event.charCode
2238                : false);
2239     if (! key)
2240       return false;
2241     if (event.type == 'keydown')
2242       {
2243         key = keys[key];
2244         if (! key)
2245           return false;
2246         if (event.shiftKey) key = "S-" + key ;
2247       }
2248     else
2249       key = String.fromCharCode (key);
2250     if (event.altKey) key = "A-" + key ;
2251     if (event.ctrlKey) key = "C-" + key ;
2252     return new MIM.Key (key);
2253   }
2254 }) ();
2255
2256 MIM.add_event_listener
2257   = (window.addEventListener
2258      ? function (target, type, listener) {
2259        target.addEventListener (type, listener, false);
2260      }
2261      : window.attachEvent
2262      ? function (target, type, listener) {
2263        target.attachEvent ('on' + type,
2264                            function() {
2265                              listener.call (target, window.event);
2266                            });
2267      }
2268      : function (target, type, listener) {
2269        target['on' + type]
2270          = function (e) { listener.call (target, e || window.event); };
2271      });
2272
2273 MIM.log = function (msg)
2274 {
2275   var node = document.getElementById ('log');
2276   node.value = msg + "\n" + node.value;
2277 }
2278
2279 MIM.debug_print = function (event, ic)
2280 {
2281   if (! MIM.debug)
2282     return;
2283   if (! MIM.debug_nodes)
2284     {
2285       MIM.debug_nodes = new Array ();
2286       MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
2287       MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
2288       MIM.debug_nodes['status0'] = document.getElementById ('status0');
2289       MIM.debug_nodes['status1'] = document.getElementById ('status1');
2290       MIM.debug_nodes['keymap0'] = document.getElementById ('keymap0');
2291       MIM.debug_nodes['keymap1'] = document.getElementById ('keymap1');
2292       MIM.debug_nodes['preedit0'] = document.getElementById ('preedit0');
2293       MIM.debug_nodes['preedit1'] = document.getElementById ('preedit1');
2294     }
2295   var target = event.target;
2296   var code = event.keyCode;
2297   var ch = event.type == 'keydown' ? 0 : event.charCode;
2298   var key = MIM.decode_key_event (event);
2299   var index;
2300
2301   MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
2302   index = (event.type == 'keydown' ? '0' : '1');
2303   if (ic)
2304     MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status;
2305   else
2306     MIM.debug_nodes['status' + index].innerHTML = 'no IM';
2307   MIM.debug_nodes['keymap' + index].innerHTML = ic.keymap.name;
2308   MIM.debug_nodes['preedit' + index].innerHTML = ic.preedit;
2309 };
2310
2311 MIM.get_range = function (target, range)
2312 {
2313   if (target.selectionStart != null) // for Mozilla
2314     {
2315       range[0] = target.selectionStart;
2316       range[1] = target.selectionEnd;
2317     }
2318   else                          // for IE
2319     {
2320       var r = document.selection.createRange ();
2321       var rr = r.duplicate ();
2322
2323       rr.moveToElementText (target);
2324       rr.setEndPoint ('EndToEnd', range);
2325       range[0] = rr.text.length - r.text.length;
2326       range[1] = rr.text.length;
2327     }
2328 }
2329
2330 MIM.set_caret = function (target, ic)
2331 {
2332   if (target.setSelectionRange) // Mozilla
2333     {
2334       var scrollTop = target.scrollTop;
2335       target.setSelectionRange (ic.spot, ic.spot + ic.preedit.length);
2336       target.scrollTop = scrollTop;
2337     }
2338   else                          // IE
2339     {
2340       var range = target.createTextRange ();
2341       range.moveStart ('character', ic.spot);
2342       range.moveEnd ('character', ic.spot + ic.preedit.length);
2343       range.select ();
2344     }
2345 };
2346
2347 (function () {
2348   var range = new Array ();
2349
2350   MIM.check_range = function (target, ic)
2351   {
2352     MIM.get_range (target, range);
2353     if (range[0] != ic.spot || range[1] - range[0] != ic.preedit.length
2354         || target.value.substring (range[0], range[1]) != ic.preedit)
2355       {
2356         MIM.log ('reset:' + ic.spot + '-' + (ic.spot + ic.preedit.length)
2357                  + '/' + range[0] + '-' + range[1]);
2358         ic.reset ();
2359       }
2360     target.value = (target.value.substring (0, range[0])
2361                     + target.value.substring (range[1]));
2362     ic.spot = range[0];
2363   }
2364 }) ();
2365
2366 MIM.update = function (target, ic)
2367 {
2368   var text = target.value;
2369   target.value = (text.substring (0, ic.spot)
2370                   + ic.produced
2371                   + ic.preedit
2372                   + text.substring (ic.spot));
2373   ic.spot += ic.produced.length;
2374   MIM.set_caret (target, ic);
2375 };
2376
2377 MIM.reset_ic = function (event)
2378 {
2379   if (event.target.mim_ic)
2380     {
2381       var ic = event.target.mim_ic;
2382       var pos = ic.spot + ic.preedit.length;
2383       ic.reset ();
2384       if (pos > ic.spot)
2385         event.target.setSelectionRange (pos, pos);
2386     }
2387 };
2388
2389 MIM.keydown = function (event)
2390 {
2391   var target = event.target;
2392   if (! (target.type == "text" || target.type == "textarea"))
2393     return;
2394
2395   var ic = target.mim_ic;
2396   if (! ic || ic.im != MIM.current)
2397     {
2398       MIM.log ('creating IC');
2399       ic = new MIM.IC (MIM.current);
2400       target.mim_ic = ic;
2401       MIM.add_event_listener (target, 'blur', MIM.reset_ic);
2402     }
2403   if (ic.im.load_status != MIM.LoadStatus.Loaded)
2404     return;
2405   MIM.check_range (target, ic);
2406   MIM.debug_print (event, ic);
2407   ic.key = MIM.decode_key_event (event);
2408 };
2409
2410 MIM.keypress = function (event)
2411 {
2412   if (! (event.target.type == "text" || event.target.type == "textarea"))
2413     return;
2414
2415   var ic = event.target.mim_ic;
2416   var i;
2417
2418   try {
2419     if (ic.im.load_status != MIM.LoadStatus.Loaded)
2420       return;
2421     if (! ic.key)
2422       ic.key = MIM.decode_key_event (event);
2423     if (! ic.key)
2424       {
2425         ic.reset ();
2426         return;
2427       }
2428     
2429     MIM.log ("filtering " + ic.key);
2430     var result = ic.Filter (ic.key);
2431     MIM.update (event.target, ic);
2432     if (! ic.key_unhandled)
2433       event.preventDefault ();
2434   } finally {
2435     MIM.debug_print (event, ic);
2436   }
2437   return;
2438 };
2439
2440 MIM.select_im = function (event)
2441 {
2442   var target = event.target.parentNode;
2443   while (target.tagName != "SELECT")
2444     target = target.parentNode;
2445   var idx = 0;
2446   var im = false;
2447   for (var lang in MIM.im_list)
2448     for (var name in MIM.im_list[lang])
2449       if (idx++ == target.selectedIndex)
2450         {
2451           im = MIM.im_list[lang][name];
2452           break;
2453         }
2454   document.getElementsByTagName ('body')[0].removeChild (target);
2455   target.target.focus ();
2456   if (im && im != MIM.current)
2457     {
2458       MIM.current = im;
2459       MIM.log ('select IM: ' + im.name);
2460     }
2461 };
2462
2463 MIM.destroy_menu = function (event)
2464 {
2465   if (event.target.tagName == "SELECT")
2466     document.getElementsByTagName ('body')[0].removeChild (event.target);
2467 };
2468
2469 MIM.select_menu = function (event)
2470 {
2471   var target = event.target;
2472
2473   if (! ((target.type == "text" || target.type == "textarea")
2474          && event.which == 1 && event.ctrlKey))
2475     return;
2476
2477   var sel = document.createElement ('select');
2478   sel.onclick = MIM.select_im;
2479   sel.onmouseout = MIM.destroy_menu;
2480   sel.style.position='absolute';
2481   sel.style.left = (event.clientX - 10) + "px";
2482   sel.style.top = (event.clientY - 10) + "px";
2483   sel.target = target;
2484   var idx = 0;
2485   for (var lang in MIM.im_list)
2486     for (var name in MIM.im_list[lang])
2487       {
2488         var option = document.createElement ('option');
2489         var imname = lang + "-" + name;
2490         option.appendChild (document.createTextNode (imname));
2491         option.value = imname;
2492         sel.appendChild (option);
2493         if (MIM.im_list[lang][name] == MIM.current)
2494           sel.selectedIndex = idx;
2495         idx++;
2496       }
2497   sel.size = idx;
2498   document.getElementsByTagName ('body')[0].appendChild (sel);
2499 };
2500
2501 MIM.test = function ()
2502 {
2503   var im = MIM.im_list['t']['latn-post'];
2504   var ic = new MIM.IC (im);
2505
2506   ic.Filter (new MIM.Key ('a'));
2507   ic.Filter (new MIM.Key ("'"));
2508
2509   if (true)
2510     document.getElementById ('text').value = ic.produced + ic.preedit;
2511   else {
2512     try {
2513       document.getElementById ('text').value
2514         = Xex.ParseTerm (domain, body).Eval (domain).toString ();
2515     } catch (e) {
2516       if (e instanceof Xex.ErrTerm)
2517         alert (e);
2518       throw e;
2519     }
2520   }
2521 }
2522
2523
2524 MIM.init = function ()
2525 {
2526   MIM.add_event_listener (window, 'keydown', MIM.keydown);
2527   MIM.add_event_listener (window, 'keypress', MIM.keypress);
2528   MIM.add_event_listener (window, 'mousedown', MIM.select_menu);
2529   if (window.location == 'http://localhost/mim/index.html')
2530     MIM.server = 'http://localhost/mim';
2531   MIM.current = MIM.im_list['vi']['telex'];
2532 };
2533
2534 MIM.init_debug = function ()
2535 {
2536   MIM.debug = true;
2537   MIM.init ();
2538 };