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