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