*** empty log message ***
[m17n/m17n-lib-js.git] / xex.js
diff --git a/xex.js b/xex.js
index 68e38df..c9199a0 100644 (file)
--- a/xex.js
+++ b/xex.js
@@ -6,11 +6,16 @@ var Xex = {
   {
     if (! Xex.LogNode)
       return;
-    var str = '';
-    if (indent != undefined)
-      for (var i = 0; i <= indent; i++)
-       str += '  ';
-    Xex.LogNode.value = str + arg + "\n" + Xex.LogNode.value;
+    if (! arg)
+      Xex.LogNode.value = '';
+    else
+      {
+       var str = '';
+       if (indent != undefined)
+         for (var i = 0; i <= indent; i++)
+           str += '  ';
+       Xex.LogNode.value = str + arg + "\n" + Xex.LogNode.value;
+      }
   }
 };
 
@@ -1300,6 +1305,8 @@ var MIM = {
   keysyms["del"] = "delete";
 
   function decode_keysym (str) {
+    if (str.length == 1)
+      return str;
     var parts = str.split ("-");
     var len = parts.length, i;
     var has_modifier = len > 1;
@@ -2818,22 +2825,64 @@ MIM.im_domain.DefType (MIM.State.prototype);
   keys[0x90] = "numlock";
   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';
+
+  var modifiers = {}
+  modifiers.Shift = 1;
+  modifiers.Control = 1;
+  modifiers.Alt = 1;
+  modifiers.AltGraph = 1;
+  modifiers.Meta = 1
+
   MIM.decode_key_event = function (event)
   {
-    var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
+    var key = event.keyIdentifier;
+
+    if (key)                   // keydown event of Chrome
+      {
+       if (modifiers[key])
+         return false;
+       var mod = '';
+       if (event.ctrlKey) mod += 'C-';
+       if (event.metaKey) mod += 'M-';
+       if (event.altKey) mod += 'A-';
+       var keysym = keyids[key];
+       if (keysym)
+         key = keysym;
+       else if (key.match(/^U\+([0-9A-Z]+)$/))
+         {
+           if (mod.length == 0)
+             return;
+           key = String.fromCharCode (parseInt (RegExp.$1, 16));
+         }
+       else
+         key = key.toLowerCase ();
+       if (event.shiftKey) mod += 'S-';
+       return new MIM.Key (mod + key);
+      }
+    else
+      {
+       key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
               : event.charCode ? event.charCode
               : false);
-    if (! key)
-      return false;
-    if (event.type == 'keydown')
-      {
-       key = keys[key];
        if (! key)
          return false;
-       if (event.shiftKey) key = "S-" + key ;
+       if (event.type == 'keydown')
+         {
+           key = keys[key];
+           if (! key)
+             return false;
+           if (event.shiftKey) key = "S-" + key ;
+         }
+       else
+         key = String.fromCharCode (key);
       }
-    else
-      key = String.fromCharCode (key);
     if (event.altKey) key = "A-" + key ;
     if (event.ctrlKey) key = "C-" + key ;
     return new MIM.Key (key);
@@ -2864,10 +2913,10 @@ MIM.debug_print = function (event, ic)
   if (! MIM.debug_nodes)
     {
       MIM.debug_nodes = new Array ();
-      MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
-      MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
       MIM.debug_nodes['status0'] = document.getElementById ('status0');
       MIM.debug_nodes['status1'] = document.getElementById ('status1');
+      MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
+      MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
       MIM.debug_nodes['keymap0'] = document.getElementById ('keymap0');
       MIM.debug_nodes['keymap1'] = document.getElementById ('keymap1');
       MIM.debug_nodes['preedit0'] = document.getElementById ('preedit0');
@@ -2875,11 +2924,11 @@ MIM.debug_print = function (event, ic)
     }
   var target = event.target;
   var code = event.keyCode;
-  var ch = event.type == 'keydown' ? 0 : event.charCode;
+  var ch = event.type == 'keypress' ? event.charCode : 0;
   var key = MIM.decode_key_event (event);
   var index;
 
-  MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
+  MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + ":" + key + '/' + event.keyIdentifier;
   index = (event.type == 'keydown' ? '0' : '1');
   if (ic)
     MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status;
@@ -2990,6 +3039,14 @@ MIM.keydown = function (event)
     }
   MIM.debug_print (event, ic);
   ic.key = MIM.decode_key_event (event);
+  if (ic.key)
+    {
+      Xex.Log ("filtering " + ic.key);
+      var result = ic.Filter (ic.key);
+      MIM.update (target, ic);
+      if (! ic.key_unhandled)
+       event.preventDefault ();
+    }
 };
 
 MIM.keypress = function (event)
@@ -3019,230 +3076,271 @@ MIM.keypress = function (event)
     MIM.update (target, ic);
     if (! ic.key_unhandled)
       event.preventDefault ();
+  } catch (e) {
+    Xex.Log ("error:" + e);
+    event.preventDefault ();
   } finally {
     MIM.debug_print (event, ic);
   }
   return;
 };
 
-MIM.Lang = {
-  European: new Array ('de', 'fr'),
-  MiddleEast: new Array ('ar', 'he'),
-  SouthAsia: new Array ('hi'),
-  SouthEastAsia: new Array ('th'),
-  EastAsia: new Array ('ja', 'zh'),
-  Other: new Array ()
-};
+(function () {
+  var lang_category = {
+    European: {
+      cs: { name: 'Czech' },
+      da: { name: 'Danish' },
+      el: { name: 'Greek' },
+      en: { name: 'English' },
+      eo: { name: 'Esperanto' },
+      fr: { name: 'French' },
+      grc: { name: 'ClassicGreek' },
+      hr: { name: 'Croatian' },
+      hy: { name: 'Armenian' },
+      ka: { name: 'Georgian' },
+      kk: { name: 'Kazakh' },
+      ru: { name: 'Russian' },
+      sk: { name: 'Slovak' },
+      sr: { name: 'Serbian' },
+      sv: { name: 'Swedish' },
+      vi: { name: 'Vietnamese' },
+      yi: { name: 'Yiddish' } },
+    MiddleEast: {
+      ar: { name: 'Arabic' },
+      dv: { name: 'Divehi' },
+      fa: { name: 'Persian' },
+      he: { name: 'Hebrew' },
+      kk: { name: 'Kazakh' },
+      ps: { name: 'Pushto' },
+      ug: { name: 'Uighur' },
+      yi: { name: 'Yiddish' } },
+    SouthAsia: {
+      as: { name: 'Assamese' },
+      bn: { name: 'Bengali' },
+      bo: { name: 'Tibetan' },
+      gu: { name: 'Gujarati' },
+      hi: { name: 'Hindi' },
+      kn: { name: 'Kannada' },
+      ks: { name: 'Kashmiri' },
+      ml: { name: 'Malayalam' },
+      mr: { name: 'Marathi' },
+      ne: { name: 'Nepali' },
+      or: { name: 'Oriya' },
+      pa: { name: 'Panjabi' },
+      sa: { name: 'Sanskirit' },
+      sd: { name: 'Sindhi' },
+      si: { name: 'Sinhalese' },
+      ta: { name: 'Tamil' },
+      te: { name: 'Telugu' },
+      ur: { name: 'Urdu' } },
+    SouthEastAsia: {
+      cmc: { name: 'Cham' },
+      km: { name: 'Khmer'},
+      lo: { name: 'Lao' },
+      my: { name: 'Burmese' },
+      tai: { name: 'Tai Viet' },
+      th: { name: 'Thai' },
+      vi: { name: 'Vietanamese' } },
+    EastAsia: {
+      ii: { name: 'Yii' },
+      ja: { name: 'Japanese' },
+      ko: { name: 'Korean' },
+      zh: { name: 'Chinese' } },
+    Other: {
+      am: { name:  'Amharic' },
+      ath: { name: 'Carrier' },
+      bla: { name: 'Blackfoot' },
+      cr: { name: 'Cree' },
+      eo: { name: 'Esperanto' },
+      iu: { name: 'Inuktitut' },
+      nsk: { name: 'Naskapi' },
+      oj: { name: 'Ojibwe' },
+      t: { name: 'Generic' } }
+  };
+
+  function categorize_im ()
+  {
+    var cat, lang, list, name;
+    for (lang in MIM.imlist)
+      {
+       list = null;
+       for (cat in lang_category)
+         if (lang_category[cat][lang])
+           {
+             list = lang_category[cat][lang].list;
+             if (! list)
+               list = lang_category[cat][lang].list = {};
+             break;
+           }
+       if (list)
+         for (name in MIM.imlist[lang])
+           list[name] = MIM.imlist[lang][name];
+       else
+         for (name in MIM.imlist[lang])
+           Xex.Log ('no category ' + lang + '-' + name);
+      }
+  }
 
-// Other
-// am
-// ath
-// bla
-// cr
-// el
-// eo
-// iu
-// nsk
-// oj
-
-// Middle Eastern
-// ar
-// dv
-// fa
-// he
-// hi
-// kk
-// ps
-// ug
-// yi*
-
-// South Asian
-// as
-// bn
-// bo
-// gu
-// kn
-// ks
-// ml
-// mr
-// ne
-// or
-// pa
-// sd
-// sa
-// si
-// ta
-// te
-// ur
-
-// European
-// cs
-// da
-// eo
-// fr
-// hr
-// hy
-// kk
-// ru
-// sk
-// sr
-// sv
-// vi* 
-// yi*
-
-// East Asian
-// ii
-// ja
-// ko
-// zh
-
-// SouthEast Asian
-// km
-// lo
-// my
-// tai
-// th
-// vi*
-
-MIM.select_im = function (event)
-{
-  var target = event.target.parentNode;
-  while (target.tagName != "mim-select-im")
-    target = target.parentNode;
-  var idx = 0;
-  var im = false;
-  for (var lang in MIM.imlist)
-    for (var name in MIM.imlist[lang])
-      if (idx++ == target.selectedIndex)
-       {
-         im = MIM.imlist[lang][name];
-         break;
-       }
-  document.getElementsByTagName ('body')[0].removeChild (target);
-  target.target.focus ();
-  if (im && im != MIM.current)
-    {
-      MIM.current = im;
-      Xex.Log ('select IM: ' + im.name);
-    }
-};
+  var destroy_timer;
+  var last_target;
 
-MIM.destroy_menu = function (event)
-{
-  if (event.target.tagName == "mim-select-im")
-    document.getElementsByTagName ('body')[0].removeChild (event.target);
-};
+  function destroy ()
+  {
+    clearTimeout (destroy_timer);
+    destroy_timer = null;
+    var target = document.getElementById ('mim-menu');
+    if (target)
+      {
+       for (; last_target && last_target.menu_level;
+            last_target = last_target.parentLi)
+         last_target.style.backgroundColor = 'white';
+       var nodes = target.getElementsByTagName ('ul');
+       for (var i = 0; i < nodes.length; i++)
+         nodes[i].style.visibility = 'hidden';
+       document.getElementsByTagName ('body')[0].removeChild (target);
+      }
+  }    
 
-MIM.select_menu = function (event)
-{
-  var target = event.target;
-  var sel;
+  function destroy_menu () {
+    if (! destroy_timer)
+      destroy_timer = setTimeout (destroy, 1000);
+  }
 
-  if (! ((target.type == "text" || target.type == "textarea")
-        && event.which == 1 && event.ctrlKey))
-    return;
-  if (! sel)
-    {
-      sel = document.createElement ('select');
-      sel.onclick = MIM.select_im;
-      sel.onmouseout = MIM.destroy_menu;
-      sel.style.position='absolute';
-      sel.style.left = (event.clientX - 10) + "px";
-      sel.style.top = (event.clientY - 10) + "px";
-      sel.target = target;
-      var idx = 0;
-      for (var lang in MIM.imlist)
-       for (var name in MIM.imlist[lang])
+  function show_submenu (event)
+  {
+    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';
+       if (target.menu_level < last_target.menu_level)
          {
-           var option = document.createElement ('option');
-           var imname = lang + "-" + name;
-           option.appendChild (document.createTextNode (imname));
-           option.value = imname;
-           sel.appendChild (option);
-           if (MIM.imlist[lang][name] == MIM.current)
-             sel.selectedIndex = idx;
-           idx++;
+           last_target = last_target.parentLi;
+           last_target.style.backgroundColor = 'white';
          }
-      sel.size = idx;
-    }
-  document.getElementsByTagName ('body')[0].appendChild (sel);
-};
+       var uls = last_target.getElementsByTagName ('ul');
+       for (var i = 0; i < uls.length; i++)
+         uls[i].style.visibility = 'hidden';
+      }
+    last_target = target;
+    target.style.backgroundColor = 'yellow';
+    if (target.menu_level < 3)
+      {
+       target.lastChild.style.visibility = 'visible';
+       target.lastChild.style.left = target.clientWidth + 'px';
+      }
+    event.preventDefault ();   
+  }
+
+  function select_im (event)
+  {
+    var target = event.target;
+    if (target.im)
+      {
+       MIM.current = target.im;
+       destroy ();
+      }
+    event.preventDefault ();
+  }
+
+  function create_ul (visibility)
+  {
+    var ul = document.createElement ('ul');
+    ul.style.position = 'absolute';
+    ul.style.margin = '0px';
+    ul.style.padding = '0px';
+    ul.style.border = '1px solid gray';
+    ul.style.borderBottom = 'none';
+    ul.style.top = '-1px';
+    ul.style.backgroundColor = 'white';
+    ul.style.visibility = visibility;
+    return ul;
+  }
+
+  function create_li (level, text)
+  {
+    var li = document.createElement ('li');
+    li.style.position = 'relative';
+    li.style.margin = '0px';
+    li.style.padding = '1px';
+    li.style.borderBottom = '1px solid gray';
+    li.style.top = '0px';
+    li.style.listStyle = 'none';
+    li.menu_level = level;
+    li.appendChild (document.createTextNode (text));
+    return li;
+  }
 
-MIM.select_menu = function (event)
-{
-  var target = event.target;
   var menu;
 
-  if (! ((target.type == "text" || target.type == "textarea")
-        && event.which == 1 && event.ctrlKey))
-    return;
-  if (! menu)
-    {
-      menu = document.createElement ('ul');
-      menu.style.margin = '0';
-      menu.style.padding = '0';
-      menu.id = 'mim-select-im';
-      menu.onclick = MIM.select_im;
-      menu.onmouseout = MIM.destroy_menu;
-      menu.style.position='absolute';
-      menu.style.left = (event.clientX - 10) + "px";
-      menu.style.top = (event.clientY - 10) + "px";
-      menu.style['border-bottom'] = '1px solid #ccc';
-      menu.style['background-color'] = 'white';
-      menu.target = target;
-      var idx = 0;
-      for (var lang in MIM.imlist)
-       {
-         var li = document.createElement ('li');
-         li.style.position = 'relative';
-         li.style['list-style']= 'none';
-         li.style.margin = '0';
-         li.style.padding = '0';
-         li.onmouseover = function ()
+  function create_menu (event)
+  {
+    var target = event.target;
+
+    if (! ((target.type == "text" || target.type == "textarea")
+          && event.which == 1 && event.ctrlKey))
+      return;
+    if (! menu)
+      {
+       categorize_im ();
+       menu = create_ul ('visible');
+       menu.style.fontFamily = 'sans-serif';
+       menu.style.fontWeight = 'bold';
+       menu.id = 'mim-menu';
+       menu.onclick = select_im;
+       menu.onmouseover = show_submenu;
+       menu.onmouseout = destroy_menu;
+       for (var catname in lang_category)
          {
-           this.style.backgroundColor = 'yellow';
-           var children = this.getElementsByTagName ('ul');
-           for (var i = children.length - 1; i >= 0; i--)
-             {
-               children[i].display = 'block';
-               children[i].visibility = 'visible';
-               children[i].left = '100px';
-             }
-           children = this.getElementsByTagName ('li');
-           for (var i = children.length - 1; i >= 0; i--)
+           var cat = lang_category[catname];
+           var li = create_li (1, catname);
+           var sub = create_ul ('hidden');
+           for (var langname in cat)
              {
-               children[i].display = 'block';
-               children[i].visibility = 'visible';
-               children[i].left = '100px';
+               var lang = cat[langname];
+               if (! lang.list)
+                 continue;
+               var sub_li = create_li (2, lang.name);
+               sub_li.parentLi = li;
+               var subsub = create_ul ('hidden');
+               for (var name in lang.list)
+                 {
+                   var im = lang.list[name];
+                   var subsub_li = create_li (3, im.name);
+                   subsub_li.parentLi = sub_li;
+                   subsub_li.im = im;
+                   subsub.appendChild (subsub_li);
+                 }
+               sub_li.appendChild (subsub);
+               sub.appendChild (sub_li);
              }
-         };
-         li.onmouseout = function () { this.style.backgroundColor = 'white'; };
-         li.appendChild (document.createTextNode (lang));
-         var sub = document.createElement ('ul');
-         sub.style.position = 'absolute';
-         sub.style.top = '0px';
-         sub.style.visibility = 'hidden';
-         li.appendChild (sub);
-         for (var name in MIM.imlist[lang])
-           {
-             var sub_li = document.createElement ('li');
-             var imname = '  ' + lang + "-" + name;
-             sub_li.style.position = 'absolute';
-             sub_li.style.top = '0px';
-             sub_li.style['list-style']= 'none';
-             //sub_li.style.visibility = 'hidden';
-             sub_li.appendChild (document.createTextNode (imname));
-             sub.appendChild (sub_li);
-             if (MIM.imlist[lang][name] == MIM.current)
-               menu.selectedIndex = idx;
-             idx++;
-           }
-         menu.appendChild (li);
-       }
-      menu.size = idx;
-    }
-  document.getElementsByTagName ('body')[0].appendChild (menu);
-};
+           li.appendChild (sub);
+           menu.appendChild (li);
+         }
+       document.mimmenu = menu;
+       lang_category = null;
+      }
+    menu.style.left = (event.clientX - 10) + "px";
+    menu.style.top = (event.clientY - 10) + "px";
+    document.getElementsByTagName ('body')[0].appendChild (menu);
+  };
+
+  MIM.init = function ()
+  {
+    MIM.add_event_listener (window, 'keydown', MIM.keydown);
+    MIM.add_event_listener (window, 'keypress', MIM.keypress);
+    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['vi']['telex'];
+  };
+}) ();
 
 MIM.test = function ()
 {
@@ -3267,19 +3365,10 @@ MIM.test = function ()
 }
 
 
-MIM.init = function ()
-{
-  MIM.add_event_listener (window, 'keydown', MIM.keydown);
-  MIM.add_event_listener (window, 'keypress', MIM.keypress);
-  MIM.add_event_listener (window, 'mousedown', MIM.select_menu);
-  if (window.location == 'http://localhost/mim/index.html')
-    MIM.server = 'http://localhost/mim';
-  MIM.current = MIM.imlist['vi']['telex'];
-};
-
 MIM.init_debug = function ()
 {
   MIM.debug = true;
   Xex.LogNode = document.getElementById ('log');
+  Xex.Log (null);
   MIM.init ();
 };