*** 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 Fset (domain, vari, args)
833   {
834     if (! vari)
835       throw new Xex.ErrTerm (Xex.Error.NoVariableName,
836                              'No variable name to set');
837     vari.SetValue (args[0]);
838     return args[0];
839   }
840
841   function Fadd (domain, vari, args)
842   {
843     var n = vari ? vari.val.val : 0;
844     var len = args.length;
845
846     for (var i = 0; i < len; i++)
847       n += args[i].val;
848     return maybe_set_intvar (vari, n);
849   }
850
851   function Fand (domain, vari, args)
852   {
853     var len = args.length;
854     for (var i = 0; i < len; i++)
855     {
856       var result = args[i].Eval (domain);
857       if (domain.Thrown ())
858         return result;
859       if (! result.IsTrue)
860         return Xex.Zero;
861     }
862     return Xex.One;
863   }
864
865   function For (domain, vari, args)
866   {
867     var len = args.length;
868     for (var i = 0; i < len; i++)
869     {
870       var result = args[i].Eval (domain);
871       if (domain.Thrown ())
872         return result;
873       if (result.IsTrue ())
874         return Xex.One;
875     }
876     return Xex.Zero;
877   }
878
879   function Feq (domain, vari, args)
880   {
881     for (var i = 1; i < args.length; i++)
882       if (! args[i - 1].Equals (args[i]))
883         return Xex.Zero;
884     return Xex.One;
885   }
886
887   function Fprogn (domain, vari, args)
888   {
889     var result = Xex.One;
890     var len = args.length;
891
892     for (var i = 0; i < len; i++)
893       {
894         result = args[i].Eval (domain);
895         if (domain.Thrown ())
896           return result;
897       }
898     return result;
899   }
900
901   function Fif (domain, vari, args)
902   {
903     var result = args[0].Eval (domain);
904
905     if (domain.Thrown ())
906       return result;
907     if (result.IsTrue ())
908       return args[1].Eval (domain);
909     if (args.length == 2)
910       return Zero;
911     return args[2].Eval (domain);
912   }
913
914   function Fcond (domain, vari, args)
915   {
916     for (var i = 0; i < args.length; i++)
917       {
918         var list = args[i].val;
919         var result = list.val[0].Eval (doamin);
920         if (result.isTrue ())
921           {
922             for (var j = 1; j < list.val.length; j++)
923               {
924                 domain.depth++;
925                 result = list.val[j].Eval (domain);
926                 domain.depth--;
927                 if (domain.Thrown ())
928                   return result;
929                 }
930             return result;
931           }
932       }
933     return Xex.Zero;
934   }
935
936   function eval_terms (domain, terms, idx)
937   {
938     var result = Xex.Zero;
939     domain.caught = false;
940     for (var i = idx; i < terms.length; i++)
941       {
942         result = terms[i].Eval (domain);
943         if (domain.Thrown ())
944           return result;
945       }
946     return result;
947   }
948
949   function Fcatch (domain, vari, args)
950   {
951     var caught = false;
952     var result;
953
954     if (args[0].IsError)
955       {
956         try {
957           result = eval_terms (domain, args, 1);
958         } catch (e) {
959           if (e instanceof Xex.ErrTerm)
960             {
961               if (! args[0].Matches (e))
962                 throw e;
963               if (vari)
964                 vari.SetValue (e);
965               return Xex.One;
966             }
967         }
968       }
969     else if (args[0].IsSymbol)
970       {
971         try {
972           domain.Catch (args[0].val);
973           result = eval_terms (domain, args, 1);
974           if (domain.caught)
975             {
976               if (vari != null)
977                 vari.SetValue (result);
978               return Xex.One;
979             }
980           return Xex.Zero;
981         } finally {
982           domain.Uncatch ();
983         }
984       }
985     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
986                            "Not a symbol nor an error: " + args[0]);
987   }
988
989   function Fthrow (domain, vari, args)
990   {
991     if (args[0].IsSymbl)
992       {
993         domain.ThrowSymbol (args[0]);
994         return (args[args.length - 1]);
995       }
996     if (args[0].IsError)
997       {
998         throw args[0];
999       }
1000     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1001                            "Not a symbol nor an error:" + args[0]);
1002   }
1003
1004   Xex.BasicDomain = basic;
1005
1006   basic.DefSubr (Fset, "set", true, 1, 1);
1007   basic.DefSubr (Fadd, "add", true, 1, -1);
1008   basic.DefSubr (Fthrow, "throw", false, 1, 2);
1009
1010   basic.DefSpecial (Fand, "and", false, 1, -1);
1011   basic.DefSpecial (For, "or", false, 1, -1);
1012   basic.DefAlias ("=", "set");
1013   basic.DefSpecial (Fprogn, "progn", false, 1, -1);
1014   basic.DefSpecial (Fif, "if", false, 2, 3);
1015   basic.DefSpecial (Fcatch, "catch", true, 2, -1);
1016
1017   basic.DefType (Xex.Funcall.prototype);
1018   basic.DefType (Xex.Varref.prototype);
1019   basic.DefType (Xex.ErrTerm.prototype);
1020   basic.DefType (Xex.IntTerm.prototype);
1021   basic.DefType (Xex.StrTerm.prototype);
1022   basic.DefType (Xex.SymTerm.prototype);
1023   basic.DefType (Xex.LstTerm.prototype);
1024
1025 }) ();
1026
1027 Xex.Zero = new Xex.IntTerm (0);
1028 Xex.One = new Xex.IntTerm (1);
1029
1030 Xex.Load = function (server, file)
1031 {
1032   var obj = new XMLHttpRequest ();
1033   var url = server ? server + '/' + file : file;
1034   obj.open ('GET', url, false);
1035   obj.overrideMimeType ('text/xml');
1036   obj.send ('');
1037   alert (file);
1038   return obj.responseXML.firstChild;
1039 }
1040
1041 var MIM = {
1042   // URL of the input method server.
1043   server: "http://www.m17n.org/common/mim-js",
1044   // Boolean flag to tell if MIM is active or not.
1045   enabled: true,
1046   // Boolean flag to tell if MIM is running in debug mode or not.
1047   debug: false,
1048   // List of main input methods.
1049   imlist: {},
1050   // List of extra input methods;
1051   imextra: {},
1052   // Global input method data
1053   im_global: null,
1054   // Currently selected input method.
1055   current: false,
1056
1057   // enum
1058   LoadStatus: { NotLoaded:0, Loading:1, Loaded:2, Error:-1 },
1059   ChangedStatus: {
1060     None:       0x00,
1061     StateTitle: 0x01,
1062     PreeditText:0x02,
1063     CursorPos:  0x04,
1064     CandidateList:0x08,
1065     CandidateIndex:0x10,
1066     CandidateShow:0x20,
1067     Preedit:    0x06,           // PreeditText | CursorPos
1068     Candidate:  0x38 // CandidateList | CandidateIndex | CandidateShow
1069   },
1070   KeyModifier: {
1071     SL: 0x00400000,
1072     SR: 0x00800000,
1073     S:  0x00C00000,
1074     CL: 0x01000000,
1075     CR: 0x02000000,
1076     C:  0x03000000,
1077     AL: 0x04000000,
1078     AR: 0x08000000,
1079     A:  0x0C000000,
1080     ML: 0x04000000,
1081     MR: 0x08000000,
1082     M:  0x0C000000,
1083     G:  0x10000000,
1084     s:  0x20000000,
1085     H:  0x40000000,
1086     High:       0x70000000,
1087     All:        0x7FC00000
1088   },
1089   Error: {
1090     ParseError: "parse-error"
1091   }
1092 };
1093   
1094 (function () {
1095   var keysyms = new Array ();
1096   keysyms["bs"] = "backspace";
1097   keysyms["lf"] = "linefeed";
1098   keysyms["cr"] = keysyms["enter"] = "return";
1099   keysyms["esc"] = "escape";
1100   keysyms["spc"] = "space";
1101   keysyms["del"] = "delete";
1102
1103   function decode_keysym (str) {
1104     var parts = str.split ("-");
1105     var len = parts.length, i;
1106     var has_modifier = len > 1;
1107
1108     for (i = 0; i < len - 1; i++)
1109       if (! MIM.KeyModifier.hasOwnProperty (parts[i]))
1110         return false;
1111     var key = parts[len - 1];
1112     if (key.length > 1)
1113       {
1114         key = keysyms[key.toLowerCase ()];
1115         if (key)
1116           {
1117             if (len > 1)
1118               {
1119                 str = parts[0];
1120                 for (i = 1; i < len - 1; i++)
1121                   str += '-' + parts[i];
1122                 str += '-' + key;
1123               }
1124             else
1125               str = key;
1126           }
1127       }
1128     if (has_modifier)
1129       {
1130         parts = new Array ();
1131         parts.push (str);
1132         return parts;
1133       }
1134     return str;
1135   }
1136
1137   MIM.Key = function (val)
1138   {
1139     this.key;
1140     this.has_modifier = false;
1141     if (typeof val == 'string' || val instanceof String)
1142       {
1143         this.key = decode_keysym (val);
1144         if (! this.key)
1145           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
1146         if (this.key instanceof Array)
1147           {
1148             this.key = this.key[0];
1149             this.has_modifier = true;
1150           }
1151       }
1152     else if (typeof val == 'number' || val instanceof Number)
1153       this.key = String.fromCharCode (val);
1154     else
1155       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
1156   }
1157
1158   MIM.Key.prototype.toString = function () { return this.key; };
1159 }) ();
1160
1161 (function () {
1162   MIM.KeySeq = function (seq)
1163   {
1164     this.val = new Array ();
1165     this.has_modifier = false;
1166
1167     if (seq)
1168       {
1169         if (seq.IsList)
1170           {
1171             var len = seq.val.length;
1172             for (var i = 0; i < len; i++)
1173               {
1174                 var v = seq.val[i];
1175                 if (v.type != 'string' && v.type != 'integer'
1176                     && v.type != 'symbol')
1177                   throw new Xex.ErrTerm (MIM.Error.ParseError,
1178                                          "Invalid key: " + v);
1179                 var key = new MIM.Key (v.val);
1180                 this.val.push (key);
1181                 if (key.has_modifier)
1182                   this.has_modifier = true;
1183               }
1184           }
1185         else if (seq.IsStr)
1186           {
1187             var len = seq.val.length;
1188             for (var i = 0; i < len; i++)
1189               this.val.push (new MIM.Key (seq.val.charCodeAt (i)));
1190           }
1191         else
1192           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + seq);
1193       }
1194   }
1195
1196   var proto = new Xex.Term ('keyseq');
1197   proto.Clone = function () { return this; }
1198   proto.Parser = function (domain, node)
1199   {
1200     var seq = new Array ();
1201     for (node = node.firstChild; node; node = node.nextSibling)
1202       if (node.nodeType == 1)
1203         {
1204           var term = Xex.Term.Parse (domain, node);
1205           return new MIM.KeySeq (term);
1206         }
1207     throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid keyseq");
1208   }
1209   proto.toString = function ()
1210   {
1211     var len = this.val.length;
1212     if (len == 0)
1213       return '<keyseq/>';
1214     var first = true;
1215     var str = '<keyseq>';
1216     for (var i = 0; i < len; i++)
1217       {
1218         if (first)
1219           first = false;
1220         else if (this.has_modifier)
1221           str += ' ';
1222         str += this.val[i].toString ();
1223       }
1224     return str + '</keyseq>';
1225   }
1226
1227   MIM.KeySeq.prototype = proto;
1228 }) ();
1229
1230 (function () {
1231   MIM.Marker = function () { }
1232   MIM.Marker.prototype = new Xex.Term ('marker');
1233   MIM.Marker.prototype.CharAt = function (ic)
1234   {
1235     var p = this.Position (ic);
1236     if (p < 0)
1237       return ic.GetSurroundingChar (p);
1238     else if (pos >= ic.preedit.length)
1239       return ic.GetSurroundingChar (p - ic.preedit.length);
1240     return ic.preedit.charCodeAt (p);
1241   }
1242
1243   MIM.NamedMarker = function (name) { this.val = name; }
1244   MIM.NamedMarker.prototype = new MIM.Marker ();
1245   MIM.NamedMarker.prototype.Position = function (ic)
1246   {
1247     var p = ic.marker_positions[this.val];
1248     return (p == undefined ? 0 : p);
1249   }
1250   MIM.NamedMarker.prototype.Mark = function (ic)
1251   {
1252     ic.marker_positions[this.val] = ic.cursor_pos;
1253   }
1254
1255   MIM.PredefinedMarker = function (name) { this.val = name; }
1256   MIM.PredefinedMarker.prototype = new MIM.Marker ();
1257   MIM.PredefinedMarker.prototype.Position = function (ic)
1258   {
1259     if (typeof this.pos == 'number')
1260       return this.pos;
1261     return this.pos (ic);
1262   }
1263
1264   var predefined = { }
1265
1266   function def_predefined (name, position)
1267   {
1268     predefined[name] = new MIM.PredefinedMarker (name);
1269     predefined[name].pos = position;
1270   }
1271
1272   def_predefined ('@<', 0);
1273   def_predefined ('@>', function (ic) { return ic.preedit.length; });
1274   def_predefined ('@-', function (ic) { return ic.cursor_pos - 1; });
1275   def_predefined ('@+', function (ic) { return ic.cursor_pos + 1; });
1276   def_predefined ('@[', function (ic) {
1277     if (ic.cursor_pos > 0)
1278       {
1279         var pos = ic.cursor_pos;
1280         return ic.preedit.FindProp ('candidates', pos - 1).from;
1281       }
1282     return 0;
1283   });
1284   def_predefined ('@]', function (ic) {
1285     if (ic.cursor_pos < ic.preedit.length - 1)
1286       {
1287         var pos = ic.cursor_pos;
1288         return ic.preedit.FindProp ('candidates', pos).to;
1289       }
1290     return ic.preedit.length;
1291   });
1292   for (var i = 0; i < 10; i++)
1293     def_predefined ("@" + i, i);
1294   predefined['@first'] = predefined['@<'];
1295   predefined['@last'] = predefined['@>'];
1296   predefined['@previous'] = predefined['@-'];
1297   predefined['@next'] = predefined['@+'];
1298   predefined['@previous-candidate-change'] = predefined['@['];
1299   predefined['@next-candidate-change'] = predefined['@]'];
1300
1301   MIM.SurroundMarker = function (name)
1302   {
1303     this.val = name;
1304     this.distance = parseInt (name.slice (2));
1305     if (isNaN (this.distance))
1306       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name);
1307   }
1308   MIM.SurroundMarker.prototype = new MIM.Marker ();
1309   MIM.SurroundMarker.prototype.Position = function (ic)
1310   {
1311     return ic.cursor_pos + this.distance;
1312   }
1313
1314   MIM.Marker.prototype.Parser = function (domain, node)
1315   {
1316     var name = node.firstChild.nodeValue;
1317     if (name.charAt (0) == '@')
1318       {
1319         var n = predefined[name];
1320         if (n)
1321           return n;
1322         if (name.charAt (1) == '-')
1323           return new MIM.SurroundMarker (name);
1324         throw new Xex.ErrTerm (MIM.Error.ParseError,
1325                                "Invalid marker: " + name);
1326       }
1327     return new MIM.NamedMarker (name);
1328   }
1329 }) ();
1330
1331 MIM.Selector = function (name)
1332 {
1333   this.val = name;
1334 }
1335 MIM.Selector.prototype = new Xex.Term ('selector');
1336
1337 (function () {
1338   var selectors = {};
1339   selectors["@<"] = selectors["@first"] = new MIM.Selector ('@<');
1340   selectors["@="] = selectors["@current"] = new MIM.Selector ('@=');
1341   selectors["@>"] = selectors["@last"] = new MIM.Selector ('@>');
1342   selectors["@-"] = selectors["@previous"] = new MIM.Selector ('@-');
1343   selectors["@+"] = selectors["@next"] = new MIM.Selector ('@+');
1344   selectors["@["] = selectors["@previous-candidate-change"]
1345     = new MIM.Selector ('@[');
1346   selectors["@]"] = selectors["@next-candidate-change"]
1347     = new MIM.Selector ('@]');
1348
1349   MIM.Selector.prototype.Parser = function (domain, node)
1350   {
1351     var name = node.firstChild.nodeValue;
1352     var s = selectors[name];
1353     if (! s)
1354       throw new Xex.ErrTerm (MIM.Error.ParseError,
1355                              "Invalid selector: " + name);
1356     return s;
1357   }
1358 }) ();
1359
1360 MIM.Rule = function (keyseq, actions)
1361 {
1362   this.keyseq = keyseq;
1363   this.actions = actions;
1364 }
1365 MIM.Rule.prototype = new Xex.Term ('rule');
1366 MIM.Rule.prototype.Parser = function (domain, node)
1367 {
1368   var n;
1369   for (n = node.firstChild; n && n.nodeType != 1; n = n.nextSibling);
1370   if (! n)
1371     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1372   var keyseq = Xex.Term.Parse (domain, n);
1373   if (keyseq.type != 'keyseq')
1374     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1375   var actions = Xex.Term.Parse (domain, n.nextElement (), null);
1376   return new MIM.Rule (keyseq, actions);
1377 }
1378 MIM.Rule.prototype.toString = function ()
1379 {
1380   return '<rule/>';
1381 }
1382
1383 MIM.Map = function (name)
1384 {
1385   this.name = name;
1386   this.rules = new Array ();
1387 };
1388
1389 (function () {
1390   var proto = new Xex.Term ('map');
1391
1392   proto.Parser = function (domain, node)
1393   {
1394     var name = node.attributes['mname'].nodeValue;
1395     if (! name)
1396       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1397     var map = new MIM.Map (name);
1398     for (var n = node.firstChild; n; n = n.nextSibling)
1399       if (n.nodeType == 1)
1400         map.rules.push (Xex.Term.Parse (domain, n));
1401     return map;
1402   }
1403
1404   proto.toString = function ()
1405   {
1406     var str = '<map mname="' + this.name + '">';
1407     var len = this.rules.length;
1408     for (i = 0; i < len; i++)
1409       str += this.rules[i];
1410     return str + '</map>';
1411   }
1412
1413   MIM.Map.prototype = proto;
1414 }) ();
1415
1416 Xex.CatchTag._mimtag = new Xex.SymTerm ('@mimtag');
1417
1418 MIM.Action = function (domain, terms)
1419 {
1420   var args = new Array ();
1421   args.push (Xex.CatchTag_.mimtag);
1422   for (var i = 0; i < terms.length; i++)
1423     args.push (terms[i]);
1424   this.action = Xex.Funcall.prototype.New (domain, 'catch', null, args);
1425 }
1426
1427 MIM.Action.prototype.Run = function (domain)
1428 {
1429   var result = this.action.Eval (domain);
1430   if (result.type == 'error')
1431     {
1432       domain.context.Error = result.toString ();
1433       return false;
1434     }
1435   return (result != Xex.CatchTag._mimtag);
1436 }
1437
1438 MIM.Keymap = function ()
1439 {
1440   this.name = 'TOP';
1441   this.submaps = null;
1442   this.actions = null;
1443 };
1444
1445 (function () {
1446   var proto = {};
1447
1448   function add_rule (keymap, rule)
1449   {
1450     var keyseq = rule.keyseq;
1451     var len = keyseq.val.length;
1452     var name = '';
1453
1454     for (var i = 0; i < len; i++)
1455       {
1456         var key = keyseq.val[i];
1457         var sub = false;
1458
1459         name += key.key;
1460         if (! keymap.submaps)
1461           keymap.submaps = {};
1462         else
1463           sub = keymap.submaps[key.key];
1464         if (! sub)
1465           keymap.submaps[key.key] = sub = new MIM.Keymap ();
1466         keymap = sub;
1467         keymap.name = name;
1468       }
1469     keymap.actions = rule.actions;
1470   }
1471
1472   proto.Add = function (map)
1473   {
1474     var rules = map.rules;
1475     var len = rules.length;
1476
1477     for (var i = 0; i < len; i++)
1478       add_rule (this, rules[i]);
1479   }
1480   proto.Lookup = function (keys, index)
1481   {
1482     var sub;
1483
1484     if (index < keys.val.length && this.submaps
1485         && (sub = this.submaps[keys.val[index].key]))
1486       {
1487         index++;
1488         return sub.Lookup (keys, index);
1489       }
1490     return { map: this, index: index };
1491   }
1492
1493   MIM.Keymap.prototype = proto;
1494 }) ();
1495
1496 MIM.State = function (name)
1497 {
1498   this.name = name;
1499   this.keymap = new MIM.Keymap ();
1500 };
1501
1502 (function () {
1503   var proto = new Xex.Term ('state');
1504
1505   proto.Parser = function (domain, node)
1506   {
1507     var map_list = domain.map_list;
1508     var name = node.attributes['sname'].nodeValue;
1509     if (! name)
1510       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1511     var state = new MIM.State (name);
1512     for (node = node.firstElement (); node; node = node.nextElement ())
1513       {
1514         if (node.nodeName == 'title')
1515           state.title = node.firstChild.nodeValue;
1516         else
1517           {
1518             var n = node.firstElement ();
1519             if (node.nodeName == 'branch')
1520             {
1521               state.keymap.Add (map_list[node.attributes['mname'].nodeValue]);
1522               state.keymap.actions = Xex.Term.Parse (domain, n, null);
1523             }
1524             else if (node.nodeName == 'state-hook')
1525               state.enter_actions = Xex.Term.Parse (domain, n, null);
1526             else if (node.nodeName == 'catch-all-branch')
1527               state.fallback_actions = Xex.Term.Parse (domain, n, null);
1528           }
1529       }
1530     return state;
1531   }
1532
1533   proto.toString = function ()
1534   {
1535     return '<state sname="' + this.name + '">' + this.keymap + '</state>';
1536   }
1537
1538   MIM.State.prototype = proto;
1539 }) ();
1540
1541 MIM.im_domain = new Xex.Domain ('input-method', null, null);
1542 MIM.im_domain.DefType (MIM.KeySeq.prototype);
1543 MIM.im_domain.DefType (MIM.Marker.prototype);
1544 MIM.im_domain.DefType (MIM.Selector.prototype);
1545 MIM.im_domain.DefType (MIM.Rule.prototype);
1546 MIM.im_domain.DefType (MIM.Map.prototype);
1547 MIM.im_domain.DefType (MIM.State.prototype);
1548
1549 (function () {
1550   var im_domain = MIM.im_domain;
1551
1552   function Finsert (domain, vari, args)
1553   {
1554     var text;
1555     if (args[0].type == 'integer')
1556       text = String.fromCharCode (args[0].val);
1557     else
1558       text = args[0].val;
1559     domain.context.insert (text, null);
1560   }
1561
1562   function Finsert_candidates (domain, vari, args)
1563   {
1564     var ic = domain.context;
1565     var candidates = new Candidates (args, column);
1566     ic.insert (candidates.Current (), candidates);
1567     return args[0];
1568   }
1569
1570   function Fmove (domain, vari, args)
1571   {
1572     var ic = domain.context;
1573     var pos = args[0].IsInt ? args[0].val : args[0].Position (ic);
1574     ic.move (pos);
1575     return args[0];
1576   }
1577
1578   function Fmark (domain, vari, args)
1579   {
1580     args[0].Mark (domain.context);
1581     return args[0];
1582   }
1583
1584   im_domain.DefSubr (Finsert, "insert", false, 1, 1);
1585   im_domain.DefSubr (Finsert_candidates, "insert-candidates", false, 1, 1);
1586   im_domain.DefSubr (Fmove, "move", false, 1, 1);
1587   im_domain.DefSubr (Fmark, "mark", false, 1, 1);
1588 }) ();
1589
1590
1591 (function () {
1592   function get_global_var (vname)
1593   {
1594     if (MIM.im_global.load_status == MIM.LoadStatus.NotLoaded)
1595       MIM.im_global.Load ()
1596     return MIM.im_global.domain.variables[vname];
1597   }
1598
1599   var parsers = { };
1600
1601   parsers['description'] = function (node)
1602   {
1603     this.description = node.firstChild.nodeValue;
1604   }
1605   parsers['variable-list'] = function (node)
1606   {
1607     for (node = node.firstElement (); node; node = node.nextElement ())
1608       {
1609         var vname = node.attributes['vname'].nodeValue;
1610         if (this != MIM.im_global)
1611           {
1612             var vari = get_global_var (vname);
1613             if (vari != null)
1614               this.domain.Defvar (vname);
1615           }
1616         Xex.Term.Parse (this.domain, node);
1617       }
1618   }
1619   parsers['command-list'] = function (node)
1620   {
1621   }
1622   parsers['macro-list'] = function (node)
1623   {
1624     for (node = node.firstElement (); node; node = node.nextElement ())
1625       {
1626         if (node.nodeName == 'xi:include')
1627           {
1628             var im = include (node);
1629             if (! im)
1630               continue;
1631             for (var macro in im.domain.functions)
1632               im.domain.CopyFunc (this.domain, macro);
1633           }
1634         else
1635           Xex.Term.Parse (node);
1636       }
1637   }
1638   parsers['title'] = function (node)
1639   {
1640     this.title = node.firstChild.nodeValue;
1641   }
1642   parsers['map-list'] = function (node)
1643   {
1644     for (node = node.firstChild; node; node = node.nextSibling)
1645       {
1646         if (node.nodeType != 1 || node.nodeName != 'map')
1647           continue;
1648         var map = Xex.Term.Parse (this.domain, node);
1649         this.map_list[map.name] = map;
1650       }
1651   }
1652   parsers['state-list'] = function (node)
1653   {
1654     this.domain.map_list = this.map_list;
1655     for (node = node.firstChild; node; node = node.nextSibling)
1656       {
1657         if (node.nodeType != 1 || node.nodeName != 'state')
1658           continue;
1659         var state = Xex.Term.Parse (this.domain, node);
1660         if (! state.title)
1661           state.title = this.title;
1662         if (! this.initial_state)
1663           this.initial_state = state;
1664         this.state_list[state.name] = state;
1665       }
1666     delete this.domain.map_list;
1667   }
1668
1669   MIM.IM = function (lang, name, extra_id, file)
1670   {
1671     this.lang = lang;
1672     this.name = name;
1673     this.extra_id = extra_id;
1674     this.file = file;
1675     this.load_status = MIM.LoadStatus.NotLoaded;
1676     this.domain = new Xex.Domain (this.lang + '-'
1677                                   + (this.name != 'nil'
1678                                      ? this.name : this.extra_id),
1679                                   MIM.im_domain, null);
1680   }
1681
1682   var proto = {
1683     Load: function ()
1684     {
1685       var node = Xex.Load (null, this.file);
1686       if (! node)
1687         {
1688           this.load_status = MIM.LoadStatus.Error;
1689           return false;
1690         }
1691       this.map_list = {};
1692       this.initial_state = null;
1693       this.state_list = {};
1694       for (node = node.firstElement (); node; node = node.nextElement ())
1695         {
1696           var name = node.nodeName;
1697           var parser = parsers[name];
1698           if (parser)
1699             parser.call (this, node);
1700         }
1701       this.load_status = MIM.LoadStatus.Loaded;
1702       return true;
1703     }
1704   }
1705
1706   MIM.IM.prototype = proto;
1707
1708   MIM.IC = function (im)
1709   {
1710     if (im.load_status == MIM.LoadStatus.NotLoaded)
1711       im.Load ();
1712     if (im.load_status != MIM.LoadStatus.Loaded)
1713       alert ('im:' + im.name + ' error:' + im.load_status);
1714     this.im = im;
1715     this.domain = new Xex.Domain ('context', im.domain, this);
1716     this.active = true;
1717     this.reset ();
1718     this.spot = 0;
1719   }
1720
1721   MIM.CandidateTable = function ()
1722   {
1723     this.table = new Array ();
1724   }
1725
1726   MIM.CandidateTable.prototype.get = function (from)
1727   {
1728     for (var i = 0; i < this.table.length; i++)
1729       {
1730         var elt = this.table[i];
1731         if (elt.from <= from && elt.to > from)
1732           return elt.val;
1733       }
1734   }
1735
1736   MIM.CandidateTable.prototype.put = function (from, to, candidates)
1737   {
1738     for (var i = 0; i < this.table.length; i++)
1739       {
1740         var elt = this.table[i];
1741         if (elt.from >= from && elt.from < to
1742             || elt.to >= from && elt.to < to)
1743           {
1744             elt.from = from;
1745             elt.to = to;
1746             elt.val = candidates;
1747             return;
1748           }
1749       }
1750     this.table.push ({ from: from, to: to, val: candidates });
1751   }
1752
1753   MIM.CandidateTable.prototype.adjust = function (from, to, inserted)
1754   {
1755     var diff = inserted - (to - from);
1756     for (var i = 0; i < this.table.length; i++)
1757       {
1758         var elt = this.table[i];
1759         if (elt.from >= to)
1760           {
1761             elt.from += diff;
1762             elt.to += diff;
1763           }
1764       }
1765   }
1766
1767   MIM.CandidateTable.prototype.clear = function ()
1768   {
1769     this.table.length = 0;
1770   }
1771
1772   function Block (index, term)
1773   {
1774     this.Index = index;
1775     if (term.IsStr)
1776       this.Data = term.val;
1777     else if (term.IsList)
1778       {
1779         this.Data = new Array ();
1780         for (var i = 0; i < term.val.length; i++)
1781           this.Data.push (term.val[i].val);
1782       }
1783   }
1784
1785   Block.prototype.Count = function () { return this.Data.length; }
1786   Block.prototype.get = function (i)
1787   {
1788     return (this.Data instanceof Array ? this.Data[i] : this.Data.charAt (i));
1789   }
1790
1791   function fill_group (start)
1792   {
1793     var nitems = this.group.length;
1794     var r = this.row;
1795     var b = this.blocks[r];
1796
1797     if (start < b.Index)
1798       while (start < b.Index)
1799         b = this.blocks[--r];
1800     else
1801       while (start >= b.Index + b.Count ())
1802         b = this.blocks[++r];
1803     this.row = r;
1804
1805     var count = b.Count ();
1806     start -= b.Index;
1807     for (var i = 0; i < nitems; i++, start++)
1808       {
1809         if (start >= count)
1810           {
1811             r++;
1812             if (r == this.blocks.Length)
1813               return i;
1814             b = this.blocks[r];
1815             count = b.Count ();
1816             start = 0;
1817           }
1818         this.group[i] = b.get (start);
1819       }
1820     return nitems;
1821   }
1822
1823   function Candidates (candidates, column)
1824   {
1825     this.column = column;
1826     this.row = 0;
1827     this.index = 0;
1828     this.total = 0;
1829     this.blocks = new Array ();
1830
1831     for (var i = 0; i < candidates.length; i++)
1832       {
1833         var block = new Block (this.total, candidates[i]);
1834         this.blocks.push (block);
1835         this.total += block.Count ();
1836       }
1837   }
1838
1839   Candidates.prototype.Column = function ()
1840   {
1841     return (this.column > 0 ? this.index % this.column
1842             : this.index - this.blocks[this.row].Index);
1843   }
1844
1845   Candidates.prototype.GroupLength = function ()
1846   {
1847     if (this.column > 0)
1848       {
1849         var start = this.index - (this.index % this.column);
1850         return (start + this.column <= this.total ? this.column
1851                 : this.total - start);
1852       }
1853     return this.blocks[this.row].Count;
1854   }
1855
1856   Candidates.prototype.Current = function ()
1857   {
1858     var b = this.blocks[this.row];
1859     return b.get (this.index - b.Index);
1860   }
1861
1862   Candidates.prototype.PrevGroup = function ()
1863   {
1864     var col = this.Column ();
1865     var nitems;
1866     if (this.column > 0)
1867       {
1868         this.index -= this.column;
1869         if (this.index >= 0)
1870           nitems = this.column;
1871         else
1872           {
1873             var lastcol = (this.total - 1) % this.column;
1874             this.index = (col < lastcol ? this.total - lastcol + col
1875                           : this.total - 1);
1876             this.row = this.blocks.length - 1;
1877             nitems = lastcol + 1;
1878           }
1879         while (this.blocks[this.row].Index > this.index)
1880           this.row--;
1881       }
1882     else
1883       {
1884         this.row = this.row > 0 ? this.row - 1 : this.blocks.length - 1;
1885         nitems = this.blocks[this.row].Count ();
1886         this.index = (this.blocks[this.row].Index
1887                       + (col < nitems ? col : nitems - 1));
1888       }
1889     return nitems;
1890   }
1891
1892   Candidates.prototype.NextGroup = function ()
1893   {
1894     var col = this.Column ();
1895     var nitems;
1896     if (this.column > 0)
1897       {
1898         this.index += this.column - col;
1899         if (this.index < this.total)
1900           {
1901             if (this.index + col >= this.total)
1902               {
1903                 nitems = this.total - this.index;
1904                 this.index = this.total - 1;
1905               }
1906             else
1907               {
1908                 nitems = this.column;
1909                 this.index += col;
1910               }
1911           }
1912         else
1913           {
1914             this.index = col;
1915             this.row = 0;
1916           }
1917         while (this.blocks[this.row].Index > this.index)
1918           this.row++;
1919       }
1920     else
1921       {
1922         this.row = this.row < this.blocks.length - 1 ? this.row + 1 : 0;
1923         nitems = this.blocks[this.row].Count ();
1924         this.index = (this.blocks[this.row].Index
1925                       + (col < nitems ? col : nitems - 1));
1926       }
1927     return nitems;
1928   }
1929
1930   Candidates.prototype.Prev = function ()
1931   {
1932     if (this.index == 0)
1933       {
1934         this.index = this.total - 1;
1935         this.row = this.blocks.length - 1;
1936       }
1937     else
1938       {
1939         this.index--;
1940         if (this.blocks[this.row].Index > this.index)
1941           this.row--;
1942       }
1943     }
1944
1945   Candidates.prototype.Next = function ()
1946   {
1947     this.index++;
1948     if (this.index == this.total)
1949       {
1950         this.index = 0;
1951         this.row = 0;
1952       }
1953     else
1954       {
1955         var b = this.blocks[this.row];
1956         if (this.index == b.Index + b.Count ())
1957           this.row++;
1958       }
1959   }
1960
1961   Candidates.prototype.First = function ()
1962   {
1963     this.index -= this.Column ();
1964     while (this.blocks[this.row].Index > this.index)
1965       this.row--;
1966   }
1967
1968   Candidates.prototype.Last = function ()
1969   {
1970     var b = this.blocks[this.row];
1971     if (this.column > 0)
1972       {
1973         if (this.index + 1 < this.total)
1974           {
1975             this.index += this.column - this.Column () + 1;
1976             while (b.Index + b.Count () <= this.index)
1977               b = this.blocks[++this.row];
1978           }
1979       }
1980     else
1981       this.index = b.Index + b.Count () - 1;
1982   }
1983
1984   Candidates.prototype.Select = function (selector)
1985   {
1986     if (selector instanceof MIM.Selector)
1987       {
1988         switch (selector.val)
1989           {
1990           case '@<': this.First (); break;
1991           case '@>': this.Last (); break;
1992           case '@-': this.Prev (); break;
1993           case '@+': this.Next (); break;
1994           case '@[': this.PrevGroup (); break;
1995           case '@]': this.NextGroup (); break;
1996           default: break;
1997           }
1998         return this.Current ();
1999       }
2000     var col, start, end
2001     if (this.column > 0)
2002       {
2003         col = this.index % this.column;
2004         start = this.index - col;
2005         end = start + this.column;
2006       }
2007     else
2008       {
2009         start = this.blocks[this.row].Index;
2010         col = this.index - start;
2011         end = start + this.blocks[this.row].Count;
2012       }
2013     if (end > this.total)
2014       end = this.total;
2015     this.index += selector - col;
2016     if (this.index >= end)
2017       this.index = end - 1;
2018     if (this.column > 0)
2019       {
2020         if (selector > col)
2021           while (this.blocks[this.row].Index + this.blocks[this.row].Count
2022                  < this.index)
2023             this.row++;
2024         else
2025           while (this.blocks[this.row].Index > this.index)
2026             this.row--;
2027       }
2028     return this.Current ();
2029   }
2030
2031   function detach_candidates (ic)
2032   {
2033     ic.candidate_table.clear ();
2034     ic.candidates = null;
2035     ic.changed |= (MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos
2036                    | ChangedStatus.CandidateList
2037                    | ChangedStatus.CandidateIndex
2038                    | ChangedStatus.CandidateShow);
2039   }
2040
2041   function set_cursor (prefix, pos)
2042   {
2043     this.cursor_pos = pos;
2044     if (pos > 0)
2045       this.candidates = this.candidate_table.get (pos - 1);
2046     else
2047       this.candidates = null;
2048   }
2049
2050   function save_state ()
2051   {
2052     this.state_var_values = this.domain.SaveValues ();
2053     this.state_preedit = this.preedit;
2054     this.state_key_head = this.key_head;
2055     this.state_pos = this.cursor_pos;
2056   }
2057
2058   function restore_state ()
2059   {
2060     this.domain.RestoreValues (this.state_var_values);
2061     this.preedit = this.state_preedit;
2062     set_cursor.call (this, "restore", this.state_pos);
2063   }
2064
2065   function handle_key ()
2066   {
2067     var out = this.keymap.Lookup (this.keys, this.key_head);
2068     var sub = out.map;
2069     var branch_actions = this.state.keymap.actions;
2070
2071     MIM.log ('handling ' + this.keys.val[this.key_head]
2072              + ' in ' + this.state.name + ':' + this.keymap.name);
2073     this.key_head = out.index;
2074     if (sub != this.keymap)
2075       {
2076
2077         restore_state.call (this);
2078         this.keymap = sub;
2079         MIM.log ('submap found');
2080         if (this.keymap.actions)
2081           {
2082             MIM.log ('taking map actions:');
2083             if (! this.take_actions (this.keymap.actions))
2084               return false;
2085           }
2086         else if (this.keymap.submaps)
2087           {
2088             MIM.log ('no map actions');
2089             for (var i = this.state_key_head; i < this.key_head; i++)
2090               {
2091                 MIM.log ('inserting key:' + this.keys.val[i].key);
2092                 this.insert (this.keys.val[i].key, null);
2093               }
2094           }
2095         if (! this.keymap.submaps)
2096           {
2097             MIM.log ('terminal:');
2098             if (this.keymap.branch_actions != null)
2099               {
2100                 MIM.log ('branch actions:');
2101                 if (! this.take_actions (branch_actions))
2102                   return false;
2103               }
2104             if (this.keymap != this.state.keymap)
2105               this.shift (this.state);
2106           }
2107       }
2108     else
2109       {
2110         MIM.log ('no submap');
2111         var current_state = this.state;
2112         var map = this.keymap;
2113
2114         if (branch_actions)
2115           {
2116             MIM.log ('branch actions');
2117             if (! this.take_actions (this.keymap.branch_actions))
2118               return false;
2119           }
2120
2121         if (map == this.keymap)
2122           {
2123             MIM.log ('no state change');
2124             if (map == this.initial_state.keymap
2125                 && this.key_head < this.keys.val.length)
2126               {
2127                 MIM.log ('unhandled');
2128                 return false;
2129               }
2130             if (this.keymap != current_state.keymap)
2131               this.shift (current_state);
2132             else if (this.keymap.actions == null)
2133               this.shift (this.initial_state);
2134           }
2135       }
2136     return true;
2137   }
2138
2139   proto = {
2140     reset: function ()
2141     {
2142       this.produced = null;
2143       this.preedit = '';
2144       this.preedit_saved = '';
2145       this.cursor_pos = 0;
2146       this.marker_positions = {};
2147       this.candidates = null;
2148       this.candidate_show = false;
2149       this.state = null;
2150       this.prev_state = null;
2151       this.initial_state = this.im.initial_state;
2152       this.title = this.initial_state.title;
2153       this.state_preedit = '';
2154       this.state_key_head = 0;
2155       this.state_var_values = {};
2156       this.state_pos = 0;
2157       this.keymap = null;
2158       this.keys = new MIM.KeySeq ();
2159       this.key_head = 0;
2160       this.key_unhandled = false;
2161       this.unhandled_key = null;
2162       this.changed = MIM.ChangedStatus.None;
2163       this.error_message = '';
2164       this.title = this.initial_state.title;
2165       this.produced = '';
2166       this.preedit = '';
2167       this.preedit_saved = '';
2168       this.marker_positions = {};
2169       this.candidate_table = new MIM.CandidateTable ();
2170       this.candidates = null;
2171       this.candidate_show = false;
2172       this.shift (this.initial_state);
2173     },
2174
2175     catch_args: new Array (Xex.CatchTag._mimtag, null),
2176
2177     take_actions: function (actions)
2178     {
2179       var func_progn = this.domain.GetFunc ('progn');
2180       var func_catch = this.domain.GetFunc ('catch');
2181       this.catch_args[1] = new Xex.Funcall (func_progn, null, actions);
2182       var term = new Xex.Funcall (func_catch, null, this.catch_args);
2183       term = term.Eval (this.domain);
2184       return (! term.IsSymbol || term.val != '@mimtag');
2185     },
2186
2187     GetSurroundingChar: function (pos)
2188     {
2189       if (pos < 0 ? this.caret_pos < - pos : this.target.value.length < pos)
2190         return 0;
2191       return this.target.value.charCodeAt (this.caret_pos + pos);
2192     },
2193     
2194     adjust_markers: function (from, to, inserted)
2195     {
2196       var diff = inserted - (to - from);
2197
2198       for (var m in this.marker_positions)
2199         if (this.marker_positions[m] > from)
2200           this.marker_positions[m] = (this.marker_positions[m] >= to
2201                                       ? pos + diff : from);
2202       if (this.cursor_pos >= to)
2203         set_cursor.call (this, 'adjust', this.cursor_pos + diff);
2204       else if (this.cursor_pos > from)
2205         set_cursor.call (this, 'adjust', from)
2206     },
2207
2208     preedit_replace: function (from, to, text, candidates)
2209     {
2210       this.preedit = (this.preedit.substring (0, from)
2211                       + text + this.preedit.substring (to));
2212       this.adjust_markers (from, to, text.length);
2213       this.candidate_table.adjust (from, to, text.length);
2214       if (candidates)
2215         this.candidate_table.put (from, from + text.length, candidates)
2216     },
2217
2218     insert: function (text, candidates)
2219     {
2220       this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates);
2221       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
2222     },
2223
2224     del: function (pos)
2225     {
2226       if (pos < 0)
2227         {
2228           this.DelSurroundText (pos);
2229           pos = 0;
2230         }
2231       else if (pos > this.preedit.length)
2232         {
2233           this.DelSurroundText (pos - this.preedit.length);
2234           pos = this.preedit.length;
2235         }
2236       if  (pos < this.cursor_pos)
2237         this.preedit = (this.predit.substring (0, pos)
2238                         + this.preedit.substring (this.cursor_pos));
2239       else
2240         this.preedit = (this.preedit.substring (0, this.cursor_pos)
2241                         + this.predit.substring (pos));
2242     },
2243
2244     show: function ()
2245     {
2246       this.candidate_show = true;
2247       this.changed |= MIM.ChangedStatus.CandidateShow;
2248     },
2249
2250     hide: function ()
2251     {
2252       this.candidate_show = false;
2253       this.changed |= MIM.ChangedStatus.CandidateShow;
2254     },
2255
2256     move: function (pos)
2257     {
2258       if (pos < 0)
2259         pos = 0;
2260       else if (pos > this.preedit.length)
2261         pos = this.preedit.length;
2262       if (pos != this.cursor_pos)
2263         {
2264           set_cursor.call (this, 'move', pos);
2265           this.changed |= MIM.ChangedStatus.Preedit;
2266         }
2267     },
2268
2269     pushback: function (n)
2270     {
2271       if (n instanceof MIM.KeySeq)
2272         {
2273           if (this.key_head > 0)
2274             this.key_head--;
2275           if (this.key_head < this.keys.val.length)
2276             this.keys.val.splice (this.key_head,
2277                                   this.keys.val.length - this.key_head);
2278           for (var i = 0; i < n.val.length; i++)
2279             this.keys.val.push (n.val[i]);
2280           return;
2281         }
2282       if (n > 0)
2283         {
2284           this.key_head -= n;
2285           if (this.key_head < 0)
2286             this.key_head = 0;
2287         }
2288       else if (n == 0)
2289         this.key_head = 0;
2290       else
2291       {
2292         this.key_head = - n;
2293         if (this.key_head > this.keys.val.length)
2294           this.key_head = this.keys.val.length;
2295       }
2296     },
2297
2298     pop: function ()
2299     {
2300       if (this.key_head < this.keys.val.length)
2301         this.keys.val.splice (this.key_head, 1);
2302     },
2303
2304     commit: function ()
2305     {
2306       if (this.preedit.length > 0)
2307       {
2308         this.candidate_table.clear ();
2309         this.produced += this.preedit;
2310         this.preedit_replace.call (this, 0, this.preedit.length, '', null);
2311       }
2312     },
2313
2314     shift: function (state)
2315     {
2316       if (state == null)
2317         {
2318           MIM.log ("shifting back to previous");
2319           if (this.prev_state == null)
2320             return;
2321           state = this.prev_state;
2322         }
2323       else
2324         MIM.log ("shifting to " + state.name);
2325
2326       if (state == this.initial_state)
2327         {
2328           if (this.state)
2329             {
2330               this.commit ();
2331               this.keys.val.splice (0, this.key_head);
2332               this.key_head = 0;
2333               this.prev_state = null;
2334             }
2335         }
2336       else
2337         {
2338           if (state != this.state)
2339             this.prev_state = this.state;
2340         }
2341       if (state != this.state && state.enter_actions)
2342         take_actions.call (state.enter_actions);
2343       if (! this.state || this.state.title != state.title)
2344         this.changed |= MIM.ChangedStatus.StateTitle;
2345       this.state = state;
2346       this.keymap = state.keymap;
2347       this.state_key_head = this.key_head;
2348       save_state.call (this);
2349     },
2350
2351     Filter: function (key)
2352     {
2353       if (! this.active)
2354         {
2355           this.key_unhandled = true;
2356           this.unhandled_key = key;
2357           return false;
2358         }
2359       if (key.key == '_reload')
2360         return true;
2361       this.changed = MIM.ChangedStatus.None;
2362       this.produced = '';
2363       this.key_unhandled = false;
2364       this.keys.val.push (key);
2365       var count = 0;
2366       while (this.key_head < this.keys.val.length)
2367         {
2368           if (! handle_key.call (this))
2369             {
2370               if (this.key_head < this.keys.val.length)
2371                 {
2372                   this.unhandled_key = this.keys.val[this.key_head];
2373                   this.keys.val.splice (this.key_head, this.key_head + 1);
2374                 }
2375               this.key_unhandled = true;
2376               break;
2377             }
2378           if (++count == 10)
2379             {
2380               this.reset ();
2381               this.key_unhandled = true;
2382               break;
2383             }
2384         }
2385       if (this.key_unhandled)
2386         {
2387           this.keys.val.length = 0;
2388           this.key_head = this.state_key_head = this.commit_key_head = 0;
2389         }
2390       return (! this.key_unhandled
2391               && this.produced.length == 0
2392               && this.preedit.length == 0);
2393     }
2394   }
2395
2396   MIM.IC.prototype = proto;
2397
2398   var node = Xex.Load (null, "imlist.xml");
2399   for (node = node.firstChild; node; node = node.nextSibling)
2400     if (node.nodeName == 'input-method')
2401       {
2402         var lang = null, name = null, extra_id = null, file = null;
2403
2404         for (var n = node.firstChild; n; n = n.nextSibling)
2405           {
2406             if (n.nodeName == 'language')
2407               lang = n.firstChild.nodeValue;
2408             else if (n.nodeName == 'name')
2409               name = n.firstChild.nodeValue;
2410             else if (n.nodeName == 'extra-id')
2411               extra_id = n.firstChild.nodeValue;
2412             else if (n.nodeName == 'filename')
2413               file = n.firstChild.nodeValue;
2414           }
2415         if (name && name != 'nil')
2416           {
2417             if (! MIM.imlist[lang])
2418               MIM.imlist[lang] = {};
2419             MIM.imlist[lang][name] = new MIM.IM (lang, name, extra_id, file);
2420           }
2421         else if (extra_id && extra_id != 'nil')
2422           {
2423             if (! MIM.imextra[lang])
2424               MIM.imextra[lang] = {};
2425             MIM.imextra[lang][extra_id] = new MIM.IM (lang, name, extra_id, file);
2426           }
2427       }
2428   if (MIM.imextra.t && MIM.imextra.t.global)
2429     MIM.im_global = MIM.imextra.t.global;
2430   else
2431     {
2432       MIM.im_global = new MIM.IM ('t', 'nil', 'global', null);
2433       MIM.im_global.load_status = MIM.LoadStatus.Error;
2434     }
2435   node = undefined;
2436 }) ();
2437
2438 (function () {
2439   var keys = new Array ();
2440   keys[0x09] = 'tab';
2441   keys[0x08] = 'backspace';
2442   keys[0x0D] = 'return';
2443   keys[0x1B] = 'escape';
2444   keys[0x20] = 'space';
2445   keys[0x21] = 'pageup';
2446   keys[0x22] = 'pagedown';
2447   keys[0x23] = 'end';
2448   keys[0x24] = 'home';
2449   keys[0x25] = 'left';
2450   keys[0x26] = 'up';
2451   keys[0x27] = 'right';
2452   keys[0x28] = 'down';
2453   keys[0x2D] = 'insert';
2454   keys[0x2E] = 'delete';
2455   for (var i = 1; i <= 12; i++)
2456     keys[111 + i] = "f" + i;
2457   keys[0x90] = "numlock";
2458   keys[0xF0] = "capslock";
2459
2460   MIM.decode_key_event = function (event)
2461   {
2462     var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
2463                : event.charCode ? event.charCode
2464                : false);
2465     if (! key)
2466       return false;
2467     if (event.type == 'keydown')
2468       {
2469         key = keys[key];
2470         if (! key)
2471           return false;
2472         if (event.shiftKey) key = "S-" + key ;
2473       }
2474     else
2475       key = String.fromCharCode (key);
2476     if (event.altKey) key = "A-" + key ;
2477     if (event.ctrlKey) key = "C-" + key ;
2478     return new MIM.Key (key);
2479   }
2480 }) ();
2481
2482 MIM.add_event_listener
2483   = (window.addEventListener
2484      ? function (target, type, listener) {
2485        target.addEventListener (type, listener, false);
2486      }
2487      : window.attachEvent
2488      ? function (target, type, listener) {
2489        target.attachEvent ('on' + type,
2490                            function() {
2491                              listener.call (target, window.event);
2492                            });
2493      }
2494      : function (target, type, listener) {
2495        target['on' + type]
2496          = function (e) { listener.call (target, e || window.event); };
2497      });
2498
2499 MIM.log = function (msg)
2500 {
2501   var node = document.getElementById ('log');
2502   node.value = msg + "\n" + node.value;
2503 }
2504
2505 MIM.debug_print = function (event, ic)
2506 {
2507   if (! MIM.debug)
2508     return;
2509   if (! MIM.debug_nodes)
2510     {
2511       MIM.debug_nodes = new Array ();
2512       MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
2513       MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
2514       MIM.debug_nodes['status0'] = document.getElementById ('status0');
2515       MIM.debug_nodes['status1'] = document.getElementById ('status1');
2516       MIM.debug_nodes['keymap0'] = document.getElementById ('keymap0');
2517       MIM.debug_nodes['keymap1'] = document.getElementById ('keymap1');
2518       MIM.debug_nodes['preedit0'] = document.getElementById ('preedit0');
2519       MIM.debug_nodes['preedit1'] = document.getElementById ('preedit1');
2520     }
2521   var target = event.target;
2522   var code = event.keyCode;
2523   var ch = event.type == 'keydown' ? 0 : event.charCode;
2524   var key = MIM.decode_key_event (event);
2525   var index;
2526
2527   MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
2528   index = (event.type == 'keydown' ? '0' : '1');
2529   if (ic)
2530     MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status;
2531   else
2532     MIM.debug_nodes['status' + index].innerHTML = 'no IM';
2533   MIM.debug_nodes['keymap' + index].innerHTML = ic.state.name;
2534   MIM.debug_nodes['preedit' + index].innerHTML = ic.preedit;
2535 };
2536
2537 MIM.get_range = function (target, range)
2538 {
2539   if (target.selectionStart != null) // for Mozilla
2540     {
2541       range[0] = target.selectionStart;
2542       range[1] = target.selectionEnd;
2543     }
2544   else                          // for IE
2545     {
2546       var r = document.selection.createRange ();
2547       var rr = r.duplicate ();
2548
2549       rr.moveToElementText (target);
2550       rr.setEndPoint ('EndToEnd', range);
2551       range[0] = rr.text.length - r.text.length;
2552       range[1] = rr.text.length;
2553     }
2554 }
2555
2556 MIM.set_caret = function (target, ic)
2557 {
2558   if (target.setSelectionRange) // Mozilla
2559     {
2560       var scrollTop = target.scrollTop;
2561       target.setSelectionRange (ic.spot, ic.spot + ic.preedit.length);
2562       target.scrollTop = scrollTop;
2563     }
2564   else                          // IE
2565     {
2566       var range = target.createTextRange ();
2567       range.moveStart ('character', ic.spot);
2568       range.moveEnd ('character', ic.spot + ic.preedit.length);
2569       range.select ();
2570     }
2571 };
2572
2573 (function () {
2574   var range = new Array ();
2575
2576   MIM.check_range = function (target, ic)
2577   {
2578     MIM.get_range (target, range);
2579     if (range[0] != ic.spot || range[1] - range[0] != ic.preedit.length
2580         || target.value.substring (range[0], range[1]) != ic.preedit)
2581       {
2582         MIM.log ('reset:' + ic.spot + '-' + (ic.spot + ic.preedit.length)
2583                  + '/' + range[0] + '-' + range[1]);
2584         ic.reset ();
2585       }
2586     target.value = (target.value.substring (0, range[0])
2587                     + target.value.substring (range[1]));
2588     ic.spot = range[0];
2589   }
2590 }) ();
2591
2592 MIM.update = function (target, ic)
2593 {
2594   var text = target.value;
2595   target.value = (text.substring (0, ic.spot)
2596                   + ic.produced
2597                   + ic.preedit
2598                   + text.substring (ic.spot));
2599   ic.spot += ic.produced.length;
2600   MIM.set_caret (target, ic);
2601 };
2602
2603 MIM.reset_ic = function (event)
2604 {
2605   if (event.target.mim_ic)
2606     {
2607       var ic = event.target.mim_ic;
2608       var pos = ic.spot + ic.preedit.length;
2609       ic.reset ();
2610       if (pos > ic.spot)
2611         event.target.setSelectionRange (pos, pos);
2612     }
2613 };
2614
2615 MIM.keydown = function (event)
2616 {
2617   var target = event.target;
2618   if (! (target.type == "text" || target.type == "textarea"))
2619     return;
2620
2621   var ic = target.mim_ic;
2622   if (! ic || ic.im != MIM.current)
2623     {
2624       MIM.log ('creating IC');
2625       ic = new MIM.IC (MIM.current);
2626       target.mim_ic = ic;
2627       MIM.add_event_listener (target, 'blur', MIM.reset_ic);
2628     }
2629   if (ic.im.load_status != MIM.LoadStatus.Loaded)
2630     return;
2631   MIM.check_range (target, ic);
2632   MIM.debug_print (event, ic);
2633   ic.key = MIM.decode_key_event (event);
2634 };
2635
2636 MIM.keypress = function (event)
2637 {
2638   if (! (event.target.type == "text" || event.target.type == "textarea"))
2639     return;
2640
2641   var ic = event.target.mim_ic;
2642   var i;
2643
2644   try {
2645     if (ic.im.load_status != MIM.LoadStatus.Loaded)
2646       return;
2647     if (! ic.key)
2648       ic.key = MIM.decode_key_event (event);
2649     if (! ic.key)
2650       {
2651         ic.reset ();
2652         return;
2653       }
2654     
2655     MIM.log ("filtering " + ic.key);
2656     var result = ic.Filter (ic.key);
2657     MIM.update (event.target, ic);
2658     if (! ic.key_unhandled)
2659       event.preventDefault ();
2660   } finally {
2661     MIM.debug_print (event, ic);
2662   }
2663   return;
2664 };
2665
2666 MIM.select_im = function (event)
2667 {
2668   var target = event.target.parentNode;
2669   while (target.tagName != "SELECT")
2670     target = target.parentNode;
2671   var idx = 0;
2672   var im = false;
2673   for (var lang in MIM.imlist)
2674     for (var name in MIM.imlist[lang])
2675       if (idx++ == target.selectedIndex)
2676         {
2677           im = MIM.imlist[lang][name];
2678           break;
2679         }
2680   document.getElementsByTagName ('body')[0].removeChild (target);
2681   target.target.focus ();
2682   if (im && im != MIM.current)
2683     {
2684       MIM.current = im;
2685       MIM.log ('select IM: ' + im.name);
2686     }
2687 };
2688
2689 MIM.destroy_menu = function (event)
2690 {
2691   if (event.target.tagName == "SELECT")
2692     document.getElementsByTagName ('body')[0].removeChild (event.target);
2693 };
2694
2695 MIM.select_menu = function (event)
2696 {
2697   var target = event.target;
2698
2699   if (! ((target.type == "text" || target.type == "textarea")
2700          && event.which == 1 && event.ctrlKey))
2701     return;
2702
2703   var sel = document.createElement ('select');
2704   sel.onclick = MIM.select_im;
2705   sel.onmouseout = MIM.destroy_menu;
2706   sel.style.position='absolute';
2707   sel.style.left = (event.clientX - 10) + "px";
2708   sel.style.top = (event.clientY - 10) + "px";
2709   sel.target = target;
2710   var idx = 0;
2711   for (var lang in MIM.imlist)
2712     for (var name in MIM.imlist[lang])
2713       {
2714         var option = document.createElement ('option');
2715         var imname = lang + "-" + name;
2716         option.appendChild (document.createTextNode (imname));
2717         option.value = imname;
2718         sel.appendChild (option);
2719         if (MIM.imlist[lang][name] == MIM.current)
2720           sel.selectedIndex = idx;
2721         idx++;
2722       }
2723   sel.size = idx;
2724   document.getElementsByTagName ('body')[0].appendChild (sel);
2725 };
2726
2727 MIM.test = function ()
2728 {
2729   var im = MIM.imlist['t']['latn-post'];
2730   var ic = new MIM.IC (im);
2731
2732   ic.Filter (new MIM.Key ('a'));
2733   ic.Filter (new MIM.Key ("'"));
2734
2735   if (true)
2736     document.getElementById ('text').value = ic.produced + ic.preedit;
2737   else {
2738     try {
2739       document.getElementById ('text').value
2740         = Xex.Term.Parse (domain, body).Eval (domain).toString ();
2741     } catch (e) {
2742       if (e instanceof Xex.ErrTerm)
2743         alert (e);
2744       throw e;
2745     }
2746   }
2747 }
2748
2749
2750 MIM.init = function ()
2751 {
2752   MIM.add_event_listener (window, 'keydown', MIM.keydown);
2753   MIM.add_event_listener (window, 'keypress', MIM.keypress);
2754   MIM.add_event_listener (window, 'mousedown', MIM.select_menu);
2755   if (window.location == 'http://localhost/mim/index.html')
2756     MIM.server = 'http://localhost/mim';
2757   MIM.current = MIM.imlist['vi']['telex'];
2758 };
2759
2760 MIM.init_debug = function ()
2761 {
2762   MIM.debug = true;
2763   MIM.init ();
2764 };