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