*** empty log message ***
[m17n/m17n-lib-js.git] / xex.js
1 // -* coding: utf-8; -*
2
3 var Xex = {
4   LogNode: null,
5   LogSuspended: '',
6   Log: function (arg, indent, suspend)
7   {
8     if (! Xex.LogNode)
9       return;
10     if (! arg)
11       Xex.LogNode.value = '';
12     else if (suspend)
13       Xex.LogSuspended += arg;
14     else
15       {
16         var str = Xex.LogSuspended;
17         Xex.LogSuspended = '';
18         if (indent != undefined)
19           for (var i = 0; i <= indent; i++)
20             str += '  ';
21         Xex.LogNode.value = str + arg + "\n" + Xex.LogNode.value;
22       }
23   }
24 };
25
26 Xex.Error = {
27   UnknownError: "unknown-error",
28   WrongArgument: "wrong-argument",
29   // Load time errors.
30   InvalidInteger: "invalid-integer",
31   TermTypeInvalid: "term-type-invalid",
32   FunctionConflict: "function-conflict",
33   VariableTypeConflict: "variable-type-conflict",
34   VariableRangeConflict: "variable-range-conflict",
35   VariableWrongRange: "variable-wrong-range",
36   VariableWrongValue: "variable-wrong-value",
37
38   UnknownFunction: "unknown-function",
39   MacroExpansionError: "macro-expansion-error",
40   NoVariableName: "no-variable-name",
41   NoFunctionName: "no-funcion-name",
42
43   // Run time errors.
44   ArithmeticError: "arithmetic-error",
45   WrongType: "wrong-type",
46   IndexOutOfRange: "index-out-of-range",
47   ValueOutOfRange: "value-out-of-range",
48   NoLoopToBreak: "no-loop-to-break",
49   UncaughtThrow: "uncaught-throw"
50 };
51
52 Xex.Variable = function (domain, name, desc, val, range)
53 {
54   this.domain = domain;
55   this.name = name;
56   this.desc = desc;
57   this.val = val;
58   this.range = range;
59 }
60
61 Xex.Variable.prototype.clone = function ()
62 {
63   return new Xex.Variable (this.domain, this.name, this.desc,
64                            this.val, this.range);
65 }
66     
67 Xex.Variable.prototype.Equals = function (obj)
68 {
69   return ((obj instanceof Xex.Variable)
70           && obj.name == this.name);
71 }
72
73 Xex.Variable.prototype.SetValue = function (term)
74 {
75   this.val = term;
76   return term;
77 }
78
79 Xex.Function = function (name, with_var, min_args, max_args)
80 {
81   this.name = name;
82   this.with_var = with_var;
83   this.min_args = min_args;
84   this.max_args = max_args;
85 };  
86
87 Xex.Subrountine = function (builtin, name, with_var, min_args, max_args)
88 {
89   this.name = name;
90   this.with_var = with_var;
91   this.min_args = min_args;
92   this.max_args = max_args;
93   this.builtin = builtin;
94 }
95
96 Xex.Subrountine.prototype.Call = function (domain, vari, args)
97 {
98   var newargs = new Array ();
99   for (var i = 0; i < args.length; i++)
100     {
101       newargs[i] = args[i].Eval (domain);
102       if (domain.Thrown ())
103         return newargs[i];
104     }
105   return this.builtin (domain, vari, newargs)
106 }
107
108 Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
109 {
110   this.name = name;
111   this.with_var = with_var;
112   this.min_args = min_args;
113   this.max_args = max_args;
114   this.builtin = builtin;
115 }
116
117 Xex.SpecialForm.prototype.Call = function (domain, vari, args)
118 {
119   return this.builtin (domain, vari, args)
120 }
121
122 Xex.Lambda = function (name, min_args, max_args, args, body)
123 {
124   this.name = name;
125   this.min_args = min_args;
126   this.max_args = max_args;
127   this.args = args;
128   this.body = body;
129 }
130
131 Xex.Lambda.prototype.Call = function (domain, vari, args)
132 {
133   var current = domain.bindings;
134   var result = Xex.Zero;
135   var limit = max_args >= 0 ? args.length : args.length - 1;
136   var i;
137   
138   try {
139     for (i = 0; i < limit; i++)
140       {
141         result = args[i].Eval (domain);
142         if (domain.Thrown ())
143           return result;
144         domain.Bind (this.args[i], result);
145       }
146     if (max_args < 0)
147       {
148         var list = new Array ();
149         for (i = 0; i < args[limit].length; i++)
150           {
151             result = args[limit].Eval (domain);
152             if (domain.Thrown ())
153               return result;
154             list[i] = result;
155           }
156         domain.Bind (this.args[limit], list);
157       }
158     try {
159       domain.Catch (Xex.CatchTag.Return);
160       for (var term in this.body)
161         {
162           result = term.Eval (domain);
163           if (domain.Thrown ())
164             return result;
165         }
166     } finally {
167       domain.Uncatch ();
168     }
169   } finally {
170     domain.UnboundTo (current);
171   }
172   return result;
173 }
174
175 Xex.Macro = function (name, min_args, max_args, args, body)
176 {
177   this.name = name;
178   this.min_args = min_args;
179   this.max_args = max_args;
180   this.args = args;
181   this.body = body;
182 }
183
184 Xex.Macro.prototype.Call = function (domain, vari, args)
185 {
186   var current = domain.bindings;
187   var result = Xex.Zero;
188   var i;
189
190   try {
191     for (i = 0; i < args.length; i++)
192       domain.Bind (this.args[i], args[i]);
193     try {
194       domain.Catch (Xex.CatchTag.Return);
195       for (var i in this.body)
196         {
197           result = this.body[i].Eval (domain);
198           if (domain.Thrown ())
199             break;
200         }
201     } finally {
202       domain.Uncatch ();
203     }
204   } finally {
205     domain.UnboundTo (current);
206   }
207   return result;
208 }
209
210 Xex.Bindings = function (vari)
211 {
212   this.vari = vari;
213   this.old_value = vari.val;
214 }
215
216 Xex.Bindings.prototype.UnboundTo = function (boundary)
217 {
218   for (var b = this; b != boundary; b = b.next)
219     b.vari.val = b.old_value;
220   return boundary;
221 }
222
223 Xex.Bind = function (bindings, vari, val)
224 {
225   var b = new Xex.Bindings (vari);
226   b.vari.val = val;
227   b.next = bindings;
228   return b;
229 }
230
231 Xex.CatchTag = {
232   Return: 0,
233   Break: 1
234 }
235
236 Xex.Domain = function (name, parent, context)
237 {
238   this.name = name;
239   this.context = context;
240   this.depth = 0;
241
242   if (name != 'basic' && ! parent)
243     parent = Xex.BasicDomain
244   this.parent = parent;
245   this.termtypes = {};
246   this.functions = {};
247   this.variables = {};
248   if (parent)
249     {
250       var elt;
251       for (elt in parent.termtypes)
252         this.termtypes[elt] = parent.termtypes[elt];
253       for (elt in parent.functions)
254         this.functions[elt] = parent.functions[elt];
255       for (elt in parent.variables)
256         {
257           var vari = parent.variables[elt];
258           this.variables[elt] = new Xex.Variable (this, vari.name, vari.desc,
259                                                   vari.val, vari.range);
260         }
261     }
262
263   this.call_stack = new Array ();
264   this.bindings = null;
265   this.catch_stack = new Array ();
266   this.catch_count = 0;
267   this.caught = false;
268 };
269
270 Xex.Domain.prototype = {
271   CallStackCount: function () { return this.call_stack.length; },
272   CallStackPush: function (term) { this.call_stack.push (term); },
273   CallStackPop: function () { this.call_stack.pop (); },
274   Bind: function (vari, val)
275   {
276     this.bindings = Xex.Bind (this.bindings, vari, val);
277   },
278   UnboundTo: function (boundary)
279   {
280     if (this.bindings)
281       this.bindings = this.bindings.UnboundTo (boundary);
282   },
283   Catch: function (tag) { this.catch_stack.push (tag); this.catch_count++; },
284   Uncatch: function ()
285   {
286     this.catch_stack.pop ();
287     if (this.catch_count > this.catch_stack.length)
288       this.catch_count--;
289   },
290   Thrown: function ()
291   {
292     if (this.catch_count < this.catch_stack.length)
293       {
294         this.caught = (this.catch_count == this.catch_stack.length - 1);
295         return true;
296       }
297     this.caught = false;
298     return false;
299   },
300   ThrowReturn: function ()
301   {
302     for (var i = this.catch_stack.length - 1; i >= 0; i--)
303       {
304         this.catch_count--;
305         if (this.catch_stack[i] == Xex.CatchTag.Return)
306           break;
307       }
308   },
309   ThrowBreak: function ()
310   {
311     if (this.catch_stack[this.catch_stack.length - 1] != Xex.CatchTag.Break)
312       throw new Xex.ErrTerm (Xex.Error.NoLoopToBreak,
313                            "No surrounding loop to break");
314     this.catch_count--;
315   },
316   ThrowSymbol: function (tag)
317   {
318     var i = this.catch_count;
319     for (var j = this.catch_stack.length - 1; j >= 0; j--)
320       {
321         i--;
322         if (Xex.CatchTag.Matches (this.catch_stack[i], tag))
323           {
324             this.catch_count = i;
325             return;
326           }
327       }
328     throw new Xex.ErrTerm (Xex.Error.UncaughtThrow,
329                          "No corresponding catch: " + tag);
330   },
331   DefType: function (obj)
332   {
333     var type = obj.type;
334     if (this.termtypes[type])
335       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
336                            "Already defined: " + type);
337     if (this.functions[type])
338       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
339                            "Already defined as a funciton or a macro: "
340                            + type);
341     this.termtypes[type] = obj.Parser;
342   },
343   DefSubr: function (builtin, name, with_var, min_args, max_args)
344   {
345     this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
346                                                 min_args, max_args);
347   },
348   DefSpecial: function (builtin, name, with_var, min_args, max_args)
349   {
350     this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
351                                                 min_args, max_args);
352   },
353   Defun: function (name, min_args, max_args, args, body)
354   {
355     this.functions[name] =  new Xex.Lambda (name, min_args, max_args,
356                                             args, body);
357   },
358   DefunByFunc: function (func) { this.functions[func.name] = func; },
359   Defmacro: function (name, min_args, max_args, args, body)
360   {
361     this.functions[name] = new Xex.Macro (name, min_args, max_args,
362                                           args, body);
363   },
364   DefAlias: function (alias, fname)
365   {
366     var func = this.functions[fname];
367
368     if (! func)
369       throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname);
370     if (this.termtypes[alias])
371       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
372                            "Already defined as a term type: " + alias);
373     if (this.functions[alias])
374       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
375                            "Already defined as a function: " + alias);
376     this.functions[alias] = func;
377   },
378   Defvar: function (name, desc, val, range)
379   {
380     var vari = new Xex.Variable (this, name, desc, val, range);
381     this.variables[name] = vari;
382     return vari;
383   },
384   GetFunc: function (name)
385   {
386     var func = this.functions[name];
387     if (! func)
388       throw new Xex.ErrTerm (Xex.Error.UnknownFunction,
389                              "Unknown function: " + name);
390     return func;
391   },
392   CopyFunc: function (domain, name)
393   {
394     var func = this.functions[name];
395     domain.DefunByFunc (func);
396     return true;
397   },
398   CopyFuncAll: function (domain)
399   {
400     for (var elt in this.functions)
401       domain.DefunByFunc (this.functions[elt]);
402   },
403   GetVarCreate: function (name)
404   {
405     var vari = this.variables[name];
406     if (! vari)
407       vari = this.variables[name] = new Xex.Variable (this, name, null,
408                                                       Xex.Zero, null);
409     return vari;
410   },
411   GetVar: function (name) { return this.variables[name]; },
412   SaveValues: function ()
413   {
414     values = {};
415     for (var elt in this.variables)
416       values[elt] = this.variables[elt].val.Clone ();
417     return values;
418   },
419   RestoreValues: function (values)
420   {
421     var name;
422     for (name in values)
423       {
424         var vari = this.variables[name];
425         vari.val = values[name];
426       }
427   }
428 };
429
430 Xex.Term = function (type) { this.type = type; }
431 Xex.Term.prototype = {
432   IsTrue: function () { return true; },
433   Eval: function (domain) { return this.Clone (); },
434   Clone: function (domain) { return this; },
435   Equals: function (obj)
436   {
437     return (this.type == obj.type
438             && this.val
439             && obj.val == this.val);
440   },
441   Matches: function (obj) { return this.Equals (obj); },
442   toString: function ()
443   {
444     if (this.val != undefined)
445       return '<' + this.type + '>' + this.val + '</' + this.type + '>';
446     return '<' + this.type + '/>';
447   },
448   Intval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
449                                                "Not an integer"); },
450   Strval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
451                                                "Not a string"); }
452 };
453
454 Node.prototype.firstElement = function ()
455 {
456   for (var n = this.firstChild; n; n = n.nextSibling)
457     if (n.nodeType == 1)
458       return n;
459   return null;
460 }
461
462 Node.prototype.nextElement = function ()
463 {
464   for (var n = this.nextSibling; n; n = n.nextSibling)
465     if (n.nodeType == 1)
466       return n;
467   return null;
468 };
469
470 (function () {
471   function parse_defvar (domain, node)
472   {
473     var name = node.attributes['vname'].nodeValue;
474     if (! name)
475       throw new Xex.ErrTerm (Xex.Error.NoVariableName, node, '');
476     var vari = domain.variables[name];
477     var desc, val, range;
478     if (vari)
479       {
480         desc = vari.description;
481         val = vari.val;
482         range = vari.range;
483       }
484     node = node.firstElement ();
485     if (node && node.nodeName == 'description')
486       {
487         desc = node.firstChild.nodeValue;
488         node = node.nextElement ();
489       }
490     if (node)
491       {
492         val = Xex.Term.Parse (domain, node);
493         node = node.nextElement ();
494         if (node && node.nodeName == 'possible-values')
495           for (node = node.firstElement (); node; node = node.nextElement ())
496             {
497               var pval;
498               if (node.nodeName == 'range')
499                 {
500                   if (! val.IsInt)
501                     throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
502                                            'Range not allowed for ' + name);
503                   pval = new Array ();
504                   for (var n = node.firstElement (); n; n = n.nextElement ())
505                     {
506                       var v = Xex.Term.Parse (domain, n);
507                       if (! v.IsInt)
508                         throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
509                                                'Invalid range value: ' + val);
510                       pval.push (v);
511                     }
512                   }
513               else
514                 {
515                   pval = Xex.Term.Parse (domain, node);
516                   if (val.type != pval.type)
517                     throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
518                                            'Invalid possible value: ' + pval);
519                 }
520               if (! range)
521                 range = new Array ();
522               range.push (pval);
523           }
524       }
525     if (! val)
526       val = Xex.Zero;
527     domain.Defvar (name, desc, val, range);
528     return name;
529   }
530
531   function parse_defun_head (domain, node)
532   {
533     var name = node.attributes['fname'].nodeValue;
534     if (! name)
535       throw new Xex.ErrTerm (Xex.Error.NoFunctionName, node, '');
536     var args = new Array ();
537     var nfixed = 0, noptional = 0, nrest = 0;
538
539     node = node.firstElement ();
540     if (node && node.nodeName == 'args')
541       {
542         var n;
543         for (n = n.firstElement (); n; n = n.nextElement ())
544           {
545             if (n.nodeName == 'fixed')
546               nfixed++;
547             else if (n.nodeName == 'optional')
548               noptional++;
549             else if (n.nodeName == 'rest')
550               nrest++;
551             else
552               throw new Xex.ErrTerm (Xex.Error.WrongType, n, n.nodeName);
553           }
554         if (nrest > 1)
555           throw new Xex.ErrTerm (Xex.Error.WrongType, n, 'Too many <rest>');
556         for (n = node.firstElement (); n; n = n.nextElement ())
557           args.push (domain.DefVar (n.attributes['vname'].nodeValue));
558       }
559     args.min_args = nfixed;
560     args.max_args = nrest == 0 ? nfixed + noptional : -1;
561
562     if (node.nodeName == 'defun')
563       domain.Defun (name, args, null);
564     else
565       domain.Defmacro (name, args, null);
566     return name;
567   }
568
569   function parse_defun_body (domain, node)
570   {
571     var name = node.attributes['fname'].nodeValue;
572     var func = domain.GetFunc (name);
573     var body;
574     for (node = node.firstElement (); node; node = node.nextElement ())
575       if (node.nodeName != 'description' && node.nodeName != 'args')
576         break;
577     body = Xex.Term.Parse (domain, node, null);
578     func.body = body;
579   }
580
581   Xex.Term.Parse = function (domain, node, stop)
582   {
583     if (arguments.length == 2)
584       {
585         var name = node.nodeName;
586         var parser = domain.termtypes[name];
587
588         if (parser)
589           return parser (domain, node);
590         if (name == 'defun' || name == 'defmacro')
591           {
592             name = parse_defun_head (domain, node);
593             parse_defun_body (domain, node);
594             return new Xex.StrTerm (name);
595           }
596         if (name == 'defvar')
597           {
598             name = parse_defvar (domain, node);
599             return new Xex.StrTerm (name);
600           }
601         return new Xex.Funcall.prototype.Parser (domain, node);
602       }
603     for (var n = node; n && n != stop; n = n.nextElement ())
604       if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
605         parse_defun_head (domain, n);
606     var terms = null;
607     for (var n = node; n && n != stop; n = n.nextElement ())
608       {
609         if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
610           parse_defun_body (domain, n);
611         else if (n.nodeName == 'defvar')
612           parse_defvar (domain, n);
613         else
614           {
615             if (! terms)
616               terms = new Array ();
617             terms.push (Xex.Term.Parse (domain, n));
618           }
619       }
620     return terms;
621   }
622 }) ();
623
624 Xex.Varref = function (vname)
625 {
626   this.val = vname;
627 };
628
629 (function () {
630   var proto = new Xex.Term ('varref');
631
632   proto.Clone = function () { return new Xex.Varref (this.val); }
633   proto.Eval = function (domain)
634   {
635     var vari = domain.GetVarCreate (this.val);
636     Xex.Log (this.ToString () + '=>' + vari.val, domain.depth);
637     return vari.val;
638   }
639
640   proto.Parser = function (domain, node)
641   {
642     return new Xex.Varref (node.attributes['vname'].nodeValue);
643   }
644
645   proto.ToString = function ()
646   {
647     return '<varref vname="' + this.val + '"/>';
648   }
649
650   Xex.Varref.prototype = proto;
651 }) ();
652
653 var null_args = new Array ();
654   
655 Xex.Funcall = function (func, vname, args)
656 {
657   this.func = func;
658   this.vname = vname;
659   this.args = args || null_args;
660 };
661
662 (function () {
663   var proto = new Xex.Term ('funcall');
664
665   proto.Parser = function (domain, node)
666   {
667     var fname = node.nodeName;
668     var attr;
669
670     if (fname == 'funcall')
671       fname = node.attributes['fname'].nodeValue;
672     var func = domain.GetFunc (fname);
673     var vname;
674     attr = node.attributes['vname'];
675     vname = attr != undefined ? attr.nodeValue : null;
676     var args = Xex.Term.Parse (domain, node.firstElement (), null);
677     return new Xex.Funcall (func, vname, args);
678   }
679
680   proto.New = function (domain, fname, vname, args)
681   {
682     var func = domain.GetFunc (fname);
683     var funcall = new Xex.Funcall (func, vname, args);
684     if (func instanceof Xex.Macro)
685       funcall = funcall.Eval (domain);
686     return funcall;
687   }
688
689   proto.Eval = function (domain)
690   {
691     if (! (this.func instanceof Xex.Subrountine))
692       Xex.Log (this, domain.depth);
693     var vari;
694     if (this.vname)
695       vari = domain.GetVarCreate (this.vname);
696     domain.depth++;
697     var result;
698     try {
699       result = this.func.Call (domain, vari, this.args);
700     } finally {
701       Xex.Log (this + ' => ' + result, --domain.depth);
702     }
703     return result;
704   }
705
706   proto.Clone = function ()
707   {
708     return new Xex.Funcall (this.func, this.vari, this.args);
709   }
710
711   proto.Equals = function (obj)
712   {
713     return (obj.type == 'funcall'
714             && obj.func == this.func
715             && obj.vari.Equals (this.vari)
716             && obj.args.length == this.func.length);
717   }
718
719   proto.toString = function ()
720   {
721     var arglist = ''
722     var len = this.args.length;
723     var str = '<' + this.func.name;
724     if (this.vari)
725       str += ' vname="' + this.vari.name + '"';
726     if (len == 0)
727       return str + '/>';
728     if (this.func instanceof Xex.Subrountine)
729       for (var i = 0; i < len; i++)
730         arglist += this.args[i].toString ();
731     else
732       for (var i = 0; i < len; i++)
733         arglist += '.';
734     return str + '>' + arglist + '</' + this.func.name + '>';
735   }
736
737   Xex.Funcall.prototype = proto;
738 }) ();
739
740 Xex.ErrTerm = function (ename, message, stack)
741 {
742   this.ename = ename;
743   this.message = message;
744   this.stack = stack;
745 };
746
747 (function () {
748   var proto = new Xex.Term ('error');
749
750   proto.IsError = true;
751
752   proto.Parser = function (domain, node)
753   {
754     return new Xex.ErrTerm (node.attributes['ename'].nodeValue,
755                             node.innerText, false);
756   }
757
758   proto.CallStack = function () { return stack; }
759
760   proto.SetCallStack = function (value) { statck = value; }
761
762   proto.Clone = function ()
763   {
764     return new Xex.ErrTerm (ename, message, false);
765   }
766
767   proto.Equals = function (obj)
768   {
769     return (obj.IsError
770             && obj.ename == ename && obj.message == message
771             && (obj.stack ? (stack && stack.length == obj.stack.length)
772                 : ! stack));
773   }
774
775   proto.Matches = function (obj)
776   {
777     return (obj.IsError && obj.ename == ename);
778   }
779
780   proto.toString = function ()
781   {
782     return '<error ename="' + this.ename + '">' + this.message + '</error>';
783   }
784
785   Xex.ErrTerm.prototype = proto;
786 }) ();
787
788 Xex.IntTerm = function (num) { this.val = num; };
789 (function () {
790   var proto = new Xex.Term ('integer');
791   proto.IsInt = true;
792   proto.Intval = function () { return this.val; };
793   proto.IsTrue = function () { return this.val != 0; }
794   proto.Clone = function () { return new Xex.IntTerm (this.val); }
795   proto.Parser = function (domain, node)
796   {
797     var str = node.firstChild.nodeValue;
798
799     if (str.charAt (0) == '?' && str.length == 2)
800       return new Xex.IntTerm (str.charCodeAt (1));
801     return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
802   }
803   Xex.IntTerm.prototype = proto;
804 }) ();
805
806 Xex.StrTerm = function (str) { this.val = str; };
807 (function () {
808   var proto = new Xex.Term ('string');
809   proto.IsStr = true;
810   proto.Strval = function () { return this.val; };
811   proto.IsTrue = function () { return this.val.length > 0; }
812   proto.Clone = function () { return new Xex.StrTerm (this.val); }
813   proto.Parser = function (domain, node)
814   {
815     return new Xex.StrTerm (node.firstChild ? node.firstChild.nodeValue : '');
816   }
817   Xex.StrTerm.prototype = proto;
818 }) ();
819
820 Xex.SymTerm = function (str) { this.val = str; };
821 (function () {
822   var proto = new Xex.Term ('symbol');
823   proto.IsSymbol = true;
824   proto.IsTrue = function () { return this.val != 'nil'; }
825   proto.Clone = function () { return new Xex.SymTerm (this.val); }
826   proto.Parser = function (domain, node)
827   {
828     return new Xex.SymTerm (node.firstChild.nodeValue);
829   }
830   Xex.SymTerm.prototype = proto;
831 }) ();
832
833 Xex.LstTerm = function (list) { this.val = list; };
834 (function () {
835   var proto = new Xex.Term ('list');
836   proto.IsList = true;
837   proto.IsTrue = function () { return this.val.length > 0; }
838   proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); }
839
840   proto.Equals = function (obj)
841   {
842     if (obj.type != 'list' || obj.val.length != this.val.length)
843       return false;
844     var i, len = this.val.length;
845     for (i = 0; i < len; i++)
846       if (! this.val[i].Equals (obj.val[i]))
847         return false;
848     return true;
849   }
850
851   proto.Parser = function (domain, node)
852   {
853     var list = Xex.Term.Parse (domain, node.firstElement (), null);
854     return new Xex.LstTerm (list);
855   }
856
857   proto.toString = function ()
858   {
859     var len = this.val.length;
860
861     if (len == 0)
862       return '<list/>';
863     var str = '<list>';
864     for (var i = 0; i < len; i++)
865       str += this.val[i].toString ();
866     return str + '</list>';
867   }
868   Xex.LstTerm.prototype = proto;
869 }) ();
870
871 (function () {
872   var basic = new Xex.Domain ('basic', null, null);
873
874   function Fset (domain, vari, args)
875   {
876     if (! vari)
877       throw new Xex.ErrTerm (Xex.Error.NoVariableName,
878                              'No variable name to set');
879     vari.SetValue (args[0]);
880     return args[0];
881   }
882
883   function Fnot (domain, vari, args)
884   {
885     return (args[0].IsTrue () ? Xex.Zero : Xex.One);
886   }
887
888   function maybe_set_intvar (vari, n)
889   {
890     var term = new Xex.IntTerm (n);
891     if (vari)
892       vari.SetValue (term);
893     return term;
894   }
895
896   function Fadd (domain, vari, args)
897   {
898     var n = vari ? vari.val.Intval () : 0;
899     var len = args.length;
900
901     for (var i = 0; i < len; i++)
902       n += args[i].Intval ();
903     return maybe_set_intvar (vari, n);
904   }
905
906   function Fmul (domain, vari, args)
907   {
908     var n = vari ? vari.val.Intval () : 1;
909     for (var i = 0; i < args.length; i++)
910       n *= arg.Intval ();
911     return maybe_set_intvar (vari, n);
912   }
913
914   function Fsub (domain, vari, args)
915   {
916     var n, i;
917
918     if (! vari)
919       {
920         n = args[0].Intval ();
921         i = 1;
922       }
923     else
924       {
925         n = vari.val.Intval ();
926         i = 0;
927       }
928     while (i < args.length)
929       n -= args[i++].Intval ();
930     return maybe_set_intvar (vari, n);
931   }
932
933   function Fdiv (domain, vari, args)
934   {
935     var n, i;
936
937     if (! vari == null)
938       {
939         n = args[0].Intval ();
940         i = 1;
941       }
942     else
943       {
944         n = vari.val.Intval ();
945         i = 0;
946       }
947     while (i < args.length)
948       n /= args[i++].Intval ();
949     return maybe_set_intvar (vari, n);
950   }
951
952   function Fmod (domain, vari, args)
953   {
954     return maybe_set_intvar (vari, args[0].Intval () % args[1].Intval ());
955   }
956
957   function Flogior (domain, vari, args)
958   {
959     var n = vari == null ? 0 : vari.val;
960     for (var i = 0; i < args.length; i++)
961       n |= args[i].val;
962     return maybe_set_intvar (vari, n);
963   }
964
965   function Flogand (domain, vari, args)
966   {
967     var n, i;
968     if (vari == null)
969       {
970         Xex.Log ("logand arg args[0]" + args[0]);
971         n = args[0].Intval ()
972         i = 1;
973       }
974     else
975       {
976         Xex.Log ("logand arg var " + vari);
977         n = vari.val.Intval ();
978         i = 0;
979       }
980     while (n > 0 && i < args.length)
981       {
982         Xex.Log ("logand arg " + args[i]);
983         n &= args[i++].val;
984       }
985     return maybe_set_intvar (vari, n);
986   }
987
988   function Flsh (domain, vari, args)
989   {
990     return maybe_set_intvar (vari, args[0].Intval () << args[1].Intval ());
991   }
992
993   function Frsh (domain, vari, args)
994   {
995     return maybe_set_intvar (vari, args[0].Intval () >> args[1].Intval ());
996   }
997
998   function Fand (domain, vari, args)
999   {
1000     var len = args.length;
1001     for (var i = 0; i < len; i++)
1002     {
1003       var result = args[i].Eval (domain);
1004       if (domain.Thrown ())
1005         return result;
1006       if (! result.IsTrue ())
1007         return Xex.Zero;
1008     }
1009     return Xex.One;
1010   }
1011
1012   function For (domain, vari, args)
1013   {
1014     var len = args.length;
1015     for (var i = 0; i < len; i++)
1016     {
1017       var result = args[i].Eval (domain);
1018       if (domain.Thrown ())
1019         return result;
1020       if (result.IsTrue ())
1021         return Xex.One;
1022     }
1023     return Xex.Zero;
1024   }
1025
1026   function Feq (domain, vari, args)
1027   {
1028     for (var i = 1; i < args.length; i++)
1029       if (! args[i - 1].Equals (args[i]))
1030         return Xex.Zero;
1031     return Xex.One;
1032   }
1033
1034   function Fnoteq (domain, vari, args)
1035   {
1036     return (Feq (domain, vari, args) == Xex.One ? Xex.Zero : Xex.One);
1037   }
1038
1039   function Flt (domain, vari, args)
1040   {
1041     var n = args[0].Intval ();
1042
1043     for (var i = 1; i < args.length; i++)
1044       {
1045         var n1 = args[i].Intval ();
1046         if (n >= n1)
1047           return Xex.Zero;
1048         n = n1;
1049       }
1050     return Xex.One;
1051   }
1052
1053   function Fle (domain, vari, args)
1054   {
1055     var n = args[0].Intval ();
1056     for (var i = 1; i < args.length; i++)
1057       {
1058         var n1 = args[i].Intval ();
1059         if (n > n1)
1060           return Xex.Zero;
1061         n = n1;
1062       }
1063     return Xex.One;
1064   }
1065
1066   function Fgt (domain, vari, args)
1067   {
1068     var n = args[0].Intval ();
1069     for (var i = 1; i < args.length; i++)
1070       {
1071         var n1 = args[i].Intval ();
1072         if (n <= n1)
1073           return Xex.Zero;
1074         n = n1;
1075       }
1076     return Xex.One;
1077   }
1078
1079   function Fge (domain, vari, args)
1080   {
1081     var n = args[0].Intval ();
1082     for (var i = 1; i < args.Length; i++)
1083       {
1084         var n1 = args[i].Intval ();
1085         if (n < n1)
1086           return Xex.Zero;
1087         n = n1;
1088       }
1089     return Xex.One;
1090   }
1091
1092   function Fprogn (domain, vari, args)
1093   {
1094     var result = Xex.One;
1095     var len = args.length;
1096
1097     for (var i = 0; i < len; i++)
1098       {
1099         result = args[i].Eval (domain);
1100         if (domain.Thrown ())
1101           return result;
1102       }
1103     return result;
1104   }
1105
1106   function Fif (domain, vari, args)
1107   {
1108     var result = args[0].Eval (domain);
1109
1110     if (domain.Thrown ())
1111       return result;
1112     if (result.IsTrue ())
1113       return args[1].Eval (domain);
1114     if (args.length == 2)
1115       return Zero;
1116     return args[2].Eval (domain);
1117   }
1118
1119   function Fcond (domain, vari, args)
1120   {
1121     for (var i = 0; i < args.length; i++)
1122       {
1123         var list = args[i];
1124         var result = list.val[0].Eval (domain);
1125         if (result.IsTrue ())
1126           {
1127             for (var j = 1; j < list.val.length; j++)
1128               {
1129                 domain.depth++;
1130                 result = list.val[j].Eval (domain);
1131                 domain.depth--;
1132                 if (domain.Thrown ())
1133                   return result;
1134                 }
1135             return result;
1136           }
1137       }
1138     return Xex.Zero;
1139   }
1140
1141   function eval_terms (domain, terms, idx)
1142   {
1143     var result = Xex.Zero;
1144     domain.caught = false;
1145     for (var i = idx; i < terms.length; i++)
1146       {
1147         result = terms[i].Eval (domain);
1148         if (domain.Thrown ())
1149           return result;
1150       }
1151     return result;
1152   }
1153
1154   function Fcatch (domain, vari, args)
1155   {
1156     var caught = false;
1157     var result;
1158
1159     if (args[0].IsError)
1160       {
1161         try {
1162           result = eval_terms (domain, args, 1);
1163         } catch (e) {
1164           if (e instanceof Xex.ErrTerm)
1165             {
1166               if (! args[0].Matches (e))
1167                 throw e;
1168               if (vari)
1169                 vari.SetValue (e);
1170               return Xex.One;
1171             }
1172         }
1173       }
1174     else if (args[0].IsSymbol)
1175       {
1176         try {
1177           domain.Catch (args[0].val);
1178           result = eval_terms (domain, args, 1);
1179           if (domain.caught)
1180             {
1181               if (vari != null)
1182                 vari.SetValue (result);
1183               return Xex.One;
1184             }
1185           return Xex.Zero;
1186         } finally {
1187           domain.Uncatch ();
1188         }
1189       }
1190     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1191                            "Not a symbol nor an error: " + args[0]);
1192   }
1193
1194   function Fthrow (domain, vari, args)
1195   {
1196     if (args[0].IsSymbl)
1197       {
1198         domain.ThrowSymbol (args[0]);
1199         return (args[args.length - 1]);
1200       }
1201     if (args[0].IsError)
1202       {
1203         throw args[0];
1204       }
1205     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1206                            "Not a symbol nor an error:" + args[0]);
1207   }
1208
1209   Xex.BasicDomain = basic;
1210
1211   basic.DefSubr (Fset, "set", true, 1, 1);
1212   basic.DefAlias ("=", "set");
1213   basic.DefSubr (Fnot, "not", false, 1, 1);
1214   basic.DefAlias ("!", "not");
1215   basic.DefSubr (Fadd, "add", true, 1, -1);
1216   basic.DefSubr (Fmul, "mul", true, 1, -1);
1217   basic.DefAlias ("*", "mul");
1218   basic.DefSubr (Fsub, "sub", true, 1, -1);
1219   basic.DefAlias ("-", "sub");
1220   basic.DefSubr (Fdiv, "div", true, 1, -1);
1221   basic.DefAlias ("/", "div");
1222   basic.DefSubr (Fmod, "mod", true, 1, 2);
1223   basic.DefAlias ("%", "mod");
1224   basic.DefSubr (Flogior, "logior", true, 1, -1);
1225   basic.DefAlias ('|', "logior");
1226   basic.DefSubr (Flogand, "logand", true, 1, -1);
1227   basic.DefAlias ("&", "logand");
1228   basic.DefSubr (Flsh, "lsh", true, 1, 2);
1229   basic.DefAlias ("<<", "lsh");
1230   basic.DefSubr (Frsh, "rsh", true, 1, 2);
1231   basic.DefAlias (">>", "rsh");
1232   basic.DefSubr (Feq, "eq", false, 2, -1);
1233   basic.DefAlias ("==", "eq");
1234   basic.DefSubr (Fnoteq, "noteq", false, 2, 2);
1235   basic.DefAlias ("!=", "noteq");
1236   basic.DefSubr (Flt, "lt", false, 2, -1);
1237   basic.DefAlias ("<", "lt");
1238   basic.DefSubr (Fle, "le", false, 2, -1);
1239   basic.DefAlias ("<=", "le");
1240   basic.DefSubr (Fgt, "gt", false, 2, -1);
1241   basic.DefAlias (">", "gt");
1242   basic.DefSubr (Fge, "ge", false, 2, -1);
1243   basic.DefAlias (">=", "ge");
1244   basic.DefSubr (Fthrow, "throw", false, 1, 2);
1245
1246   //basic.DefSubr (Fappend, "append", true, 0, -1);
1247   //basic.DefSubr (Fconcat, "concat", true, 0, -1);
1248   //basic.DefSubr (Fnth, "nth", false, 2, 2);
1249   //basic.DefSubr (Fcopy, "copy", false, 1, 1);
1250   //basic.DefSubr (Fins, "ins", true, 2, 2);
1251   //basic.DefSubr (Fdel, "del", true, 2, 2);
1252   //basic.DefSubr (Feval, "eval", false, 1, 1);
1253   //basic.DefSubr (Fbreak, "break", false, 0, 1);
1254   //basic.DefSubr (Freturn, "return", false, 0, 1);
1255   //basic.DefSubr (Fthrow, "throw", false, 1, 2);
1256
1257   basic.DefSpecial (Fand, "and", false, 1, -1);
1258   basic.DefAlias ("&&", "and");
1259   basic.DefSpecial (For, "or", false, 1, -1);
1260   basic.DefAlias ("||", "or");
1261   basic.DefSpecial (Fprogn, "progn", false, 1, -1);
1262   basic.DefAlias ("expr", "progn");
1263   basic.DefSpecial (Fif, "if", false, 2, 3);
1264   //basic.DefSpecial (Fwhen, "when", false, 1, -1);
1265   //basic.DefSpecial (Floop, "loop", false, 1, -1);
1266   //basic.DefSpecial (Fwhile, "while", false, 1, -1);
1267   basic.DefSpecial (Fcond, "cond", false, 1, -1);
1268   //basic.DefSpecial (Fforeach, "foreach", true, 2, -1);
1269   //basic.DefSpecial (Fquote, "quote", false, 1, 1);
1270   //basic.DefSpecial (Ftype, "type", false, 1, 1);
1271   basic.DefSpecial (Fcatch, "catch", true, 2, -1);
1272
1273   basic.DefType (Xex.Funcall.prototype);
1274   basic.DefType (Xex.Varref.prototype);
1275   basic.DefType (Xex.ErrTerm.prototype);
1276   basic.DefType (Xex.IntTerm.prototype);
1277   basic.DefType (Xex.StrTerm.prototype);
1278   basic.DefType (Xex.SymTerm.prototype);
1279   basic.DefType (Xex.LstTerm.prototype);
1280
1281 }) ();
1282
1283 Xex.Zero = new Xex.IntTerm (0);
1284 Xex.One = new Xex.IntTerm (1);
1285 Xex.nil = new Xex.SymTerm ('nil');
1286
1287 Xex.Load = function (server, file)
1288 {
1289   var obj = new XMLHttpRequest ();
1290   var url = server ? server + '/' + file : file;
1291   obj.open ('GET', url, false);
1292   obj.overrideMimeType ('text/xml');
1293   obj.send ('');
1294   return obj.responseXML.firstChild;
1295 }
1296
1297 var MIM = {
1298   // URL of the input method server.
1299   server: "http://www.m17n.org/common/mim-js",
1300   // Boolean flag to tell if MIM is active or not.
1301   enabled: true,
1302   // Boolean flag to tell if MIM is running in debug mode or not.
1303   debug: false,
1304   // List of main input methods.
1305   imlist: {},
1306   // List of extra input methods;
1307   imextra: {},
1308   // Global input method data
1309   im_global: null,
1310   // Currently selected input method.
1311   current: false,
1312
1313   // enum
1314   LoadStatus: { NotLoaded:0, Loading:1, Loaded:2, Error:-1 },
1315   ChangedStatus: {
1316     None:       0x00,
1317     StateTitle: 0x01,
1318     PreeditText:0x02,
1319     CursorPos:  0x04,
1320     CandidateList:0x08,
1321     CandidateIndex:0x10,
1322     CandidateShow:0x20,
1323     Preedit:    0x06,           // PreeditText | CursorPos
1324     Candidate:  0x38 // CandidateList | CandidateIndex | CandidateShow
1325   },
1326   KeyModifier: {
1327     SL: 0x00400000,
1328     SR: 0x00800000,
1329     S:  0x00C00000,
1330     CL: 0x01000000,
1331     CR: 0x02000000,
1332     C:  0x03000000,
1333     AL: 0x04000000,
1334     AR: 0x08000000,
1335     A:  0x0C000000,
1336     ML: 0x04000000,
1337     MR: 0x08000000,
1338     M:  0x0C000000,
1339     G:  0x10000000,
1340     s:  0x20000000,
1341     H:  0x40000000,
1342     High:       0x70000000,
1343     All:        0x7FC00000
1344   },
1345   Error: {
1346     ParseError: "parse-error"
1347   }
1348 };
1349   
1350 (function () {
1351   var keysyms = new Array ();
1352   keysyms["bs"] = "backspace";
1353   keysyms["lf"] = "linefeed";
1354   keysyms["cr"] = keysyms["enter"] = "return";
1355   keysyms["esc"] = "escape";
1356   keysyms["spc"] = "space";
1357   keysyms["del"] = "delete";
1358
1359   function decode_keysym (str) {
1360     if (str.length == 1)
1361       return str;
1362     var parts = str.split ("-");
1363     var len = parts.length, i;
1364     var has_modifier = len > 1;
1365
1366     for (i = 0; i < len - 1; i++)
1367       if (! MIM.KeyModifier.hasOwnProperty (parts[i]))
1368         return false;
1369     var key = parts[len - 1];
1370     if (key.length > 1)
1371       {
1372         key = keysyms[key.toLowerCase ()];
1373         if (key)
1374           {
1375             if (len > 1)
1376               {
1377                 str = parts[0];
1378                 for (i = 1; i < len - 1; i++)
1379                   str += '-' + parts[i];
1380                 str += '-' + key;
1381               }
1382             else
1383               str = key;
1384           }
1385       }
1386     if (has_modifier)
1387       {
1388         parts = new Array ();
1389         parts.push (str);
1390         return parts;
1391       }
1392     return str;
1393   }
1394
1395   MIM.Key = function (val)
1396   {
1397     this.key;
1398     this.has_modifier = false;
1399     if (val instanceof Xex.Term)
1400       this.key = val.val;
1401     else if (typeof val == 'string' || val instanceof String)
1402       {
1403         this.key = decode_keysym (val);
1404         if (! this.key)
1405           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
1406         if (this.key instanceof Array)
1407           {
1408             this.key = this.key[0];
1409             this.has_modifier = true;
1410           }
1411       }
1412     else if (typeof val == 'number' || val instanceof Number)
1413       this.key = String.fromCharCode (val);
1414     else
1415       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
1416   }
1417
1418   MIM.Key.prototype.toString = function () { return this.key; };
1419
1420   MIM.Key.FocusIn = new MIM.Key (new Xex.StrTerm ('input-focus-in'));
1421   MIM.Key.FocusOut = new MIM.Key (new Xex.StrTerm ('input-focus-out'));
1422   MIM.Key.FocusMove = new MIM.Key (new Xex.StrTerm ('input-focus-move'));
1423 }) ();
1424
1425 (function () {
1426   MIM.KeySeq = function (seq)
1427   {
1428     this.val = new Array ();
1429     this.has_modifier = false;
1430
1431     if (seq)
1432       {
1433         if (seq.IsList)
1434           {
1435             var len = seq.val.length;
1436             for (var i = 0; i < len; i++)
1437               {
1438                 var v = seq.val[i], key;
1439                 if (v.type == 'symbol' || v.type == 'string')
1440                   key = new MIM.Key (v);
1441                 else if (v.type == 'integer')
1442                   key = new MIM.Key (v.val);
1443                 else
1444                   throw new Xex.ErrTerm (MIM.Error.ParseError,
1445                                          "Invalid key: " + v);
1446                 this.val.push (key);
1447                 if (key.has_modifier)
1448                   this.has_modifier = true;
1449               }
1450           }
1451         else if (seq.IsStr)
1452           {
1453             var len = seq.val.length;
1454             for (var i = 0; i < len; i++)
1455               this.val.push (new MIM.Key (seq.val.charCodeAt (i)));
1456           }
1457         else
1458           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + seq);
1459       }
1460   }
1461
1462   var proto = new Xex.Term ('keyseq');
1463   proto.Clone = function () { return this; }
1464   proto.Parser = function (domain, node)
1465   {
1466     var seq = new Array ();
1467     for (node = node.firstChild; node; node = node.nextSibling)
1468       if (node.nodeType == 1)
1469         {
1470           var term = Xex.Term.Parse (domain, node);
1471           return new MIM.KeySeq (term);
1472         }
1473     throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid keyseq");
1474   }
1475   proto.toString = function ()
1476   {
1477     var len = this.val.length;
1478     if (len == 0)
1479       return '<keyseq/>';
1480     var first = true;
1481     var str = '<keyseq>';
1482     for (var i = 0; i < len; i++)
1483       {
1484         if (first)
1485           first = false;
1486         else if (this.has_modifier)
1487           str += ' ';
1488         str += this.val[i].toString ();
1489       }
1490     return str + '</keyseq>';
1491   }
1492
1493   MIM.KeySeq.prototype = proto;
1494 }) ();
1495
1496 (function () {
1497   MIM.Marker = function () { }
1498   MIM.Marker.prototype = new Xex.Term ('marker');
1499   MIM.Marker.prototype.CharAt = function (ic)
1500   {
1501     var p = this.Position (ic);
1502     if (p < 0 || p >= ic.preedit.length)
1503       return 0;
1504     return ic.preedit.charCodeAt (p);
1505   }
1506
1507   MIM.FloatingMarker = function (name) { this.val = name; };
1508   var proto = new MIM.Marker ();
1509   MIM.FloatingMarker.prototype = proto;
1510   proto.Position = function (ic) { return ic.marker_positions[this.val]; };
1511   proto.Mark = function (ic) { ic.marker_positions[this.val] = ic.cursor_pos; };
1512
1513   MIM.PredefinedMarker = function (name) { this.val = name; }
1514   MIM.PredefinedMarker.prototype = new MIM.Marker ();
1515   MIM.PredefinedMarker.prototype.Position = function (ic)
1516   {
1517     if (typeof this.pos == 'number')
1518       return this.pos;
1519     return this.pos (ic);
1520   }
1521
1522   var predefined = { }
1523
1524   function def_predefined (name, position)
1525   {
1526     predefined[name] = new MIM.PredefinedMarker (name);
1527     predefined[name].pos = position;
1528   }
1529
1530   def_predefined ('@<', 0);
1531   def_predefined ('@>', function (ic) { return ic.preedit.length; });
1532   def_predefined ('@-', function (ic) { return ic.cursor_pos - 1; });
1533   def_predefined ('@+', function (ic) { return ic.cursor_pos + 1; });
1534   def_predefined ('@[', function (ic) {
1535     if (ic.cursor_pos > 0)
1536       {
1537         var pos = ic.cursor_pos;
1538         return ic.preedit.FindProp ('candidates', pos - 1).from;
1539       }
1540     return 0;
1541   });
1542   def_predefined ('@]', function (ic) {
1543     if (ic.cursor_pos < ic.preedit.length - 1)
1544       {
1545         var pos = ic.cursor_pos;
1546         return ic.preedit.FindProp ('candidates', pos).to;
1547       }
1548     return ic.preedit.length;
1549   });
1550   for (var i = 0; i < 10; i++)
1551     def_predefined ("@" + i, i);
1552   predefined['@first'] = predefined['@<'];
1553   predefined['@last'] = predefined['@>'];
1554   predefined['@previous'] = predefined['@-'];
1555   predefined['@next'] = predefined['@+'];
1556   predefined['@previous-candidate-change'] = predefined['@['];
1557   predefined['@next-candidate-change'] = predefined['@]'];
1558
1559   MIM.SurroundMarker = function (name)
1560   {
1561     this.val = name;
1562     this.distance = parseInt (name.slice (1));
1563     if (isNaN (this.distance))
1564       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name);
1565   }
1566   MIM.SurroundMarker.prototype = new MIM.Marker ();
1567   MIM.SurroundMarker.prototype.Position = function (ic)
1568   {
1569     return ic.cursor_pos + this.distance;
1570   }
1571   MIM.SurroundMarker.prototype.CharAt = function (ic)
1572   {
1573     if (this.val == '@-0')
1574       return -1;
1575     var p = this.Position (ic);
1576     if (p < 0)
1577       return ic.GetSurroundingChar (p);
1578     else if (p >= ic.preedit.length)
1579       return ic.GetSurroundingChar (p - ic.preedit.length);
1580     return ic.preedit.charCodeAt (p);
1581   }
1582
1583   MIM.Marker.prototype.Parser = function (domain, node)
1584   {
1585     var name = node.firstChild.nodeValue;
1586     if (name.charAt (0) == '@')
1587       {
1588         var n = predefined[name];
1589         if (n)
1590           return n;
1591         if (name.charAt (1) == '-' || name.charAt (1) == '+')
1592           return new MIM.SurroundMarker (name);
1593         throw new Xex.ErrTerm (MIM.Error.ParseError,
1594                                "Invalid marker: " + name);
1595       }
1596     return new MIM.FloatingMarker (name);;
1597   }
1598 }) ();
1599
1600 MIM.Selector = function (name)
1601 {
1602   this.val = name;
1603 }
1604 MIM.Selector.prototype = new Xex.Term ('selector');
1605
1606 (function () {
1607   var selectors = {};
1608   selectors["@<"] = selectors["@first"] = new MIM.Selector ('@<');
1609   selectors["@="] = selectors["@current"] = new MIM.Selector ('@=');
1610   selectors["@>"] = selectors["@last"] = new MIM.Selector ('@>');
1611   selectors["@-"] = selectors["@previous"] = new MIM.Selector ('@-');
1612   selectors["@+"] = selectors["@next"] = new MIM.Selector ('@+');
1613   selectors["@["] = selectors["@previous-candidate-change"]
1614     = new MIM.Selector ('@[');
1615   selectors["@]"] = selectors["@next-candidate-change"]
1616     = new MIM.Selector ('@]');
1617
1618   MIM.Selector.prototype.Parser = function (domain, node)
1619   {
1620     var name = node.firstChild.nodeValue;
1621     var s = selectors[name];
1622     if (! s)
1623       throw new Xex.ErrTerm (MIM.Error.ParseError,
1624                              "Invalid selector: " + name);
1625     return s;
1626   }
1627 }) ();
1628
1629 MIM.Rule = function (keyseq, actions)
1630 {
1631   this.keyseq = keyseq;
1632   this.actions = actions;
1633 }
1634 MIM.Rule.prototype = new Xex.Term ('rule');
1635 MIM.Rule.prototype.Parser = function (domain, node)
1636 {
1637   var n;
1638   for (n = node.firstChild; n && n.nodeType != 1; n = n.nextSibling);
1639   if (! n)
1640     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1641   var keyseq = Xex.Term.Parse (domain, n);
1642   if (keyseq.type != 'keyseq')
1643     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1644   var actions = Xex.Term.Parse (domain, n.nextElement (), null);
1645   return new MIM.Rule (keyseq, actions);
1646 }
1647 MIM.Rule.prototype.toString = function ()
1648 {
1649   return '<rule/>';
1650 }
1651
1652 MIM.Map = function (name)
1653 {
1654   this.name = name;
1655   this.rules = new Array ();
1656 };
1657
1658 (function () {
1659   var proto = new Xex.Term ('map');
1660
1661   proto.Parser = function (domain, node)
1662   {
1663     var name = node.attributes['mname'].nodeValue;
1664     if (! name)
1665       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1666     var map = new MIM.Map (name);
1667     for (var n = node.firstChild; n; n = n.nextSibling)
1668       if (n.nodeType == 1)
1669         map.rules.push (Xex.Term.Parse (domain, n));
1670     return map;
1671   }
1672
1673   proto.toString = function ()
1674   {
1675     var str = '<map mname="' + this.name + '">';
1676     var len = this.rules.length;
1677     for (i = 0; i < len; i++)
1678       str += this.rules[i];
1679     return str + '</map>';
1680   }
1681
1682   MIM.Map.prototype = proto;
1683 }) ();
1684
1685 Xex.CatchTag._mimtag = new Xex.SymTerm ('@mimtag');
1686
1687 MIM.Action = function (domain, terms)
1688 {
1689   var args = new Array ();
1690   args.push (Xex.CatchTag_.mimtag);
1691   for (var i = 0; i < terms.length; i++)
1692     args.push (terms[i]);
1693   this.action = Xex.Funcall.prototype.New (domain, 'catch', null, args);
1694 }
1695
1696 MIM.Action.prototype.Run = function (domain)
1697 {
1698   var result = this.action.Eval (domain);
1699   if (result.type == 'error')
1700     {
1701       domain.context.Error = result.toString ();
1702       return false;
1703     }
1704   return (result != Xex.CatchTag._mimtag);
1705 }
1706
1707 MIM.Keymap = function ()
1708 {
1709   this.name = 'TOP';
1710   this.submaps = null;
1711 };
1712
1713 (function () {
1714   var proto = {};
1715
1716   function add_rule (keymap, rule, branch_actions)
1717   {
1718     var keyseq = rule.keyseq;
1719     var len = keyseq.val.length;
1720     var name = '';
1721
1722     for (var i = 0; i < len; i++)
1723       {
1724         var key = keyseq.val[i];
1725         var sub = false;
1726
1727         name += key.key;
1728         if (! keymap.submaps)
1729           keymap.submaps = {};
1730         else
1731           sub = keymap.submaps[key.key];
1732         if (! sub)
1733           keymap.submaps[key.key] = sub = new MIM.Keymap ();
1734         keymap = sub;
1735         keymap.name = name;
1736       }
1737     keymap.map_actions = rule.actions;
1738     keymap.branch_actions = branch_actions;
1739   }
1740
1741   proto.Add = function (map, branch_actions)
1742   {
1743     var rules = map.rules;
1744     var len = rules.length;
1745
1746     for (var i = 0; i < len; i++)
1747       add_rule (this, rules[i], branch_actions);
1748   }
1749   proto.Lookup = function (keys, index)
1750   {
1751     var sub;
1752
1753     if (index < keys.val.length && this.submaps
1754         && (sub = this.submaps[keys.val[index].key]))
1755       {
1756         index++;
1757         return sub.Lookup (keys, index);
1758       }
1759     return { map: this, index: index };
1760   }
1761
1762   MIM.Keymap.prototype = proto;
1763 }) ();
1764
1765 MIM.State = function (name)
1766 {
1767   this.name = name;
1768   this.keymap = new MIM.Keymap ();
1769 };
1770
1771 (function () {
1772   var proto = new Xex.Term ('state');
1773
1774   proto.Parser = function (domain, node)
1775   {
1776     var map_list = domain.map_list;
1777     var name = node.attributes['sname'].nodeValue;
1778     if (! name)
1779       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1780     var state = new MIM.State (name);
1781     for (node = node.firstElement (); node; node = node.nextElement ())
1782       {
1783         if (node.nodeName == 'title')
1784           state.title = node.firstChild.nodeValue;
1785         else
1786           {
1787             var n = node.firstElement ();
1788             if (node.nodeName == 'branch')
1789               state.keymap.Add (map_list[node.attributes['mname'].nodeValue],
1790                                 Xex.Term.Parse (domain, n, null));
1791             else if (node.nodeName == 'state-hook')
1792               state.enter_actions = Xex.Term.Parse (domain, n, null);
1793             else if (node.nodeName == 'catch-all-branch')
1794               state.fallback_actions = Xex.Term.Parse (domain, n, null);
1795           }
1796       }
1797     return state;
1798   }
1799
1800   proto.toString = function ()
1801   {
1802     return '<state sname="' + this.name + '">' + this.keymap + '</state>';
1803   }
1804
1805   MIM.State.prototype = proto;
1806 }) ();
1807
1808 (function () {
1809   function Block (index, term)
1810   {
1811     this.Index = index;
1812     if (term.IsStr)
1813       this.Data = term.val;
1814     else if (term.IsList)
1815       {
1816         this.Data = new Array ();
1817         for (var i = 0; i < term.val.length; i++)
1818           this.Data.push (term.val[i].val);
1819       }
1820   }
1821
1822   Block.prototype.Count = function () { return this.Data.length; }
1823   Block.prototype.get = function (i)
1824   {
1825     return (this.Data instanceof Array ? this.Data[i] : this.Data.charAt (i));
1826   }
1827
1828   MIM.Candidates = function (candidates, column)
1829   {
1830     this.column = column;
1831     this.row = 0;
1832     this.index = 0;
1833     this.total = 0;
1834     this.blocks = new Array ();
1835
1836     for (var i = 0; i < candidates.length; i++)
1837       {
1838         var block = new Block (this.total, candidates[i]);
1839         this.blocks.push (block);
1840         this.total += block.Count ();
1841       }
1842   }
1843
1844   function get_col ()
1845   {
1846     return (this.column > 0 ? this.index % this.column
1847             : this.index - this.blocks[this.row].Index);
1848   }
1849
1850   function prev_group ()
1851   {
1852     var col = get_col.call (this);
1853     var nitems;
1854     if (this.column > 0)
1855       {
1856         this.index -= this.column;
1857         if (this.index >= 0)
1858           nitems = this.column;
1859         else
1860           {
1861             var lastcol = (this.total - 1) % this.column;
1862             this.index = (col < lastcol ? this.total - lastcol + col
1863                           : this.total - 1);
1864             this.row = this.blocks.length - 1;
1865             nitems = lastcol + 1;
1866           }
1867         while (this.blocks[this.row].Index > this.index)
1868           this.row--;
1869       }
1870     else
1871       {
1872         this.row = this.row > 0 ? this.row - 1 : this.blocks.length - 1;
1873         nitems = this.blocks[this.row].Count ();
1874         this.index = (this.blocks[this.row].Index
1875                       + (col < nitems ? col : nitems - 1));
1876       }
1877     return nitems;
1878   }
1879
1880   function next_group ()
1881   {
1882     var col = get_col.call (this);
1883     var nitems;
1884     if (this.column > 0)
1885       {
1886         this.index += this.column - col;
1887         if (this.index < this.total)
1888           {
1889             if (this.index + col >= this.total)
1890               {
1891                 nitems = this.total - this.index;
1892                 this.index = this.total - 1;
1893               }
1894             else
1895               {
1896                 nitems = this.column;
1897                 this.index += col;
1898               }
1899           }
1900         else
1901           {
1902             this.index = col;
1903             this.row = 0;
1904           }
1905         while (this.blocks[this.row].Index > this.index)
1906           this.row++;
1907       }
1908     else
1909       {
1910         this.row = this.row < this.blocks.length - 1 ? this.row + 1 : 0;
1911         nitems = this.blocks[this.row].Count ();
1912         this.index = (this.blocks[this.row].Index
1913                       + (col < nitems ? col : nitems - 1));
1914       }
1915     return nitems;
1916   }
1917
1918   function prev ()
1919   {
1920     if (this.index == 0)
1921       {
1922         this.index = this.total - 1;
1923         this.row = this.blocks.length - 1;
1924       }
1925     else
1926       {
1927         this.index--;
1928         if (this.blocks[this.row].Index > this.index)
1929           this.row--;
1930       }
1931     }
1932
1933   function next ()
1934   {
1935     this.index++;
1936     if (this.index == this.total)
1937       {
1938         this.index = 0;
1939         this.row = 0;
1940       }
1941     else
1942       {
1943         var b = this.blocks[this.row];
1944         if (this.index == b.Index + b.Count ())
1945           this.row++;
1946       }
1947   }
1948
1949   function first ()
1950   {
1951     this.index -= get_col.call (this);
1952     while (this.blocks[this.row].Index > this.index)
1953       this.row--;
1954   }
1955
1956   function last ()
1957   {
1958     var b = this.blocks[this.row];
1959     if (this.column > 0)
1960       {
1961         if (this.index + 1 < this.total)
1962           {
1963             this.index += this.column - get_col.call (this) + 1;
1964             while (b.Index + b.Count () <= this.index)
1965               b = this.blocks[++this.row];
1966           }
1967       }
1968     else
1969       this.index = b.Index + b.Count () - 1;
1970   }
1971
1972   MIM.Candidates.prototype.Current = function ()
1973   {
1974     var b = this.blocks[this.row];
1975     return b.get (this.index - b.Index);
1976   }
1977
1978   MIM.Candidates.prototype.Select = function (selector)
1979   {
1980     if (selector.type == 'selector')
1981       {
1982         switch (selector.val)
1983           {
1984           case '@<': first.call (this); break;
1985           case '@>': last.call (this); break;
1986           case '@-': prev.call (this); break;
1987           case '@+': next.call (this); break;
1988           case '@[': prev_group.call (this); break;
1989           case '@]': next_group.cal (this); break;
1990           default: break;
1991           }
1992         return this.Current ();
1993       }
1994     var col, start, end
1995     if (this.column > 0)
1996       {
1997         col = this.index % this.column;
1998         start = this.index - col;
1999         end = start + this.column;
2000       }
2001     else
2002       {
2003         start = this.blocks[this.row].Index;
2004         col = this.index - start;
2005         end = start + this.blocks[this.row].Count;
2006       }
2007     if (end > this.total)
2008       end = this.total;
2009     this.index += selector.val - col;
2010     if (this.index >= end)
2011       this.index = end - 1;
2012     if (this.column > 0)
2013       {
2014         if (selector.val > col)
2015           while (this.blocks[this.row].Index + this.blocks[this.row].Count
2016                  < this.index)
2017             this.row++;
2018         else
2019           while (this.blocks[this.row].Index > this.index)
2020             this.row--;
2021       }
2022     return this.Current ();
2023   }
2024 }) ();
2025
2026 MIM.im_domain = new Xex.Domain ('input-method', null, null);
2027 MIM.im_domain.DefType (MIM.KeySeq.prototype);
2028 MIM.im_domain.DefType (MIM.Marker.prototype);
2029 MIM.im_domain.DefType (MIM.Selector.prototype);
2030 MIM.im_domain.DefType (MIM.Rule.prototype);
2031 MIM.im_domain.DefType (MIM.Map.prototype);
2032 MIM.im_domain.DefType (MIM.State.prototype);
2033
2034 (function () {
2035   var im_domain = MIM.im_domain;
2036
2037   function Finsert (domain, vari, args)
2038   {
2039     var text;
2040     if (args[0].type == 'integer')
2041       text = String.fromCharCode (args[0].val);
2042     else
2043       text = args[0].val;
2044     domain.context.ins (text, null);
2045     return args[0];
2046   }
2047
2048   function Finsert_candidates (domain, vari, args)
2049   {
2050     var ic = domain.context;
2051     var gsize = domain.variables['candidates_group_size'];
2052     var candidates = new MIM.Candidates (args, gsize ? gsize.Intval () : 0);
2053     ic.ins (candidates.Current (), candidates);
2054     return args[0];
2055   }
2056
2057   function Fdelete (domain, vari, args)
2058   {
2059     var ic = domain.context;
2060     var pos = args[0].IsInt ? args[0].Intval () : args[0].Position (ic);
2061     return new Xex.IntTerm (ic.del (pos));
2062   }
2063
2064   function Fselect (domain, vari, args)
2065   {
2066     var ic = domain.context;
2067     var can = ic.candidates;
2068
2069     if (can)
2070       {
2071         var old_text = can.Current ();
2072         var new_text = can.Select (args[0]);
2073         ic.rep (old_text, new_text, can);
2074       }
2075     else
2076       Xex.Log ('no candidates at ' + ic.cursor_pos + ' of ' + ic.candidate_table.table.length);
2077     return args[0];
2078   }
2079
2080   function Fshow (domain, vari, args)
2081   {
2082     domain.context.candidate_show = true;
2083     domain.context.changed |= MIM.ChangedStatus.CandidateShow;
2084     return Xex.nil;
2085   }
2086
2087   function Fhide (domain, vari, args)
2088   {
2089     domain.context.candidate_show = false;
2090     domain.context.changed |= MIM.ChangedStatus.CandidateShow;
2091     return Xex.nil;
2092   }
2093
2094   function Fchar_at (domain, vari, args)
2095   {
2096     return new Xex.IntTerm (args[0].CharAt (domain.context));
2097   }
2098
2099   function Fmove (domain, vari, args)
2100   {
2101     var ic = domain.context;
2102     var pos = args[0].IsInt ? args[0].val : args[0].Position (ic);
2103     ic.move (pos);
2104     return new Xex.IntTerm (pos);
2105   }
2106
2107   function Fmark (domain, vari, args)
2108   {
2109     args[0].Mark (domain.context);
2110     return args[0];
2111   }
2112
2113   function Fpushback (domain, vari, args)
2114   {
2115     var a = (args[0].IsInt ? args[0].Intval ()
2116              : args[0].IsStr ? new KeySeq (args[0])
2117              : args[0]);
2118     domain.context.pushback (a);
2119     return args[0];
2120   }
2121
2122   function Fpop (domain, vari, args)
2123   {
2124     var ic = domain.context;
2125     if (ic.key_head < ic.keys.val.length)
2126       ic.keys.val.splice (ic.keys_head, 1);
2127     return Xex.nil;
2128   }
2129
2130   function Fundo  (domain, vari, args)
2131   {
2132     var ic = domain.context;
2133     var n = args.length == 0 ? -2 : args[0].val;
2134     Xex.Log ('undo with arg ' + args[0]);
2135     if (n < 0)
2136       ic.keys.val.splice (ic.keys.val.length + n, -n);
2137     else
2138       ic.keys.val.splice (n, ic.keys.val.length);
2139     ic.reset ();
2140     return Xex.nil;
2141   }
2142
2143   function Fcommit (domain, vari, args)
2144   {
2145     domain.context.commit ();
2146     return Xex.nil;
2147   }
2148
2149   function Funhandle (domain, vari, args)
2150   {
2151     domain.context.commit ();
2152     return Xex.Fthrow (domain, vari, Xex.CatchTag._mimtag);
2153   }
2154
2155   function Fshift (domain, vari, args)
2156   {
2157     var ic = domain.context;
2158     var state_name = args[0].val;
2159     var state = ic.im.state_list[state_name];
2160     if (! state)
2161       throw ("Unknown state: " + state_name);
2162       ic.shift (state);
2163     return args[0];
2164   }
2165
2166   function Fshiftback (domain, vari, args)
2167   {
2168     domain.context.shift (null);
2169     return Xex.nil;
2170   }
2171
2172   function Fkey_count (domain, vari, args)
2173   {
2174     return new Xex.IntTerm (domain.context.key_head);
2175   }
2176
2177   function Fsurrounding_flag (domain, vari, args)
2178   {
2179     return new Xex.IntTerm (-1);
2180   }
2181
2182   im_domain.DefSubr (Finsert, "insert", false, 1, 1);
2183   im_domain.DefSubr (Finsert_candidates, "insert-candidates", false, 1, 1);
2184   im_domain.DefSubr (Fdelete, "delete", false, 1, 1);
2185   im_domain.DefSubr (Fselect, "select", false, 1, 1);
2186   im_domain.DefSubr (Fshow, "show-candidates", false, 0, 0);
2187   im_domain.DefSubr (Fhide, "hide-candidates", false, 0, 0);
2188   im_domain.DefSubr (Fmove, "move", false, 1, 1);
2189   im_domain.DefSubr (Fmark, "mark", false, 1, 1);
2190   im_domain.DefSubr (Fpushback, "pushback", false, 1, 1);
2191   im_domain.DefSubr (Fpop, "pop", false, 0, 0);
2192   im_domain.DefSubr (Fundo, "undo", false, 0, 1);
2193   im_domain.DefSubr (Fcommit, "commit", false, 0, 0);
2194   im_domain.DefSubr (Funhandle, "unhandle", false, 0, 0);
2195   im_domain.DefSubr (Fshift, "shift", false, 1, 1);
2196   im_domain.DefSubr (Fshiftback, "shiftback", false, 0, 0);
2197   im_domain.DefSubr (Fchar_at, "char-at", false, 1, 1);
2198   im_domain.DefSubr (Fkey_count, "key-count", false, 0, 0);
2199   im_domain.DefSubr (Fsurrounding_flag, "surrounding-text-flag", false, 0, 0);
2200 }) ();
2201
2202
2203 (function () {
2204   function get_global_var (vname)
2205   {
2206     if (MIM.im_global.load_status == MIM.LoadStatus.NotLoaded)
2207       MIM.im_global.Load ()
2208     return MIM.im_global.domain.variables[vname];
2209   }
2210
2211   function include (node)
2212   {
2213     node = node.firstElement ();
2214     if (node.nodeName != 'tags')
2215       return null;
2216     
2217     var lang = null, name = null, extra = null;
2218     for (node = node.firstElement (); node; node = node.nextElement ())
2219       {
2220         if (node.nodeName == 'language')
2221           lang = node.firstChild.nodeValue;
2222         else if (node.nodeName == 'name')
2223           name = node.firstChild.nodeValue;
2224         else if (node.nodeName == 'extra-id')
2225           extra = node.firstChild.nodeValue;
2226       }
2227     if (! lang || ! MIM.imlist[lang])
2228       return null;
2229     if (! extra)
2230       {
2231         if (! name || ! (im = MIM.imlist[lang][name]))
2232           return null;
2233       }
2234     else
2235       {
2236         if (! (im = MIM.imextra[lang][extra]))
2237           return null;
2238       }
2239     if (im.load_status != MIM.LoadStatus.Loaded
2240         && (im.load_status != MIM.LoadStatus.NotLoaded || ! im.Load ()))
2241       return null;
2242     return im;
2243   }
2244
2245   var parsers = { };
2246
2247   parsers['description'] = function (node)
2248   {
2249     this.description = node.firstChild.nodeValue;
2250   }
2251   parsers['variable-list'] = function (node)
2252   {
2253     for (node = node.firstElement (); node; node = node.nextElement ())
2254       {
2255         var vname = node.attributes['vname'].nodeValue;
2256         if (this != MIM.im_global)
2257           {
2258             var vari = get_global_var (vname);
2259             if (vari != null)
2260               this.domain.Defvar (vname);
2261           }
2262         vname = Xex.Term.Parse (this.domain, node)
2263       }
2264   }
2265   parsers['command-list'] = function (node)
2266   {
2267   }
2268   parsers['macro-list'] = function (node)
2269   {
2270     for (var n = node.firstElement (); n; n = n.nextElement ())
2271       if (n.nodeName == 'xi:include')
2272         {
2273           var im = include (n);
2274           if (! im)
2275             alert ('inclusion fail');
2276           else
2277             for (var macro in im.domain.functions)
2278               {
2279                 var func = im.domain.functions[macro];
2280                 if (func instanceof Xex.Macro)
2281                   im.domain.CopyFunc (this.domain, macro);
2282               }
2283           n = n.previousSibling;
2284           node.removeChild (n.nextSibling);
2285         }
2286     Xex.Term.Parse (this.domain, node.firstElement (), null);
2287   }
2288   parsers['title'] = function (node)
2289   {
2290     this.title = node.firstChild.nodeValue;
2291   }
2292   parsers['map-list'] = function (node)
2293   {
2294     for (node = node.firstElement (); node; node = node.nextElement ())
2295       {
2296         if (node.nodeName == 'xi:include')
2297           {
2298             var im = include (node);
2299             if (! im)
2300               {
2301                 alert ('inclusion fail');
2302                 continue;
2303               }
2304             for (var mname in im.map_list)
2305               this.map_list[mname] = im.map_list[mname];
2306           }
2307         else
2308           {
2309             var map = Xex.Term.Parse (this.domain, node);
2310             this.map_list[map.name] = map;
2311           }
2312       }
2313   }
2314   parsers['state-list'] = function (node)
2315   {
2316     this.domain.map_list = this.map_list;
2317     for (node = node.firstElement (); node; node = node.nextElement ())
2318       {
2319         if (node.nodeName == 'xi:include')
2320           {
2321             var im = include (node);
2322             if (! im)
2323               alert ('inclusion fail');
2324             for (var sname in im.state_list)
2325               {
2326                 state = im.state_list[sname];
2327                 if (! this.initial_state)
2328                   this.initial_state = state;
2329                 this.state_list[sname] = state;
2330               }
2331           }
2332         else if (node.nodeName == 'state')
2333           {
2334             var state = Xex.Term.Parse (this.domain, node);
2335             if (! state.title)
2336               state.title = this.title;
2337             if (! this.initial_state)
2338               this.initial_state = state;
2339             this.state_list[state.name] = state;
2340           }
2341       }
2342     delete this.domain.map_list;
2343   }
2344
2345   MIM.IM = function (lang, name, extra_id, file)
2346   {
2347     this.lang = lang;
2348     this.name = name;
2349     this.extra_id = extra_id;
2350     this.file = file;
2351     this.load_status = MIM.LoadStatus.NotLoaded;
2352     this.domain = new Xex.Domain (this.lang + '-'
2353                                   + (this.name != 'nil'
2354                                      ? this.name : this.extra_id),
2355                                   MIM.im_domain, null);
2356   }
2357
2358   var proto = {
2359     Load: function ()
2360     {
2361       var node = Xex.Load (null, this.file);
2362       if (! node)
2363         {
2364           this.load_status = MIM.LoadStatus.Error;
2365           return false;
2366         }
2367       this.map_list = {};
2368       this.initial_state = null;
2369       this.state_list = {};
2370       for (node = node.firstElement (); node; node = node.nextElement ())
2371         {
2372           var name = node.nodeName;
2373           var parser = parsers[name];
2374           if (parser)
2375             parser.call (this, node);
2376         }
2377       this.load_status = MIM.LoadStatus.Loaded;
2378       return true;
2379     }
2380   }
2381
2382   MIM.IM.prototype = proto;
2383
2384   MIM.IC = function (im, target)
2385   {
2386     if (im.load_status == MIM.LoadStatus.NotLoaded)
2387       im.Load ();
2388     if (im.load_status != MIM.LoadStatus.Loaded)
2389       alert ('im:' + im.name + ' error:' + im.load_status);
2390     this.im = im;
2391     this.target = target;
2392     this.domain = new Xex.Domain ('context', im.domain, this);
2393     this.active = true;
2394     this.range = new Array ();
2395     this.range[0] = this.range[1] = 0;
2396     this.state = null;
2397     this.initial_state = this.im.initial_state;
2398     this.keys = new MIM.KeySeq ();
2399     this.marker_positions = new Array ();
2400     this.candidate_table = new MIM.CandidateTable ();
2401     this.reset ();
2402   }
2403
2404   MIM.CandidateTable = function ()
2405   {
2406     this.table = new Array ();
2407   }
2408
2409   MIM.CandidateTable.prototype.get = function (pos)
2410   {
2411     for (var i = 0; i < this.table.length; i++)
2412       {
2413         var elt = this.table[i];
2414         if (elt.from < pos && pos <= elt.to)
2415           return elt.val;
2416       }
2417   }
2418
2419   MIM.CandidateTable.prototype.put = function (from, to, candidates)
2420   {
2421     for (var i = 0; i < this.table.length; i++)
2422       {
2423         var elt = this.table[i];
2424         if (elt.from < to && elt.to > from)
2425           {
2426             elt.from = from;
2427             elt.to = to;
2428             elt.val = candidates;
2429             return;
2430           }
2431       }
2432     this.table.push ({ from: from, to: to, val: candidates });
2433   }
2434
2435   MIM.CandidateTable.prototype.adjust = function (from, to, inserted)
2436   {
2437     var diff = inserted - (to - from);
2438     if (diff == 0)
2439       return;
2440     for (var i = 0; i < this.table.length; i++)
2441       {
2442         var elt = this.table[i];
2443         if (elt.from >= to)
2444           {
2445             elt.from += diff;
2446             elt.to += diff;
2447           }
2448       }
2449   }
2450
2451   MIM.CandidateTable.prototype.clear = function ()
2452   {
2453     this.table.length = 0;
2454   }
2455
2456   function detach_candidates (ic)
2457   {
2458     ic.candidate_table.clear ();
2459     ic.candidates = null;
2460     ic.changed |= (MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos
2461                    | MIM.ChangedStatus.CandidateList
2462                    | MIM.ChangedStatus.CandidateIndex
2463                    | MIM.ChangedStatus.CandidateShow);
2464   }
2465
2466   function set_cursor (prefix, pos)
2467   {
2468     this.cursor_pos = pos;
2469     this.candidates = this.candidate_table.get (pos);
2470   }
2471
2472   function save_state ()
2473   {
2474     this.state_var_values = this.domain.SaveValues ();
2475     this.state_preedit = this.preedit;
2476     this.state_key_head = this.key_head;
2477     this.state_pos = this.cursor_pos;
2478   }
2479
2480   function restore_state ()
2481   {
2482     this.domain.RestoreValues (this.state_var_values);
2483     this.preedit = this.state_preedit;
2484     set_cursor.call (this, "restore", this.state_pos);
2485   }
2486
2487   function handle_key ()
2488   {
2489     var out = this.keymap.Lookup (this.keys, this.key_head);
2490     var sub = out.map;
2491
2492     Xex.Log ('handling ' + this.keys.val[this.key_head]
2493              + ' in ' + this.state.name + ':' + this.keymap.name, 0, true);
2494     this.key_head = out.index;
2495     if (sub != this.keymap)
2496       {
2497         Xex.Log (' with submap');
2498         restore_state.call (this);
2499         this.keymap = sub;
2500         if (this.keymap.map_actions)
2501           {
2502             Xex.Log ('taking map actions:');
2503             if (! this.take_actions (this.keymap.map_actions))
2504               return false;
2505           }
2506         else if (this.keymap.submaps)
2507           {
2508             Xex.Log ('no map actions');
2509             for (var i = this.state_key_head; i < this.key_head; i++)
2510               {
2511                 Xex.Log ('inserting key:' + this.keys.val[i].key);
2512                 this.ins (this.keys.val[i].key, null);
2513               }
2514           }
2515         if (! this.keymap.submaps)
2516           {
2517             Xex.Log ('terminal:');
2518             if (this.keymap.branch_actions != null)
2519               {
2520                 Xex.Log ('branch actions:');
2521                 if (! this.take_actions (this.keymap.branch_actions))
2522                   return false;
2523               }
2524             if (this.keymap != this.state.keymap)
2525               this.shift (this.state);
2526           }
2527       }
2528     else
2529       {
2530         Xex.Log (' without submap');
2531         var current_state = this.state;
2532         var map = this.keymap;
2533
2534         if (map.branch_actions)
2535           {
2536             Xex.Log ('branch actions');
2537             if (! this.take_actions (map.branch_actions))
2538               return false;
2539           }
2540
2541         if (map == this.keymap)
2542           {
2543             Xex.Log ('no state change');
2544             if (map == this.initial_state.keymap
2545                 && this.key_head < this.keys.val.length)
2546               {
2547                 Xex.Log ('unhandled');
2548                 return false;
2549               }
2550             if (this.keymap != current_state.keymap)
2551               this.shift (current_state);
2552             else if (this.keymap.actions == null)
2553               this.shift (this.initial_state);
2554           }
2555       }
2556     return true;
2557   }
2558
2559   proto = {
2560     reset: function ()
2561     {
2562       this.cursor_pos = 0;
2563       this.candidate_show = false;
2564       this.prev_state = null;
2565       this.title = this.initial_state.title;
2566       this.state_preedit = '';
2567       this.state_key_head = 0;
2568       this.state_var_values = {};
2569       this.state_pos = 0;
2570       this.key_head = 0;
2571       this.commit_key_head = 0;
2572       this.key_unhandled = false;
2573       this.unhandled_key = null;
2574       this.changed = MIM.ChangedStatus.None;
2575       this.error_message = '';
2576       this.title = this.initial_state.title;
2577       this.produced = '';
2578       this.preedit = '';
2579       this.preedit_saved = '';
2580       this.candidate_table.clear ();
2581       this.candidates = null;
2582       this.candidate_show = false;
2583       for (var elt in this.marker_positions)
2584         this.marker_positions[elt] = 0;
2585       this.shift (this.initial_state);
2586     },
2587
2588     catch_args: new Array (Xex.CatchTag._mimtag, null),
2589
2590     take_actions: function (actions)
2591     {
2592       var func_progn = this.domain.GetFunc ('progn');
2593       var func_catch = this.domain.GetFunc ('catch');
2594       this.catch_args[1] = new Xex.Funcall (func_progn, null, actions);
2595       var term = new Xex.Funcall (func_catch, null, this.catch_args);
2596       term = term.Eval (this.domain);
2597       return (! term.IsSymbol || term.val != '@mimtag');
2598     },
2599
2600     GetSurroundingChar: function (pos)
2601     {
2602       if (pos < 0)
2603         {
2604           pos += this.range[0];
2605           if (pos < 0)
2606             return 0;
2607         }
2608       else
2609         {
2610           pos += this.range[1];
2611           if (pos >= this.target.value.length)
2612             return 0;
2613         }
2614       return this.target.value.charCodeAt (pos);
2615     },
2616     
2617     DelSurroundText: function (pos)
2618     {
2619       var text;
2620       if (pos < 0)
2621         {
2622           pos += this.range[0];
2623           if (pos <= 0)
2624             {
2625               pos = 0; text = '';
2626             }
2627           else
2628             text = this.target.value.substring (0, pos);
2629           if (this.range[0] < this.target.value.length)
2630             text += this.target.value.substring (this.range[0]);
2631           this.target.value = text;
2632           this.range[1] -= this.range[0] - pos;
2633           this.range[0] = pos;
2634         }
2635       else
2636         {
2637           pos += this.range[1];
2638           text = this.target.value.substring (0, this.range[1]);
2639           if (pos >= this.target.value.length)
2640             pos = this.target.value.length;
2641           else
2642             text += this.target.value.substring (pos);
2643           this.target.value = text;
2644         }
2645     },
2646
2647     adjust_markers: function (from, to, inserted)
2648     {
2649       var diff = inserted - (to - from);
2650
2651       for (var name in this.marker_positions)
2652         {
2653           var pos = this.marker_positions[name];
2654           if (pos > from)
2655             {
2656               if (pos >= to)
2657                 this.marker_positions[name] += diff;
2658               else if (pos > from)
2659                 this.marker_positions[name] = from;
2660             }
2661         }
2662       if (this.cursor_pos >= to)
2663         set_cursor.call (this, 'adjust', this.cursor_pos + diff);
2664       else if (this.cursor_pos > from)
2665         set_cursor.call (this, 'adjust', from)
2666     },
2667
2668     preedit_replace: function (from, to, text, candidates)
2669     {
2670       this.preedit = (this.preedit.substring (0, from)
2671                       + text + this.preedit.substring (to));
2672       this.adjust_markers (from, to, text.length);
2673       this.candidate_table.adjust (from, to, text.length);
2674       if (candidates)
2675         this.candidate_table.put (from, from + text.length, candidates)
2676     },
2677
2678     ins: function (text, candidates)
2679     {
2680       this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates);
2681       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
2682     },
2683
2684     rep: function (old_text, new_text, candidates)
2685     {
2686       this.preedit_replace (this.cursor_pos - old_text.length,
2687                             this.cursor_pos, new_text, candidates);
2688       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
2689     },
2690
2691     del: function (pos)
2692     {
2693       var deleted = pos - this.cursor_pos;
2694       if (pos < this.cursor_pos)
2695         {
2696           if (pos < 0)
2697             {
2698               this.DelSurroundText (pos);
2699               deleted = - this.cursor_pos;
2700               pos = 0;
2701             }
2702           if (pos < this.cursor_pos)
2703             this.preedit_replace (pos, this.cursor_pos, '', null);
2704         }
2705       else
2706         {
2707           if (pos > this.preedit.length)
2708             {
2709               this.DelSurroundText (pos - this.preedit.length);
2710               deleted = this.preedit.length - this.cursor_pos;
2711               pos = this.preedit.length;
2712             }
2713           if (pos > this.cursor_pos)
2714             this.preedit_replace (this.cursor_pos, pos, '', null);
2715         }
2716       if (deleted != 0)
2717         this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
2718       return deleted;
2719     },
2720
2721     show: function ()
2722     {
2723       this.candidate_show = true;
2724       this.changed |= MIM.ChangedStatus.CandidateShow;
2725     },
2726
2727     hide: function ()
2728     {
2729       this.candidate_show = false;
2730       this.changed |= MIM.ChangedStatus.CandidateShow;
2731     },
2732
2733     move: function (pos)
2734     {
2735       if (pos < 0)
2736         pos = 0;
2737       else if (pos > this.preedit.length)
2738         pos = this.preedit.length;
2739       if (pos != this.cursor_pos)
2740         {
2741           set_cursor.call (this, 'move', pos);
2742           this.changed |= MIM.ChangedStatus.Preedit;
2743         }
2744     },
2745
2746     pushback: function (n)
2747     {
2748       if (n instanceof MIM.KeySeq)
2749         {
2750           if (this.key_head > 0)
2751             this.key_head--;
2752           if (this.key_head < this.keys.val.length)
2753             this.keys.val.splice (this.key_head,
2754                                   this.keys.val.length - this.key_head);
2755           for (var i = 0; i < n.val.length; i++)
2756             this.keys.val.push (n.val[i]);
2757           return;
2758         }
2759       if (n > 0)
2760         {
2761           this.key_head -= n;
2762           if (this.key_head < 0)
2763             this.key_head = 0;
2764         }
2765       else if (n == 0)
2766         this.key_head = 0;
2767       else
2768       {
2769         this.key_head = - n;
2770         if (this.key_head > this.keys.val.length)
2771           this.key_head = this.keys.val.length;
2772       }
2773     },
2774
2775     pop: function ()
2776     {
2777       if (this.key_head < this.keys.val.length)
2778         this.keys.val.splice (this.key_head, 1);
2779     },
2780
2781     commit: function ()
2782     {
2783       if (this.preedit.length > 0)
2784       {
2785         this.candidate_table.clear ();
2786         this.produced += this.preedit;
2787         this.preedit_replace.call (this, 0, this.preedit.length, '', null);
2788         this.preedit_saved = '';
2789         this.state_pos = 0;
2790         this.commit_key_head = this.key_head;
2791       }
2792     },
2793
2794     shift: function (state)
2795     {
2796       if (state == null)
2797         {
2798           if (this.prev_state == null)
2799             return;
2800           state = this.prev_state;
2801         }
2802
2803       if (state == this.initial_state)
2804         {
2805           if (this.state)
2806             {
2807               this.commit ();
2808               this.keys.val.splice (0, this.key_head);
2809               this.key_head = 0;
2810               this.prev_state = null;
2811             }
2812         }
2813       else
2814         {
2815           if (state != this.state)
2816             this.prev_state = this.state;
2817         }
2818       if (state != this.state && state.enter_actions)
2819         this.take_actions (state.enter_actions);
2820       if (! this.state || this.state.title != state.title)
2821         this.changed |= MIM.ChangedStatus.StateTitle;
2822       this.state = state;
2823       this.keymap = state.keymap;
2824       this.state_key_head = this.key_head;
2825       save_state.call (this);
2826     },
2827
2828     Filter: function (key)
2829     {
2830       if (! this.active)
2831         {
2832           this.key_unhandled = true;
2833           this.unhandled_key = key;
2834           return false;
2835         }
2836       if (key.key == '_reload')
2837         return true;
2838       this.changed = MIM.ChangedStatus.None;
2839       this.produced = '';
2840       this.key_unhandled = false;
2841       this.keys.val.push (key);
2842       var count = 0;
2843       while (this.key_head < this.keys.val.length)
2844         {
2845           if (! handle_key.call (this))
2846             {
2847               if (this.key_head < this.keys.val.length)
2848                 {
2849                   this.unhandled_key = this.keys.val[this.key_head];
2850                   this.keys.val.splice (this.key_head, this.key_head + 1);
2851                 }
2852               if (this.state_key_head > 0)
2853                 this.state_key_head--;
2854               if (this.commit_key_head > 0)
2855                 this.commit_key_head--;
2856               this.key_unhandled = true;
2857               break;
2858             }
2859           if (++count == 10)
2860             {
2861               this.reset ();
2862               this.key_unhandled = true;
2863               break;
2864             }
2865         }
2866       if (this.keymap == this.initial_state.keymap)
2867         this.commit ();
2868
2869       if (this.commit_key_head > 0)
2870         {
2871           this.keys.val.splice (0, this.commit_key_head);
2872           this.key_head -= this.commit_key_head;
2873           this.state_key_head -= this.commit_key_head;
2874           this.commit_key_head = 0;
2875         }
2876       if (this.key_unhandled)
2877         {
2878           this.keys.val.length = 0;
2879           this.key_head = this.state_key_head = this.commit_key_head = 0;
2880         }
2881       return (! this.key_unhandled
2882               && this.produced.length == 0);
2883     }
2884   }
2885
2886   MIM.IC.prototype = proto;
2887
2888   var node = Xex.Load (null, "imlist.xml");
2889   for (node = node.firstChild; node; node = node.nextSibling)
2890     if (node.nodeName == 'input-method')
2891       {
2892         var lang = null, name = null, extra_id = null, file = null;
2893
2894         for (var n = node.firstChild; n; n = n.nextSibling)
2895           {
2896             if (n.nodeName == 'language')
2897               lang = n.firstChild.nodeValue;
2898             else if (n.nodeName == 'name')
2899               name = n.firstChild.nodeValue;
2900             else if (n.nodeName == 'extra-id')
2901               extra_id = n.firstChild.nodeValue;
2902             else if (n.nodeName == 'filename')
2903               file = n.firstChild.nodeValue;
2904           }
2905         if (name && name != 'nil')
2906           {
2907             if (! MIM.imlist[lang])
2908               MIM.imlist[lang] = {};
2909             MIM.imlist[lang][name] = new MIM.IM (lang, name, extra_id, file);
2910           }
2911         else if (extra_id && extra_id != 'nil')
2912           {
2913             if (! MIM.imextra[lang])
2914               MIM.imextra[lang] = {};
2915             MIM.imextra[lang][extra_id] = new MIM.IM (lang, name, extra_id, file);
2916           }
2917       }
2918   if (MIM.imextra.t && MIM.imextra.t.global)
2919     MIM.im_global = MIM.imextra.t.global;
2920   else
2921     {
2922       MIM.im_global = new MIM.IM ('t', 'nil', 'global', null);
2923       MIM.im_global.load_status = MIM.LoadStatus.Error;
2924     }
2925   node = undefined;
2926 }) ();
2927
2928 (function () {
2929   var keys = new Array ();
2930   keys[0x09] = 'tab';
2931   keys[0x08] = 'backspace';
2932   keys[0x0D] = 'return';
2933   keys[0x1B] = 'escape';
2934   keys[0x20] = 'space';
2935   keys[0x21] = 'pageup';
2936   keys[0x22] = 'pagedown';
2937   keys[0x23] = 'end';
2938   keys[0x24] = 'home';
2939   keys[0x25] = 'left';
2940   keys[0x26] = 'up';
2941   keys[0x27] = 'right';
2942   keys[0x28] = 'down';
2943   keys[0x2D] = 'insert';
2944   keys[0x2E] = 'delete';
2945   for (var i = 1; i <= 12; i++)
2946     keys[111 + i] = "f" + i;
2947   keys[0x90] = "numlock";
2948   keys[0xF0] = "capslock";
2949
2950   var keyids = {};
2951   keyids['U+0008'] = 'backspace';
2952   keyids['U+0009'] = 'tab';
2953   keyids['U+0018'] = 'cancel';
2954   keyids['U+001B'] = 'escape';
2955   keyids['U+0020'] = 'space';
2956   keyids['U+007F'] = 'delete';
2957
2958   var modifiers = {}
2959   modifiers.Shift = 1;
2960   modifiers.Control = 1;
2961   modifiers.Alt = 1;
2962   modifiers.AltGraph = 1;
2963   modifiers.Meta = 1
2964
2965   MIM.decode_key_event = function (event)
2966   {
2967     var key = event.keyIdentifier;
2968
2969     if (key)                    // keydown event of Chrome
2970       {
2971         if (modifiers[key])
2972           return false;
2973         var mod = '';
2974         if (event.ctrlKey) mod += 'C-';
2975         if (event.metaKey) mod += 'M-';
2976         if (event.altKey) mod += 'A-';
2977         var keysym = keyids[key];
2978         if (keysym)
2979           key = keysym;
2980         else if (key.match(/^U\+([0-9A-Z]+)$/))
2981           {
2982             if (mod.length == 0)
2983               return false;
2984             key = String.fromCharCode (parseInt (RegExp.$1, 16));
2985           }
2986         else
2987           key = key.toLowerCase ();
2988         if (event.shiftKey) mod += 'S-';
2989         return new MIM.Key (mod + key);
2990       }
2991     else
2992       {
2993         key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
2994                : event.charCode ? event.charCode
2995                : false);
2996         if (! key)
2997           return false;
2998         if (event.type == 'keydown')
2999           {
3000             key = keys[key];
3001             if (! key)
3002               return false;
3003             if (event.shiftKey) key = "S-" + key ;
3004           }
3005         else
3006           key = String.fromCharCode (key);
3007       }
3008     if (event.altKey) key = "A-" + key ;
3009     if (event.ctrlKey) key = "C-" + key ;
3010     return new MIM.Key (key);
3011   }
3012 }) ();
3013
3014 MIM.add_event_listener
3015   = (window.addEventListener
3016      ? function (target, type, listener) {
3017        target.addEventListener (type, listener, false);
3018      }
3019      : window.attachEvent
3020      ? function (target, type, listener) {
3021        target.attachEvent ('on' + type,
3022                            function() {
3023                              listener.call (target, window.event);
3024                            });
3025      }
3026      : function (target, type, listener) {
3027        target['on' + type]
3028          = function (e) { listener.call (target, e || window.event); };
3029      });
3030
3031 MIM.debug_print = function (event, ic)
3032 {
3033   if (! MIM.debug)
3034     return;
3035   if (! MIM.debug_nodes)
3036     {
3037       MIM.debug_nodes = new Array ();
3038       MIM.debug_nodes['status0'] = document.getElementById ('status0');
3039       MIM.debug_nodes['status1'] = document.getElementById ('status1');
3040       MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
3041       MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
3042       MIM.debug_nodes['keymap0'] = document.getElementById ('keymap0');
3043       MIM.debug_nodes['keymap1'] = document.getElementById ('keymap1');
3044       MIM.debug_nodes['preedit0'] = document.getElementById ('preedit0');
3045       MIM.debug_nodes['preedit1'] = document.getElementById ('preedit1');
3046     }
3047   var target = event.target;
3048   var code = event.keyCode;
3049   var ch = event.type == 'keypress' ? event.charCode : 0;
3050   var key = MIM.decode_key_event (event);
3051   var index;
3052
3053   MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + ":" + key + '/' + event.keyIdentifier;
3054   index = (event.type == 'keydown' ? '0' : '1');
3055   if (ic)
3056     MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status;
3057   else
3058     MIM.debug_nodes['status' + index].innerHTML = 'no IM';
3059   MIM.debug_nodes['keymap' + index].innerHTML = ic.state.name;
3060   MIM.debug_nodes['preedit' + index].innerHTML = ic.preedit;
3061   if (index == 0)
3062     {
3063       MIM.debug_nodes.keypress.innerHTML = '';
3064       MIM.debug_nodes.status1.innerHTML = '';
3065       MIM.debug_nodes.keymap1.innerHTML = '';
3066       MIM.debug_nodes.preedit1.innerHTML = ''
3067     }
3068 };
3069
3070 MIM.get_range = function (target, ic)
3071 {
3072   var from, to;
3073   if (target.selectionStart != null) // for Mozilla
3074     {
3075       from = target.selectionStart;
3076       to = target.selectionEnd;
3077     }
3078   else                          // for IE
3079     {
3080       var r = document.selection.createRange ();
3081       var rr = r.duplicate ();
3082
3083       rr.moveToElementText (target);
3084       rr.setEndPoint ('EndToEnd', range);
3085       from = rr.text.length - r.text.length;
3086       to = rr.text.length;
3087     }
3088   if (ic.range[0] == from && ic.range[1] == to
3089       && (to == from || target.value.substring (from, to) == ic.preedit))
3090     return true;
3091   ic.range[0] = from;
3092   ic.range[1] = to;
3093   return false;
3094 }
3095
3096 MIM.set_caret = function (target, ic)
3097 {
3098   if (target.setSelectionRange) // Mozilla
3099     {
3100       var scrollTop = target.scrollTop;
3101       target.setSelectionRange (ic.range[0], ic.range[1]);
3102       target.scrollTop = scrollTop;
3103     }
3104   else                          // IE
3105     {
3106       var range = target.createTextRange ();
3107       range.moveStart ('character', ic.range[0]);
3108       range.moveEnd ('character', ic.range[1]);
3109       range.select ();
3110     }
3111 };
3112
3113 MIM.ignore_focus = false;
3114
3115 MIM.update = function (target, ic)
3116 {
3117   var text = target.value;
3118   target.value = (text.substring (0, ic.range[0])
3119                   + ic.produced
3120                   + ic.preedit
3121                   + text.substring (ic.range[1]));
3122   ic.range[0] += ic.produced.length;
3123   ic.range[1] = ic.range[0] + ic.preedit.length;
3124   MIM.set_caret (target, ic);
3125 };
3126
3127 MIM.focus_in = function (event)
3128 {
3129   var target = event.target;
3130   var ic = target.mim_ic;
3131   if (ic)
3132     {
3133       if (target.mim_ignore_focus_in)
3134         {
3135           Xex.Log ('ignore focus_in in ' + target.tagName);
3136           // Ignore this event which is caused by setSelectionRange ().
3137           target.mim_ignore_focus_in = false;
3138           event.preventDefault ();
3139         }
3140       else
3141         {
3142           Xex.Log ('focus_in in ' + target.tagName);
3143           try {
3144             ic.Filter (MIM.Key.FocusIn);
3145             MIM.update (target, ic);
3146             // Ignore further focus-in caused by setSelectionRange ().
3147             // target.mim_ignore_focus_in = true;
3148           }
3149           catch (e) { Xex.Log ('Error:' + e); throw (e); }
3150         }
3151     }
3152 }
3153
3154 MIM.cancel_ignore_focus = function ()
3155 {
3156   if (MIM.focus_ignore_target)
3157     {
3158       MIM.focus_ignore_target.mim_ignore_focus_in = false;
3159       MIM.focus_ignore_target.mim_ignore_focus_out = false;
3160     }
3161 }
3162
3163 MIM.focus_out = function (event)
3164 {
3165   var target = event.target;
3166   var ic = target.mim_ic;
3167   if (ic)
3168     {
3169       if (target.mim_ignore_focus_out)
3170         {
3171           Xex.Log ('ignore focus_out in ' + target.tagName);
3172           // Ignore this event which is caused by setSelectionRange ().
3173           target.mim_ignore_focus_out = false;
3174           event.preventDefault ();
3175         }
3176       else
3177         {
3178           Xex.Log ('focus_out in ' + target.tagName);
3179           if (! MIM.get_range (target, ic))
3180             ic.reset ();
3181           ic.Filter (MIM.Key.FocusOut);
3182           // Ignore further focus-out caused by setSelectionRange ().
3183           target.mim_ignore_focus_in = true;
3184           target.mim_ignore_focus_out = true;
3185           MIM.focus_ignore_target = target;
3186           MIM.update (target, ic);
3187           setTimeout (MIM.cancel_ignore_focus, 100);
3188         }
3189     }
3190 };
3191
3192 MIM.keydown = function (event)
3193 {
3194   var target = event.target;
3195   if (target.id == 'log')
3196     return;
3197   if (! (target.type == "text" || target.type == "textarea"))
3198     return;
3199   document.akey = event;
3200
3201   var ic = target.mim_ic;
3202   if (! ic || ic.im != MIM.current)
3203     {
3204       target.mim_ic = null;
3205       Xex.Log ('creating IC');
3206       ic = new MIM.IC (MIM.current, target);
3207       if (ic.im.load_status != MIM.LoadStatus.Loaded)
3208         return;
3209       target.mim_ic = ic;
3210       MIM.add_event_listener (target, 'focus', MIM.focus_in);
3211       MIM.add_event_listener (target, 'blur', MIM.focus_out);
3212       MIM.get_range (target, ic)
3213     }
3214   else
3215     {
3216       if (! MIM.get_range (target, ic))
3217         ic.reset ();
3218     }
3219   MIM.debug_print (event, ic);
3220   ic.key = MIM.decode_key_event (event);
3221   if (ic.key)
3222     {
3223       Xex.Log ("filtering " + ic.key);
3224       try {
3225         var result = ic.Filter (ic.key);
3226       } catch (e) {
3227         Xex.Log ('Error' + e);
3228         throw (e);
3229       }
3230       MIM.update (target, ic);
3231       if (! ic.key_unhandled)
3232         event.preventDefault ();
3233     }
3234 };
3235
3236 MIM.keypress = function (event)
3237 {
3238   var target = event.target;
3239   if (target.id == 'log')
3240     return;
3241   if (! (target.type == "text" || target.type == "textarea"))
3242     return;
3243
3244   var ic = target.mim_ic;
3245   var i;
3246
3247   try {
3248     if (ic.im.load_status != MIM.LoadStatus.Loaded)
3249       return;
3250     if (! ic.key)
3251       ic.key = MIM.decode_key_event (event);
3252     if (! ic.key)
3253       {
3254         ic.reset ();
3255         return;
3256       }
3257     
3258     Xex.Log ("filtering " + ic.key);
3259     try {
3260       var result = ic.Filter (ic.key);
3261     } catch (e) {
3262       Xex.Log ('Error;' + e);
3263       throw (e);
3264     }
3265     MIM.update (target, ic);
3266     if (! ic.key_unhandled)
3267       event.preventDefault ();
3268   } catch (e) {
3269     Xex.Log ("error:" + e);
3270     event.preventDefault ();
3271   } finally {
3272     MIM.debug_print (event, ic);
3273   }
3274   return;
3275 };
3276
3277 (function () {
3278   var lang_category = {
3279     European: {
3280       cs: { name: 'Czech' },
3281       da: { name: 'Danish' },
3282       el: { name: 'Greek' },
3283       en: { name: 'English' },
3284       eo: { name: 'Esperanto' },
3285       fr: { name: 'French' },
3286       grc: { name: 'ClassicGreek' },
3287       hr: { name: 'Croatian' },
3288       hy: { name: 'Armenian' },
3289       ka: { name: 'Georgian' },
3290       kk: { name: 'Kazakh' },
3291       ru: { name: 'Russian' },
3292       sk: { name: 'Slovak' },
3293       sr: { name: 'Serbian' },
3294       sv: { name: 'Swedish' },
3295       vi: { name: 'Vietnamese' },
3296       yi: { name: 'Yiddish' } },
3297     MiddleEast: {
3298       ar: { name: 'Arabic' },
3299       dv: { name: 'Divehi' },
3300       fa: { name: 'Persian' },
3301       he: { name: 'Hebrew' },
3302       kk: { name: 'Kazakh' },
3303       ps: { name: 'Pushto' },
3304       ug: { name: 'Uighur' },
3305       yi: { name: 'Yiddish' } },
3306     SouthAsia: {
3307       as: { name: 'Assamese' },
3308       bn: { name: 'Bengali' },
3309       bo: { name: 'Tibetan' },
3310       gu: { name: 'Gujarati' },
3311       hi: { name: 'Hindi' },
3312       kn: { name: 'Kannada' },
3313       ks: { name: 'Kashmiri' },
3314       ml: { name: 'Malayalam' },
3315       mr: { name: 'Marathi' },
3316       ne: { name: 'Nepali' },
3317       or: { name: 'Oriya' },
3318       pa: { name: 'Panjabi' },
3319       sa: { name: 'Sanskirit' },
3320       sd: { name: 'Sindhi' },
3321       si: { name: 'Sinhalese' },
3322       ta: { name: 'Tamil' },
3323       te: { name: 'Telugu' },
3324       ur: { name: 'Urdu' } },
3325     SouthEastAsia: {
3326       cmc: { name: 'Cham' },
3327       km: { name: 'Khmer'},
3328       lo: { name: 'Lao' },
3329       my: { name: 'Burmese' },
3330       tai: { name: 'Tai Viet' },
3331       th: { name: 'Thai' },
3332       vi: { name: 'Vietanamese' } },
3333     EastAsia: {
3334       ii: { name: 'Yii' },
3335       ja: { name: 'Japanese' },
3336       ko: { name: 'Korean' },
3337       zh: { name: 'Chinese' } },
3338     Other: {
3339       am: { name:  'Amharic' },
3340       ath: { name: 'Carrier' },
3341       bla: { name: 'Blackfoot' },
3342       cr: { name: 'Cree' },
3343       eo: { name: 'Esperanto' },
3344       iu: { name: 'Inuktitut' },
3345       nsk: { name: 'Naskapi' },
3346       oj: { name: 'Ojibwe' },
3347       t: { name: 'Generic' } }
3348   };
3349
3350   function categorize_im ()
3351   {
3352     var cat, lang, list, name;
3353     for (lang in MIM.imlist)
3354       {
3355         list = null;
3356         for (cat in lang_category)
3357           if (lang_category[cat][lang])
3358             {
3359               list = lang_category[cat][lang].list;
3360               if (! list)
3361                 list = lang_category[cat][lang].list = {};
3362               break;
3363             }
3364         if (list)
3365           for (name in MIM.imlist[lang])
3366             list[name] = MIM.imlist[lang][name];
3367         else
3368           for (name in MIM.imlist[lang])
3369             Xex.Log ('no category ' + lang + '-' + name);
3370       }
3371   }
3372
3373   var destroy_timer;
3374   var last_target;
3375
3376   function destroy ()
3377   {
3378     clearTimeout (destroy_timer);
3379     destroy_timer = null;
3380     var target = document.getElementById ('mim-menu');
3381     if (target)
3382       {
3383         for (; last_target && last_target.menu_level;
3384              last_target = last_target.parentLi)
3385           last_target.style.backgroundColor = 'white';
3386         var nodes = target.getElementsByTagName ('ul');
3387         for (var i = 0; i < nodes.length; i++)
3388           nodes[i].style.visibility = 'hidden';
3389         document.getElementsByTagName ('body')[0].removeChild (target);
3390       }
3391   }    
3392
3393   function destroy_menu () {
3394     if (! destroy_timer)
3395       destroy_timer = setTimeout (destroy, 1000);
3396   }
3397
3398   function show_submenu (event)
3399   {
3400     if (destroy_timer)
3401       {
3402         clearTimeout (destroy_timer);
3403         destroy_timer = null;
3404       }
3405     var target = event.target;
3406     if (! target.menu_level)
3407       return;
3408     if (last_target && target.parentLi != last_target)
3409       {
3410         last_target.style.backgroundColor = 'white';
3411         if (target.menu_level < last_target.menu_level)
3412           {
3413             last_target = last_target.parentLi;
3414             last_target.style.backgroundColor = 'white';
3415           }
3416         var uls = last_target.getElementsByTagName ('ul');
3417         for (var i = 0; i < uls.length; i++)
3418           uls[i].style.visibility = 'hidden';
3419       }
3420     last_target = target;
3421     target.style.backgroundColor = 'yellow';
3422     if (target.menu_level < 3)
3423       {
3424         target.lastChild.style.visibility = 'visible';
3425         target.lastChild.style.left = target.clientWidth + 'px';
3426       }
3427     event.preventDefault ();    
3428   }
3429
3430   function select_im (event)
3431   {
3432     var target = event.target;
3433     if (target.im)
3434       {
3435         MIM.current = target.im;
3436         destroy ();
3437       }
3438     event.preventDefault ();
3439   }
3440
3441   function create_ul (visibility)
3442   {
3443     var ul = document.createElement ('ul');
3444     ul.style.position = 'absolute';
3445     ul.style.margin = '0px';
3446     ul.style.padding = '0px';
3447     ul.style.border = '1px solid gray';
3448     ul.style.borderBottom = 'none';
3449     ul.style.top = '-1px';
3450     ul.style.backgroundColor = 'white';
3451     ul.style.visibility = visibility;
3452     return ul;
3453   }
3454
3455   function create_li (level, text)
3456   {
3457     var li = document.createElement ('li');
3458     li.style.position = 'relative';
3459     li.style.margin = '0px';
3460     li.style.padding = '1px';
3461     li.style.borderBottom = '1px solid gray';
3462     li.style.top = '0px';
3463     li.style.listStyle = 'none';
3464     li.menu_level = level;
3465     li.appendChild (document.createTextNode (text));
3466     return li;
3467   }
3468
3469   var menu;
3470
3471   function create_menu (event)
3472   {
3473     var target = event.target;
3474
3475     if (! ((target.type == "text" || target.type == "textarea")
3476            && event.which == 1 && event.ctrlKey))
3477       return;
3478     if (! menu)
3479       {
3480         categorize_im ();
3481         menu = create_ul ('visible');
3482         menu.style.fontFamily = 'sans-serif';
3483         menu.style.fontWeight = 'bold';
3484         menu.id = 'mim-menu';
3485         menu.onclick = select_im;
3486         menu.onmouseover = show_submenu;
3487         menu.onmouseout = destroy_menu;
3488         for (var catname in lang_category)
3489           {
3490             var cat = lang_category[catname];
3491             var li = create_li (1, catname);
3492             var sub = create_ul ('hidden');
3493             for (var langname in cat)
3494               {
3495                 var lang = cat[langname];
3496                 if (! lang.list)
3497                   continue;
3498                 var sub_li = create_li (2, lang.name);
3499                 sub_li.parentLi = li;
3500                 var subsub = create_ul ('hidden');
3501                 for (var name in lang.list)
3502                   {
3503                     var im = lang.list[name];
3504                     var subsub_li = create_li (3, im.name);
3505                     subsub_li.parentLi = sub_li;
3506                     subsub_li.im = im;
3507                     subsub.appendChild (subsub_li);
3508                   }
3509                 sub_li.appendChild (subsub);
3510                 sub.appendChild (sub_li);
3511               }
3512             li.appendChild (sub);
3513             menu.appendChild (li);
3514           }
3515         document.mimmenu = menu;
3516         lang_category = null;
3517       }
3518     menu.style.left = (event.clientX - 10) + "px";
3519     menu.style.top = (event.clientY - 10) + "px";
3520     document.getElementsByTagName ('body')[0].appendChild (menu);
3521   };
3522
3523   MIM.init = function ()
3524   {
3525     MIM.add_event_listener (window, 'keydown', MIM.keydown);
3526     MIM.add_event_listener (window, 'keypress', MIM.keypress);
3527     MIM.add_event_listener (window, 'mousedown', create_menu);
3528     if (window.location == 'http://localhost/mim/index.html')
3529       MIM.server = 'http://localhost/mim';
3530     MIM.current = MIM.imlist['zh']['tonepy'];
3531   };
3532 }) ();
3533
3534 MIM.test = function ()
3535 {
3536   var im = MIM.imlist['t']['latn-post'];
3537   var ic = new MIM.IC (im, null);
3538
3539   ic.Filter (new MIM.Key ('a'));
3540   ic.Filter (new MIM.Key ("'"));
3541
3542   if (true)
3543     document.getElementById ('text').value = ic.produced + ic.preedit;
3544   else {
3545     try {
3546       document.getElementById ('text').value
3547         = Xex.Term.Parse (domain, body).Eval (domain).toString ();
3548     } catch (e) {
3549       if (e instanceof Xex.ErrTerm)
3550         alert (e);
3551       throw e;
3552     }
3553   }
3554 }
3555
3556
3557 MIM.init_debug = function ()
3558 {
3559   MIM.debug = true;
3560   Xex.LogNode = document.getElementById ('log');
3561   Xex.Log (null);
3562   MIM.init ();
3563 };