*** empty log message ***
[m17n/m17n-lib-js.git] / xex.js
diff --git a/xex.js b/xex.js
index a1d38c0..46d510f 100644 (file)
--- a/xex.js
+++ b/xex.js
@@ -2,19 +2,31 @@
 
 var Xex = {
   LogNode: null,
-  Log: function (arg, indent)
+  LogNodeLen: 0,
+  Log: function (arg, indent, cont)
   {
     if (! Xex.LogNode)
       return;
     if (! arg)
-      Xex.LogNode.value = '';
+      {
+       while (Xex.LogNode.childNodes.length > 0)
+         Xex.LogNode.removeChild (Xex.LogNode.firstChild);
+       LogNodeLen = 0;
+      }
     else
       {
-       var str = '';
+       var node;
+       LogNodeLen++;
+       if (LogNodeLen >= 200)
+         node = Xex.LogNode.firstElement ();
+       else
+         node = document.createElement ('div');
        if (indent != undefined)
-         for (var i = 0; i <= indent; i++)
-           str += '  ';
-       Xex.LogNode.value += "\n" + str + arg;
+         node.style.textIndent = (indent + 1) + 'em';
+       else
+         node.style.textIndent = '0px';
+       node.innerText = LogNodeLen + ': ' + arg;
+       Xex.LogNode.appendChild (node);
        Xex.LogNode.scrollTop = Xex.LogNode.scrollHeight;
       }
   }
@@ -471,7 +483,7 @@ Node.prototype.nextElement = function ()
     if (! name)
       throw new Xex.ErrTerm (Xex.Error.NoVariableName, node, '');
     var vari = domain.variables[name];
-    var desc, val, range;
+    var desc, val = null, range;
     if (vari)
       {
        desc = vari.description;
@@ -519,7 +531,7 @@ Node.prototype.nextElement = function ()
              range.push (pval);
          }
       }
-    if (! val)
+    if (val == null)
       val = Xex.Zero;
     domain.Defvar (name, desc, val, range);
     return name;
@@ -685,8 +697,7 @@ Xex.Funcall = function (func, vname, args)
 
   proto.Eval = function (domain)
   {
-    if (! (this.func instanceof Xex.Subrountine))
-      Xex.Log (this, domain.depth);
+    Xex.Log (this, domain.depth);
     var vari;
     if (this.vname)
       vari = domain.GetVarCreate (this.vname);
@@ -695,7 +706,8 @@ Xex.Funcall = function (func, vname, args)
     try {
       result = this.func.Call (domain, vari, this.args);
     } finally {
-      Xex.Log (this + ' => ' + result, --domain.depth);
+      Xex.Log (' => ' + result, --domain.depth,
+              this.func instanceof Xex.Subrountine);
     }
     return result;
   }
@@ -718,8 +730,8 @@ Xex.Funcall = function (func, vname, args)
     var arglist = ''
     var len = this.args.length;
     var str = '<' + this.func.name;
-    if (this.vari)
-      str += ' vname="' + this.vari.name + '"';
+    if (this.vname)
+      str += ' vname="' + this.vname + '"';
     if (len == 0)
       return str + '/>';
     if (this.func instanceof Xex.Subrountine)
@@ -1605,10 +1617,8 @@ MIM.Selector.prototype = new Xex.Term ('selector');
   selectors["@>"] = selectors["@last"] = new MIM.Selector ('@>');
   selectors["@-"] = selectors["@previous"] = new MIM.Selector ('@-');
   selectors["@+"] = selectors["@next"] = new MIM.Selector ('@+');
-  selectors["@["] = selectors["@previous-candidate-change"]
-    = new MIM.Selector ('@[');
-  selectors["@]"] = selectors["@next-candidate-change"]
-    = new MIM.Selector ('@]');
+  selectors["@["] = selectors["@previous-group"] = new MIM.Selector ('@[');
+  selectors["@]"] = selectors["@next-group"] = new MIM.Selector ('@]');
 
   MIM.Selector.prototype.Parser = function (domain, node)
   {
@@ -1624,7 +1634,8 @@ MIM.Selector.prototype = new Xex.Term ('selector');
 MIM.Rule = function (keyseq, actions)
 {
   this.keyseq = keyseq;
-  this.actions = actions;
+  if (actions)
+    this.actions = actions;
 }
 MIM.Rule.prototype = new Xex.Term ('rule');
 MIM.Rule.prototype.Parser = function (domain, node)
@@ -1636,7 +1647,10 @@ MIM.Rule.prototype.Parser = function (domain, node)
   var keyseq = Xex.Term.Parse (domain, n);
   if (keyseq.type != 'keyseq')
     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
-  var actions = Xex.Term.Parse (domain, n.nextElement (), null);
+  var actions = null;
+  n = n.nextElement ();
+  if (n)
+    actions = Xex.Term.Parse (domain, n, null);
   return new MIM.Rule (keyseq, actions);
 }
 MIM.Rule.prototype.toString = function ()
@@ -1730,7 +1744,8 @@ MIM.Keymap = function ()
        keymap.name = name;
       }
     keymap.map_actions = rule.actions;
-    keymap.branch_actions = branch_actions;
+    if (branch_actions)
+      keymap.branch_actions = branch_actions;
   }
 
   proto.Add = function (map, branch_actions)
@@ -1787,13 +1802,14 @@ MIM.State = function (name)
        else
          {
            var n = node.firstElement ();
+           var branch_actions = n ? Xex.Term.Parse (domain, n, null) : null;
            if (node.nodeName == 'branch')
              state.keymap.Add (map_list[node.attributes['mname'].nodeValue],
-                               Xex.Term.Parse (domain, n, null));
+                               branch_actions);
            else if (node.nodeName == 'state-hook')
-             state.enter_actions = Xex.Term.Parse (domain, n, null);
+             state.enter_actions = branch_actions;
            else if (node.nodeName == 'catch-all-branch')
-             state.fallback_actions = Xex.Term.Parse (domain, n, null);
+             state.fallback_actions = branch_actions;
          }
       }
     return state;
@@ -1827,8 +1843,9 @@ MIM.State = function (name)
     return (this.Data instanceof Array ? this.Data[i] : this.Data.charAt (i));
   }
 
-  MIM.Candidates = function (candidates, column)
+  MIM.Candidates = function (ic, candidates, column)
   {
+    this.ic = ic;
     this.column = column;
     this.row = 0;
     this.index = 0;
@@ -1904,7 +1921,8 @@ MIM.State = function (name)
            this.index = col;
            this.row = 0;
          }
-       while (this.blocks[this.row].Index > this.index)
+       while (this.blocks[this.row].Index + this.blocks[this.row].Count ()
+              <= this.index)
          this.row++;
       }
     else
@@ -1979,6 +1997,8 @@ MIM.State = function (name)
 
   MIM.Candidates.prototype.Select = function (selector)
   {
+    var idx = this.index;
+    var gidx = this.column > 0 ? idx / this.column : this.row;
     if (selector.type == 'selector')
       {
        switch (selector.val)
@@ -1988,40 +2008,89 @@ MIM.State = function (name)
          case '@-': prev.call (this); break;
          case '@+': next.call (this); break;
          case '@[': prev_group.call (this); break;
-         case '@]': next_group.cal (this); break;
+         case '@]': next_group.call (this); break;
          default: break;
          }
-       return this.Current ();
       }
-    var col, start, end
+    else
+      {
+       var col, start, end
+       if (this.column > 0)
+         {
+           col = this.index % this.column;
+           start = this.index - col;
+           end = start + this.column;
+         }
+       else
+         {
+           start = this.blocks[this.row].Index;
+           col = this.index - start;
+           end = start + this.blocks[this.row].Count;
+         }
+       if (end > this.total)
+         end = this.total;
+       this.index += selector.val - col;
+       if (this.index >= end)
+         this.index = end - 1;
+       if (this.column > 0)
+         {
+           if (selector.val > col)
+             while (this.blocks[this.row].Index + this.blocks[this.row].Count
+                    < this.index)
+               this.row++;
+           else
+             while (this.blocks[this.row].Index > this.index)
+               this.row--;
+         }
+      }
+    var newgidx = this.column > 0 ? this.index / this.column : this.row;
+    if (this.index != idx)
+      this.ic.changed |= (gidx == newgidx
+                         ? MIM.ChangedStatus.CandidateIndex
+                         : MIM.ChangedStatus.CandidateList);
+    return this.Current ();
+  }
+
+  MIM.Candidates.prototype.CurrentCol = function ()
+  {
+    return get_col.call (this);
+  }
+
+  MIM.Candidates.prototype.CurrentGroup = function ()
+  {
+    var col, start, end, gnum, gidx;
     if (this.column > 0)
       {
+       gnum = Math.floor ((this.total - 1) / this.column) + 1;
        col = this.index % this.column;
        start = this.index - col;
+       gidx = start / this.column + 1;
        end = start + this.column;
+       if (end > this.total)
+         end = this.total;
       }
     else
       {
+       gnum = this.blocks.length;
+       gidx = this.row + 1;
        start = this.blocks[this.row].Index;
        col = this.index - start;
-       end = start + this.blocks[this.row].Count;
+       end = start + this.blocks[this.row].Count ();
       }
-    if (end > this.total)
-      end = this.total;
-    this.index += selector.val - col;
-    if (this.index >= end)
-      this.index = end - 1;
-    if (this.column > 0)
+    var group = new Array ();
+    var indices = new Array (gnum, gidx, col);
+    group.push (indices);
+    var row = this.row;
+    var block = this.blocks[row++];
+    while (start < end)
       {
-       if (selector.val > col)
-         while (this.blocks[this.row].Index + this.blocks[this.row].Count
-                < this.index)
-           this.row++;
-       else
-         while (this.blocks[this.row].Index > this.index)
-           this.row--;
+       var c = block.get (start - block.Index);
+       group.push (c);
+       start++;
+       if (start == block.Index + block.Count ())
+         block = this.blocks[row++];
       }
-    return this.Current ();
+    return group;
   }
 }) ();
 
@@ -2050,8 +2119,9 @@ MIM.im_domain.DefType (MIM.State.prototype);
   function Finsert_candidates (domain, vari, args)
   {
     var ic = domain.context;
-    var gsize = domain.variables['candidates_group_size'];
-    var candidates = new MIM.Candidates (args, gsize ? gsize.Intval () : 0);
+    var gsize = domain.variables['candidates-group-size'];
+    var candidates = new MIM.Candidates (ic, args,
+                                        gsize ? gsize.val.Intval () : 0);
     ic.ins (candidates.Current (), candidates);
     return args[0];
   }
@@ -2259,7 +2329,7 @@ MIM.im_domain.DefType (MIM.State.prototype);
          {
            var vari = get_global_var (vname);
            if (vari != null)
-             this.domain.Defvar (vname);
+             this.domain.Defvar (vname, vari.desc, vari.val, vari.range);
          }
        vname = Xex.Term.Parse (this.domain, node)
       }
@@ -2455,20 +2525,15 @@ MIM.im_domain.DefType (MIM.State.prototype);
     this.table.length = 0;
   }
 
-  function detach_candidates (ic)
-  {
-    ic.candidate_table.clear ();
-    ic.candidates = null;
-    ic.changed |= (MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos
-                  | MIM.ChangedStatus.CandidateList
-                  | MIM.ChangedStatus.CandidateIndex
-                  | MIM.ChangedStatus.CandidateShow);
-  }
-
   function set_cursor (prefix, pos)
   {
     this.cursor_pos = pos;
-    this.candidates = this.candidate_table.get (pos);
+    var candidates = this.candidate_table.get (pos);
+    if (this.candidates != candidates)
+      {
+       this.candidates = candidates;
+       this.changed |= MIM.ChangedStatus.CandidateList;
+      }
   }
 
   function save_state ()
@@ -2498,7 +2563,7 @@ MIM.im_domain.DefType (MIM.State.prototype);
     if (out.index > this.key_head)
       {
        this.key_head = out.index;
-       Xex.Log (' with submap for ' + this.key_head + 'keys');
+       Xex.Log (' with submap', false, true);
        restore_state.call (this);
        this.keymap = sub;
        if (sub.map_actions)
@@ -2519,7 +2584,7 @@ MIM.im_domain.DefType (MIM.State.prototype);
        if (! sub.submaps)
          {
            Xex.Log ('terminal:');
-           if (this.keymap.branch_actions != null)
+           if (this.keymap.branch_actions)
              {
                Xex.Log ('branch actions:');
                if (! this.take_actions (this.keymap.branch_actions))
@@ -2531,14 +2596,14 @@ MIM.im_domain.DefType (MIM.State.prototype);
       }
     else
       {
-       Xex.Log (' without submap');
+       Xex.Log (' without submap', false, true);
        this.keymap = sub;
        var current_state = this.state;
        var map = this.keymap;
 
        if (map.branch_actions)
          {
-           Xex.Log ('branch actions');
+           Xex.Log ('branch actions:');
            if (! this.take_actions (map.branch_actions))
              return false;
          }
@@ -2594,6 +2659,8 @@ MIM.im_domain.DefType (MIM.State.prototype);
 
     take_actions: function (actions)
     {
+      if (actions.length == 0)
+       return true;;
       var func_progn = this.domain.GetFunc ('progn');
       var func_catch = this.domain.GetFunc ('catch');
       this.catch_args[1] = new Xex.Funcall (func_progn, null, actions);
@@ -2664,33 +2731,33 @@ MIM.im_domain.DefType (MIM.State.prototype);
                this.marker_positions[name] = from;
            }
        }
-      if (this.cursor_pos >= to)
-       set_cursor.call (this, 'adjust', this.cursor_pos + diff);
-      else if (this.cursor_pos > from)
-       set_cursor.call (this, 'adjust', from)
     },
 
     preedit_replace: function (from, to, text, candidates)
     {
+      var newlen = text.length;
       this.preedit = (this.preedit.substring (0, from)
                      + text + this.preedit.substring (to));
-      this.adjust_markers (from, to, text.length);
-      this.candidate_table.adjust (from, to, text.length);
+      this.changed |= MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
+      this.adjust_markers (from, to, newlen);
+      this.candidate_table.adjust (from, to, newlen);
       if (candidates)
-       this.candidate_table.put (from, from + text.length, candidates)
+       this.candidate_table.put (from, from + newlen, candidates)
+      if (this.cursor_pos >= to)
+       set_cursor.call (this, 'adjust', this.cursor_pos + text.length - (to - from));
+      else if (this.cursor_pos > from)
+       set_cursor.call (this, 'adjust', from)
     },
 
     ins: function (text, candidates)
     {
       this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates);
-      this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
     },
 
     rep: function (old_text, new_text, candidates)
     {
       this.preedit_replace (this.cursor_pos - old_text.length,
                            this.cursor_pos, new_text, candidates);
-      this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
     },
 
     del: function (pos)
@@ -2718,8 +2785,6 @@ MIM.im_domain.DefType (MIM.State.prototype);
          if (pos > this.cursor_pos)
            this.preedit_replace (this.cursor_pos, pos, '', null);
        }
-      if (deleted != 0)
-       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
       return deleted;
     },
 
@@ -2775,7 +2840,6 @@ MIM.im_domain.DefType (MIM.State.prototype);
        if (this.key_head > this.keys.val.length)
          this.key_head = this.keys.val.length;
       }
-      Xex.Log ('0: key head = ' + this.key_head); 
     },
 
     pop: function ()
@@ -2812,7 +2876,7 @@ MIM.im_domain.DefType (MIM.State.prototype);
            {
              this.commit ();
              this.keys.val.splice (0, this.key_head);
-             this.key_head = 0;
+             this.key_head = this.state_key_head = this.commit_key_head = 0;
              this.prev_state = null;
            }
        }
@@ -2834,6 +2898,7 @@ MIM.im_domain.DefType (MIM.State.prototype);
     {
       if (! this.active)
        {
+         Xex.Log ("active = false");
          this.key_unhandled = true;
          this.unhandled_key = key;
          return false;
@@ -2875,17 +2940,7 @@ MIM.im_domain.DefType (MIM.State.prototype);
        {
          this.keys.val.splice (0, this.commit_key_head);
          this.key_head -= this.commit_key_head;
-         if (this.key_head < 0)
-           {
-             Xex.Log ('RECOVER key_head');
-             this.key_head = 0;
-           }
          this.state_key_head -= this.commit_key_head;
-         if (this.state_key_head < 0)
-           {
-             Xex.Log ('RECOVER state_key_head');
-             this.state_key_head = 0;
-           }
          this.commit_key_head = 0;
        }
       if (this.key_unhandled)
@@ -2894,6 +2949,13 @@ MIM.im_domain.DefType (MIM.State.prototype);
          //this.keys.val.splice (0, this.keys.val.length);
          this.key_head = this.state_key_head = this.commit_key_head = 0;
        }
+      if (this.changed & MIM.ChangedStatus.Candidate)
+       {
+         if (this.candidate_show && this.candidates)
+           MIM.show (this);
+         else
+           MIM.hide (this);
+       }
       return (! this.key_unhandled
              && this.produced.length == 0);
     }
@@ -2964,12 +3026,12 @@ MIM.im_domain.DefType (MIM.State.prototype);
   keys[0xF0] = "capslock";
 
   var keyids = {};
-  keyids['U+0008'] = 'backspace';
-  keyids['U+0009'] = 'tab';
-  keyids['U+0018'] = 'cancel';
-  keyids['U+001B'] = 'escape';
-  keyids['U+0020'] = 'space';
-  keyids['U+007F'] = 'delete';
+  keyids['U+0008'] = 'Backspace';
+  keyids['U+0009'] = 'Tab';
+  keyids['U+0018'] = 'Cancel';
+  keyids['U+001B'] = 'Escape';
+  keyids['U+0020'] = 'Space';
+  keyids['U+007F'] = 'Delete';
 
   var modifiers = {}
   modifiers.Shift = 1;
@@ -2999,8 +3061,8 @@ MIM.im_domain.DefType (MIM.State.prototype);
              return false;
            key = String.fromCharCode (parseInt (RegExp.$1, 16));
          }
-       else
-         key = key.toLowerCase ();
+       //else
+       //key = key.toLowerCase ();
        if (event.shiftKey) mod += 'S-';
        return new MIM.Key (mod + key);
       }
@@ -3101,34 +3163,108 @@ MIM.get_range = function (target, ic)
       from = rr.text.length - r.text.length;
       to = rr.text.length;
     }
-  if (ic.range[0] == from && ic.range[1] == to
-      && (to == from || target.value.substring (from, to) == ic.preedit))
+  if (from == to
+      && from == ic.range[0] + ic.cursor_pos
+      && (ic.preedit.length == 0
+         || ic.preedit == target.value.substring (ic.range[0], ic.range[1])))
     return true;
+  Xex.Log ('reset ic');
+  ic.reset ();
   ic.range[0] = from;
   ic.range[1] = to;
   return false;
-}
+};
+
+(function () {
+  var temp;
+
+  var style_props = {
+    width: 'width',
+    height: 'height',
+    padingLeft: 'padding-left',
+    paddingRight: 'padding-right',
+    paddingTop: 'padding-top',
+    paddintBottom: 'padding-bottom', 
+    borderLeftStyle: 'border-left-style',
+    borderRightStyle: 'border-right-style',
+    borderTopStyle: 'border-top-style',
+    borderBottomStyle: 'border-bottom-style',
+    borderLeftWidth: 'border-left-width',
+    borderRightWidth: 'border-right-width',
+    borderTopWidth: 'border-top-width',
+    borderBottomWidth: 'border-bottom-width',
+    fontFamily: 'font-family',
+    fontSize: 'font-size',
+    lineHeight: 'line-height',
+    letterSpacing: 'letter-spacing',
+    wordSpacing: 'word-spacing' };
+
+  function copy_style (from, to)
+  {
+    var from_style = getComputedStyle(from,'');
+    for(var name in style_props)
+      to.style[name] = from_style.getPropertyValue (style_props[name]);
+    to.style.left = from.offsetLeft + 'px'; 
+    to.style.top = from.offsetTop + 'px';
+    to.style.width = from.offsetWidth;
+    to.style.height = from.offsetHeight;
+  }
+
+  MIM.get_preedit_pos = function (target, ic)
+  {
+    if (! temp)
+      {
+       temp = document.createElement ('div');
+       temp.style.visibility = 'hidden';
+       temp.style.position = 'absolute';
+       temp.appendChild (document.createElement ('span'));
+       temp.appendChild (document.createElement ('span'));
+       document.getElementsByTagName ('body')[0].appendChild (temp);
+      }
+    if (temp.ic != ic)
+      {
+       temp.ic = ic;
+       copy_style (target, temp);
+       ic.abs_top = 0;
+       ic.abs_left = 0;
+       for (var elm = target.offsetParent; elm; elm = elm.offsetParent)
+         {
+           ic.abs_top += elm.offsetTop;
+           ic.abs_left += elm.offsetLeft;
+         }
+      }
+    temp.firstChild.innerText = target.value.substr (0, ic.range[0]);
+    temp.lastChild.innerText = "." + target.value.substr (ic.range[0], ic.range[1]);
+    ic.abs_y = (ic.abs_top + temp.lastChild.offsetTop
+               + temp.lastChild.offsetHeight - target.scrollTop);
+    ic.abs_x0 = ic.abs_left + temp.lastChild.offsetLeft;
+    ic.abs_x1 = ic.abs_x0 + temp.lastChild.offsetWidth;
+  }
+}) ();
 
 MIM.set_caret = function (target, ic)
 {
-  if (target.setSelectionRange)        // Mozilla
-    {
-      var scrollTop = target.scrollTop;
-      target.setSelectionRange (ic.range[0], ic.range[1]);
-      target.scrollTop = scrollTop;
-    }
-  else                         // IE
+  if (ic.preedit.length > 0)
     {
-      var range = target.createTextRange ();
-      range.moveStart ('character', ic.range[0]);
-      range.moveEnd ('character', ic.range[1]);
-      range.select ();
+      MIM.get_preedit_pos (target, ic);
+      if (! ic.bar)
+       {
+         ic.bar = document.createElement ('div');
+         ic.bar.style.position = 'absolute';
+         ic.bar.style.backgroundColor = "black";
+         ic.bar.style.minHeight = '1px';
+         document.getElementsByTagName ('body')[0].appendChild (ic.bar);
+       }
+      ic.bar.style.display = 'block'
+      ic.bar.style.top = (ic.abs_y + 1) + 'px';
+      ic.bar.style.left = ic.abs_x0 + 'px';
+      ic.bar.style.minWidth = (ic.abs_x1 - ic.abs_x0) + 'px';
     }
+  else if (ic.bar)
+    ic.bar.style.display = 'none'
 };
 
-MIM.ignore_focus = false;
-
-MIM.update = function (target, ic)
+MIM.update = function (target, ic, for_focus_out)
 {
   var text = target.value;
   target.value = (text.substring (0, ic.range[0])
@@ -3138,51 +3274,109 @@ MIM.update = function (target, ic)
   ic.range[0] += ic.produced.length;
   ic.range[1] = ic.range[0] + ic.preedit.length;
   MIM.set_caret (target, ic);
-  target.wait_update = false;
+  if (! for_focus_out)
+    {
+      var pos = ic.range[0] + ic.cursor_pos;
+      if (target.setSelectionRange) // Mozilla
+       {
+         var scrollTop = target.scrollTop;
+         target.setSelectionRange (pos, pos);
+         target.scrollTop = scrollTop;
+       }
+      else                     // IE
+       {
+         var range = target.createTextRange ();
+         range.moveStart ('character', pos);
+         range.moveEnd ('character', pos);
+         range.select ();
+       }
+    }
 };
 
+(function () {
+  MIM.show = function (ic)
+  {
+    if (! ic.candidates)
+      return;
+    var target = ic.target;
+    MIM.get_preedit_pos (target, ic);
+    if (! ic.can_node)
+      {
+       ic.can_node = document.createElement ('table');
+       ic.can_node.style.position = 'absolute';
+       ic.can_node.style.display = 'none';
+       ic.can_node.style.backgroundColor = "white";
+       ic.can_node.style.border = "1px solid black";
+       document.getElementsByTagName ('body')[0].appendChild (ic.can_node);
+      }
+
+    if (ic.changed & MIM.ChangedStatus.CandidateList)
+      {
+       while (ic.can_node.childNodes.length > 0)
+         ic.can_node.removeChild (ic.can_node.firstChild);
+       var tr = document.createElement ('tr');
+       var group = ic.candidates.CurrentGroup ();
+       var td = document.createElement ('td');
+       td.innerHTML = group[0][1] + '/' + group[0][0];
+       td.style.color = 'white';
+       td.style.backgroundColor = 'black';
+       tr.appendChild (td);
+       for (var i = 1; i < group.length; i++)
+         {
+           var td = document.createElement ('td');
+           td.noWrap = true;
+           td.innerHTML = (i < 10 ? i : i == 10 ? '0' : String.fromCharCode (0x60 + (i - 10))) + '.' + group[i];
+           if (i == group[0][2] + 1)
+             td.style.backgroundColor = 'lightblue';
+           tr.appendChild (td);
+         }
+       ic.can_node.appendChild (tr);
+       ic.can_node.style.top = (ic.abs_y + 10) + 'px';
+       ic.can_node.style.left = ic.abs_x0 + 'px';
+      }
+    else
+      {
+       var td = ic.can_node.firstElement ().firstElement ().nextElement ();
+       var col = ic.candidates.CurrentCol ();
+       for (var i = 0; td; td = td.nextElement ())
+         td.style.backgroundColor = (i++ == col ? 'lightblue' : 'white');
+      }
+    ic.can_node.style.display = 'block';
+  }
+
+  MIM.hide = function (ic)
+  {
+    if (ic.can_node)
+      ic.can_node.style.display = 'none';
+  }
+}) ();
+
 MIM.focus_in = function (event)
 {
   var target = event.target;
   var ic = target.mim_ic;
-  if (target.wait_focus_in);
-    {
-      Xex.Log ("Focus in " + target.tagName + ' IGNORED');
-      event.preventDefault ();
-      return;
-    }
   Xex.Log ("Focus in " + target.tagName);
+  MIM.get_range (target, ic)
   ic.Filter (MIM.Key.FocusIn);
-  //target.removeEventListener (target, 'focus', MIM.focus_in);
-  target.wait_update = true;
-  setTimeout (MIM.update (target, ic), 100);
+  setTimeout (function () { MIM.update (target, ic, false); }, 100);
+  return true;
 }
 
 MIM.focus_out = function (event)
 {
   var target = event.target;
   var ic = target.mim_ic;
-  if (target.wait_focus_out)
-    {
-      Xex.Log ("Focus out " + target.tagName + ' IGNORED');
-      event.preventDefault ();
-      return;
-    }
   Xex.Log ("Focus out " + target.tagName);
   ic.Filter (MIM.Key.FocusOut);
-  //target.removeEventListener (target, 'blur', MIM.focus_out);
-  target.wait_update = true;
-  setTimeout (MIM.update (target, ic), 100);
+  MIM.update (target, ic, true);
+  return true;
 };
 
 MIM.keydown = function (event)
 {
   var target = event.target;
-  if (target.id == 'log')
-    return;
   if (! (target.type == "text" || target.type == "textarea"))
     return;
-  document.akey = event;
 
   var ic = target.mim_ic;
   if (! ic || ic.im != MIM.current)
@@ -3191,30 +3385,23 @@ MIM.keydown = function (event)
       Xex.Log ('creating IC');
       ic = new MIM.IC (MIM.current, target);
       if (ic.im.load_status != MIM.LoadStatus.Loaded)
-       return;
+       return true;
       target.mim_ic = ic;
       MIM.add_event_listener (target, 'focus', MIM.focus_in);
       MIM.add_event_listener (target, 'blur', MIM.focus_out);
-      MIM.get_range (target, ic)
-    }
-  else
-    {
-      while (target.wait_update);
-      if (! MIM.get_range (target, ic))
-       ic.reset ();
     }
+  MIM.get_range (target, ic)
   MIM.debug_print (event, ic);
   ic.key = MIM.decode_key_event (event);
   if (ic.key)
     {
-      Xex.Log ("filtering " + ic.key);
       try {
        var result = ic.Filter (ic.key);
       } catch (e) {
        Xex.Log ('Error' + e);
        throw (e);
       }
-      MIM.update (target, ic);
+      MIM.update (target, ic, false);
       if (! ic.key_unhandled)
        event.preventDefault ();
     }
@@ -3223,8 +3410,6 @@ MIM.keydown = function (event)
 MIM.keypress = function (event)
 {
   var target = event.target;
-  if (target.id == 'log')
-    return;
   if (! (target.type == "text" || target.type == "textarea"))
     return;
 
@@ -3242,14 +3427,13 @@ MIM.keypress = function (event)
        return;
       }
     
-    Xex.Log ("filtering " + ic.key);
     try {
       var result = ic.Filter (ic.key);
     } catch (e) {
       Xex.Log ('Error:' + e);
       throw (e);
     }
-    MIM.update (target, ic);
+    MIM.update (target, ic, false);
     if (! ic.key_unhandled)
       event.preventDefault ();
   } catch (e) {
@@ -3258,6 +3442,7 @@ MIM.keypress = function (event)
   } finally {
     MIM.debug_print (event, ic);
   }
+
   return;
 };
 
@@ -3279,7 +3464,6 @@ MIM.keypress = function (event)
       sk: { name: 'Slovak' },
       sr: { name: 'Serbian' },
       sv: { name: 'Swedish' },
-      vi: { name: 'Vietnamese' },
       yi: { name: 'Yiddish' } },
     MiddleEast: {
       ar: { name: 'Arabic' },
@@ -3346,12 +3530,10 @@ MIM.keypress = function (event)
              list = lang_category[cat][lang].list;
              if (! list)
                list = lang_category[cat][lang].list = {};
-             break;
+             for (name in MIM.imlist[lang])
+               list[name] = MIM.imlist[lang][name];
            }
-       if (list)
-         for (name in MIM.imlist[lang])
-           list[name] = MIM.imlist[lang][name];
-       else
+       if (! list)
          for (name in MIM.imlist[lang])
            Xex.Log ('no category ' + lang + '-' + name);
       }
@@ -3380,18 +3562,23 @@ MIM.keypress = function (event)
   function destroy_menu () {
     if (! destroy_timer)
       destroy_timer = setTimeout (destroy, 1000);
+    return true;
   }
 
   function show_submenu (event)
   {
+    var target = event.target;
+    if (! target.menu_level)
+      {
+       if (! target.parentNode || ! target.parentNode.menu_level)
+         return true;
+       target = target.parentNode;
+      }
     if (destroy_timer)
       {
        clearTimeout (destroy_timer);
        destroy_timer = null;
       }
-    var target = event.target;
-    if (! target.menu_level)
-      return;
     if (last_target && target.parentLi != last_target)
       {
        last_target.style.backgroundColor = 'white';
@@ -3417,6 +3604,15 @@ MIM.keypress = function (event)
   function select_im (event)
   {
     var target = event.target;
+    if (! target.im)
+      {
+       if (! target.parentNode || ! target.parentNode.menu_level)
+         {
+           event.preventDefault ();
+           return false;
+         }
+       target = target.parentNode;
+      }
     if (target.im)
       {
        MIM.current = target.im;
@@ -3449,7 +3645,9 @@ MIM.keypress = function (event)
     li.style.top = '0px';
     li.style.listStyle = 'none';
     li.menu_level = level;
-    li.appendChild (document.createTextNode (text));
+    var nobr = document.createElement ('nobr');
+    nobr.innerHTML = text;
+    li.appendChild (nobr);
     return li;
   }
 
@@ -3469,7 +3667,7 @@ MIM.keypress = function (event)
        menu.style.fontFamily = 'sans-serif';
        menu.style.fontWeight = 'bold';
        menu.id = 'mim-menu';
-       menu.onclick = select_im;
+       menu.onmousedown = select_im;
        menu.onmouseover = show_submenu;
        menu.onmouseout = destroy_menu;
        for (var catname in lang_category)
@@ -3499,7 +3697,6 @@ MIM.keypress = function (event)
            li.appendChild (sub);
            menu.appendChild (li);
          }
-       document.mimmenu = menu;
        lang_category = null;
       }
     menu.style.left = (event.clientX - 10) + "px";
@@ -3514,7 +3711,7 @@ MIM.keypress = function (event)
     MIM.add_event_listener (window, 'mousedown', create_menu);
     if (window.location == 'http://localhost/mim/index.html')
       MIM.server = 'http://localhost/mim';
-    MIM.current = MIM.imlist['zh']['tonepy'];
+    MIM.current = MIM.imlist['zh']['py-gb'];
   };
 }) ();
 
@@ -3544,7 +3741,7 @@ MIM.test = function ()
 MIM.init_debug = function ()
 {
   MIM.debug = true;
-  Xex.LogNode = document.getElementById ('log');
+  Xex.LogNode = document.getElementById ('xexlog');
   Xex.Log (null);
   MIM.init ();
 };