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