X-Git-Url: http://git.chise.org/gitweb/?p=m17n%2Fm17n-lib-js.git;a=blobdiff_plain;f=xex.js;h=46d510fb983f1b53a16bde280ea016620f5a18ee;hp=632733ec2af78951c435808d8129c52e48e945e4;hb=c88986c81d58c3158777688d0bd80f9f00ed9076;hpb=83a13979d1689d61fab99bc59e65948ae5a9b186 diff --git a/xex.js b/xex.js index 632733e..46d510f 100644 --- a/xex.js +++ b/xex.js @@ -1,6 +1,36 @@ // -* coding: utf-8; -* -var Xex = {}; +var Xex = { + LogNode: null, + LogNodeLen: 0, + Log: function (arg, indent, cont) + { + if (! Xex.LogNode) + return; + if (! arg) + { + while (Xex.LogNode.childNodes.length > 0) + Xex.LogNode.removeChild (Xex.LogNode.firstChild); + LogNodeLen = 0; + } + else + { + var node; + LogNodeLen++; + if (LogNodeLen >= 200) + node = Xex.LogNode.firstElement (); + else + node = document.createElement ('div'); + if (indent != undefined) + 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; + } + } +}; Xex.Error = { UnknownError: "unknown-error", @@ -74,7 +104,7 @@ Xex.Subrountine = function (builtin, name, with_var, min_args, max_args) Xex.Subrountine.prototype.Call = function (domain, vari, args) { - newargs = new Array (); + var newargs = new Array (); for (var i = 0; i < args.length; i++) { newargs[i] = args[i].Eval (domain); @@ -171,9 +201,9 @@ Xex.Macro.prototype.Call = function (domain, vari, args) domain.Bind (this.args[i], args[i]); try { domain.Catch (Xex.CatchTag.Return); - for (var term in body) + for (var i in this.body) { - result = term.Eval (domain); + result = this.body[i].Eval (domain); if (domain.Thrown ()) break; } @@ -232,7 +262,11 @@ Xex.Domain = function (name, parent, context) for (elt in parent.functions) this.functions[elt] = parent.functions[elt]; for (elt in parent.variables) - this.variables[elt] = parent.variables[elt]; + { + var vari = parent.variables[elt]; + this.variables[elt] = new Xex.Variable (this, vari.name, vari.desc, + vari.val, vari.range); + } } this.call_stack = new Array (); @@ -388,12 +422,7 @@ Xex.Domain.prototype = { { values = {}; for (var elt in this.variables) - { - //if (! this.variables[elt].val) - //alert ('unknown value of ' + elt); - //else - values[elt] = this.variables[elt].val.Clone (); - } + values[elt] = this.variables[elt].val.Clone (); return values; }, RestoreValues: function (values) @@ -454,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; @@ -502,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; @@ -612,9 +641,9 @@ Xex.Varref = function (vname) proto.Clone = function () { return new Xex.Varref (this.val); } proto.Eval = function (domain) { - if (! this.vari || this.vari.domain != domain) - this.vari = domain.GetVarCreate (this.val); - return this.vari.val; + var vari = domain.GetVarCreate (this.val); + Xex.Log (this.ToString () + '=>' + vari.val, domain.depth); + return vari.val; } proto.Parser = function (domain, node) @@ -622,15 +651,20 @@ Xex.Varref = function (vname) return new Xex.Varref (node.attributes['vname'].nodeValue); } + proto.ToString = function () + { + return ''; + } + Xex.Varref.prototype = proto; }) (); var null_args = new Array (); -Xex.Funcall = function (func, vari, args) +Xex.Funcall = function (func, vname, args) { this.func = func; - this.vari = vari; + this.vname = vname; this.args = args || null_args; }; @@ -645,18 +679,17 @@ Xex.Funcall = function (func, vari, args) if (fname == 'funcall') fname = node.attributes['fname'].nodeValue; var func = domain.GetFunc (fname); - var vari; + var vname; attr = node.attributes['vname']; - vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : false; + vname = attr != undefined ? attr.nodeValue : null; var args = Xex.Term.Parse (domain, node.firstElement (), null); - return new Xex.Funcall (func, vari, args); + return new Xex.Funcall (func, vname, args); } proto.New = function (domain, fname, vname, args) { var func = domain.GetFunc (fname); - var vari = vname ? domain.GetVarCreate (vname) : null; - var funcall = new Xex.Funcall (func, vari, args); + var funcall = new Xex.Funcall (func, vname, args); if (func instanceof Xex.Macro) funcall = funcall.Eval (domain); return funcall; @@ -664,7 +697,19 @@ Xex.Funcall = function (func, vari, args) proto.Eval = function (domain) { - return this.func.Call (domain, this.vari, this.args); + Xex.Log (this, domain.depth); + var vari; + if (this.vname) + vari = domain.GetVarCreate (this.vname); + domain.depth++; + var result; + try { + result = this.func.Call (domain, vari, this.args); + } finally { + Xex.Log (' => ' + result, --domain.depth, + this.func instanceof Xex.Subrountine); + } + return result; } proto.Clone = function () @@ -684,11 +729,18 @@ Xex.Funcall = function (func, vari, args) { var arglist = '' var len = this.args.length; + var str = '<' + this.func.name; + if (this.vname) + str += ' vname="' + this.vname + '"'; if (len == 0) - return '<' + this.func.name + '/>'; - for (var i = 0; i < len; i++) - arglist += this.args[i].toString (); - return '<' + this.func.name + '>' + arglist + ''; + return str + '/>'; + if (this.func instanceof Xex.Subrountine) + for (var i = 0; i < len; i++) + arglist += this.args[i].toString (); + else + for (var i = 0; i < len; i++) + arglist += '.'; + return str + '>' + arglist + ''; } Xex.Funcall.prototype = proto; @@ -769,7 +821,7 @@ Xex.StrTerm = function (str) { this.val = str; }; proto.Clone = function () { return new Xex.StrTerm (this.val); } proto.Parser = function (domain, node) { - return new Xex.StrTerm (node.firstChild.nodeValue); + return new Xex.StrTerm (node.firstChild ? node.firstChild.nodeValue : ''); } Xex.StrTerm.prototype = proto; }) (); @@ -830,19 +882,6 @@ Xex.LstTerm = function (list) { this.val = list; }; function Fset (domain, vari, args) { - return vari.SetValue (args[0]); - } - - function maybe_set_intvar (vari, n) - { - var term = new Xex.IntTerm (n); - if (vari != null) - vari.SetValue (term); - return term; - } - - function Fset (domain, vari, args) - { if (! vari) throw new Xex.ErrTerm (Xex.Error.NoVariableName, 'No variable name to set'); @@ -850,9 +889,14 @@ Xex.LstTerm = function (list) { this.val = list; }; return args[0]; } + function Fnot (domain, vari, args) + { + return (args[0].IsTrue () ? Xex.Zero : Xex.One); + } + function maybe_set_intvar (vari, n) { - var term = new IntTerm (n); + var term = new Xex.IntTerm (n); if (vari) vari.SetValue (term); return term; @@ -927,6 +971,39 @@ Xex.LstTerm = function (list) { this.val = list; }; return maybe_set_intvar (vari, n); } + function Flogand (domain, vari, args) + { + var n, i; + if (vari == null) + { + Xex.Log ("logand arg args[0]" + args[0]); + n = args[0].Intval () + i = 1; + } + else + { + Xex.Log ("logand arg var " + vari); + n = vari.val.Intval (); + i = 0; + } + while (n > 0 && i < args.length) + { + Xex.Log ("logand arg " + args[i]); + n &= args[i++].val; + } + return maybe_set_intvar (vari, n); + } + + function Flsh (domain, vari, args) + { + return maybe_set_intvar (vari, args[0].Intval () << args[1].Intval ()); + } + + function Frsh (domain, vari, args) + { + return maybe_set_intvar (vari, args[0].Intval () >> args[1].Intval ()); + } + function Fand (domain, vari, args) { var len = args.length; @@ -963,13 +1040,18 @@ Xex.LstTerm = function (list) { this.val = list; }; return Xex.One; } + function Fnoteq (domain, vari, args) + { + return (Feq (domain, vari, args) == Xex.One ? Xex.Zero : Xex.One); + } + function Flt (domain, vari, args) { - var n = args[0].Intval; + var n = args[0].Intval (); for (var i = 1; i < args.length; i++) { - var n1 = args[i].Intval; + var n1 = args[i].Intval (); if (n >= n1) return Xex.Zero; n = n1; @@ -979,10 +1061,10 @@ Xex.LstTerm = function (list) { this.val = list; }; function Fle (domain, vari, args) { - var n = args[0].Intval; + var n = args[0].Intval (); for (var i = 1; i < args.length; i++) { - var n1 = args[i].Intval; + var n1 = args[i].Intval (); if (n > n1) return Xex.Zero; n = n1; @@ -992,10 +1074,10 @@ Xex.LstTerm = function (list) { this.val = list; }; function Fgt (domain, vari, args) { - var n = args[0].Intval; + var n = args[0].Intval (); for (var i = 1; i < args.length; i++) { - var n1 = args[i].Intval; + var n1 = args[i].Intval (); if (n <= n1) return Xex.Zero; n = n1; @@ -1005,10 +1087,10 @@ Xex.LstTerm = function (list) { this.val = list; }; function Fge (domain, vari, args) { - var n = args[0].Intval; + var n = args[0].Intval (); for (var i = 1; i < args.Length; i++) { - var n1 = args[i].Intval; + var n1 = args[i].Intval (); if (n < n1) return Xex.Zero; n = n1; @@ -1047,9 +1129,9 @@ Xex.LstTerm = function (list) { this.val = list; }; { for (var i = 0; i < args.length; i++) { - var list = args[i].val; - var result = list.val[0].Eval (doamin); - if (result.isTrue ()) + var list = args[i]; + var result = list.val[0].Eval (domain); + if (result.IsTrue ()) { for (var j = 1; j < list.val.length; j++) { @@ -1136,11 +1218,9 @@ Xex.LstTerm = function (list) { this.val = list; }; Xex.BasicDomain = basic; basic.DefSubr (Fset, "set", true, 1, 1); - if (basic.functions['=']) - alert (basic.functions['=']); basic.DefAlias ("=", "set"); - //basic.DefSubr (Fnot, "not", false, 1, 1); - //basic.DefAlias ("!", "not"); + basic.DefSubr (Fnot, "not", false, 1, 1); + basic.DefAlias ("!", "not"); basic.DefSubr (Fadd, "add", true, 1, -1); basic.DefSubr (Fmul, "mul", true, 1, -1); basic.DefAlias ("*", "mul"); @@ -1152,16 +1232,16 @@ Xex.LstTerm = function (list) { this.val = list; }; basic.DefAlias ("%", "mod"); basic.DefSubr (Flogior, "logior", true, 1, -1); basic.DefAlias ('|', "logior"); - //basic.DefSubr (Flogand, "logand", true, 1, -1); - //basic.DefAlias ("&", "logand"); - //basic.DefSubr (Flsh, "lsh", true, 1, 2); - //basic.DefAlias ("<<", "lsh"); - //basic.DefSubr (Frsh, "rsh", true, 1, 2); - //basic.DefAlias (">>", "rsh"); + basic.DefSubr (Flogand, "logand", true, 1, -1); + basic.DefAlias ("&", "logand"); + basic.DefSubr (Flsh, "lsh", true, 1, 2); + basic.DefAlias ("<<", "lsh"); + basic.DefSubr (Frsh, "rsh", true, 1, 2); + basic.DefAlias (">>", "rsh"); basic.DefSubr (Feq, "eq", false, 2, -1); basic.DefAlias ("==", "eq"); - //basic.DefSubr (Fnoteq, "noteq", false, 2, 2); - //basic.DefAlias ("!=", "noteq"); + basic.DefSubr (Fnoteq, "noteq", false, 2, 2); + basic.DefAlias ("!=", "noteq"); basic.DefSubr (Flt, "lt", false, 2, -1); basic.DefAlias ("<", "lt"); basic.DefSubr (Fle, "le", false, 2, -1); @@ -1286,6 +1366,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; @@ -1322,8 +1404,9 @@ var MIM = { MIM.Key = function (val) { this.key; - this.has_modifier = false; - if (typeof val == 'string' || val instanceof String) + if (val instanceof Xex.Term) + this.key = val.val; + else if (typeof val == 'string' || val instanceof String) { this.key = decode_keysym (val); if (! this.key) @@ -1341,13 +1424,16 @@ var MIM = { } MIM.Key.prototype.toString = function () { return this.key; }; + + MIM.Key.FocusIn = new MIM.Key (new Xex.StrTerm ('input-focus-in')); + MIM.Key.FocusOut = new MIM.Key (new Xex.StrTerm ('input-focus-out')); + MIM.Key.FocusMove = new MIM.Key (new Xex.StrTerm ('input-focus-move')); }) (); (function () { MIM.KeySeq = function (seq) { this.val = new Array (); - this.has_modifier = false; if (seq) { @@ -1356,12 +1442,14 @@ var MIM = { var len = seq.val.length; for (var i = 0; i < len; i++) { - var v = seq.val[i]; - if (v.type != 'string' && v.type != 'integer' - && v.type != 'symbol') + var v = seq.val[i], key; + if (v.type == 'symbol' || v.type == 'string') + key = new MIM.Key (v); + else if (v.type == 'integer') + key = new MIM.Key (v.val); + else throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + v); - var key = new MIM.Key (v.val); this.val.push (key); if (key.has_modifier) this.has_modifier = true; @@ -1418,24 +1506,16 @@ var MIM = { MIM.Marker.prototype.CharAt = function (ic) { var p = this.Position (ic); - if (p < 0) - return ic.GetSurroundingChar (p); - else if (p >= ic.preedit.length) - return ic.GetSurroundingChar (p - ic.preedit.length); + if (p < 0 || p >= ic.preedit.length) + return 0; return ic.preedit.charCodeAt (p); } - MIM.NamedMarker = function (name) { this.val = name; } - MIM.NamedMarker.prototype = new MIM.Marker (); - MIM.NamedMarker.prototype.Position = function (ic) - { - var p = ic.marker_positions[this.val]; - return (p == undefined ? 0 : p); - } - MIM.NamedMarker.prototype.Mark = function (ic) - { - ic.marker_positions[this.val] = ic.cursor_pos; - } + MIM.FloatingMarker = function (name) { this.val = name; }; + var proto = new MIM.Marker (); + MIM.FloatingMarker.prototype = proto; + proto.Position = function (ic) { return ic.marker_positions[this.val]; }; + proto.Mark = function (ic) { ic.marker_positions[this.val] = ic.cursor_pos; }; MIM.PredefinedMarker = function (name) { this.val = name; } MIM.PredefinedMarker.prototype = new MIM.Marker (); @@ -1486,7 +1566,7 @@ var MIM = { MIM.SurroundMarker = function (name) { this.val = name; - this.distance = parseInt (name.slice (2)); + this.distance = parseInt (name.slice (1)); if (isNaN (this.distance)) throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name); } @@ -1495,6 +1575,17 @@ var MIM = { { return ic.cursor_pos + this.distance; } + MIM.SurroundMarker.prototype.CharAt = function (ic) + { + if (this.val == '@-0') + return -1; + var p = this.Position (ic); + if (p < 0) + return ic.GetSurroundingChar (p); + else if (p >= ic.preedit.length) + return ic.GetSurroundingChar (p - ic.preedit.length); + return ic.preedit.charCodeAt (p); + } MIM.Marker.prototype.Parser = function (domain, node) { @@ -1504,12 +1595,12 @@ var MIM = { var n = predefined[name]; if (n) return n; - if (name.charAt (1) == '-') + if (name.charAt (1) == '-' || name.charAt (1) == '+') return new MIM.SurroundMarker (name); throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name); } - return new MIM.NamedMarker (name); + return new MIM.FloatingMarker (name);; } }) (); @@ -1526,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) { @@ -1545,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) @@ -1557,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 () @@ -1624,13 +1717,12 @@ MIM.Keymap = function () { this.name = 'TOP'; this.submaps = null; - this.actions = null; }; (function () { var proto = {}; - function add_rule (keymap, rule) + function add_rule (keymap, rule, branch_actions) { var keyseq = rule.keyseq; var len = keyseq.val.length; @@ -1651,22 +1743,31 @@ MIM.Keymap = function () keymap = sub; keymap.name = name; } - keymap.actions = rule.actions; + keymap.map_actions = rule.actions; + if (branch_actions) + keymap.branch_actions = branch_actions; } - proto.Add = function (map) + proto.Add = function (map, branch_actions) { var rules = map.rules; var len = rules.length; for (var i = 0; i < len; i++) - add_rule (this, rules[i]); + add_rule (this, rules[i], branch_actions); } proto.Lookup = function (keys, index) { var sub; if (index < keys.val.length && this.submaps + && ! keys.val[index]) + { + Xex.Log ('invalid key at ' + index); + throw 'invalid key'; + } + + if (index < keys.val.length && this.submaps && (sub = this.submaps[keys.val[index].key])) { index++; @@ -1701,15 +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]); - state.keymap.actions = Xex.Term.Parse (domain, n, null); - } + state.keymap.Add (map_list[node.attributes['mname'].nodeValue], + 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; @@ -1723,115 +1823,411 @@ MIM.State = function (name) MIM.State.prototype = proto; }) (); -MIM.im_domain = new Xex.Domain ('input-method', null, null); -MIM.im_domain.DefType (MIM.KeySeq.prototype); -MIM.im_domain.DefType (MIM.Marker.prototype); -MIM.im_domain.DefType (MIM.Selector.prototype); -MIM.im_domain.DefType (MIM.Rule.prototype); -MIM.im_domain.DefType (MIM.Map.prototype); -MIM.im_domain.DefType (MIM.State.prototype); - (function () { - var im_domain = MIM.im_domain; - - function Finsert (domain, vari, args) - { - var text; - if (args[0].type == 'integer') - text = String.fromCharCode (args[0].val); - else - text = args[0].val; - domain.context.insert (text, null); - } - - function Finsert_candidates (domain, vari, args) + function Block (index, term) { - var ic = domain.context; - var candidates = new MIM.Candidates (args, column); - ic.insert (candidates.Current (), candidates); - return args[0]; + this.Index = index; + if (term.IsStr) + this.Data = term.val; + else if (term.IsList) + { + this.Data = new Array (); + for (var i = 0; i < term.val.length; i++) + this.Data.push (term.val[i].val); + } } - function Fdelete (domain, vari, args) + Block.prototype.Count = function () { return this.Data.length; } + Block.prototype.get = function (i) { - var ic = domain.context; - var pos = args[0].IsInt ? args[0].Intval : args[0].Position (ic); - ic.del (pos); - return new Xex.Term (ic.del (pos)); + return (this.Data instanceof Array ? this.Data[i] : this.Data.charAt (i)); } - function Fselect (domain, vari, args) + MIM.Candidates = function (ic, candidates, column) { - var ic = domain.context; - var can = ic.candidates; + this.ic = ic; + this.column = column; + this.row = 0; + this.index = 0; + this.total = 0; + this.blocks = new Array (); - if (can) + for (var i = 0; i < candidates.length; i++) { - var candidate = can.Current (); - - ic.del (ic.cursor_pos - candidate.length); - candidate = can.Select (args[0]); - ic.insert (candidate, can); + var block = new Block (this.total, candidates[i]); + this.blocks.push (block); + this.total += block.Count (); } - return args[0]; } - function Fchar_at (domain, vari, args) + function get_col () { - return new Xex.Term (args[0].CharAt (domain.context)); + return (this.column > 0 ? this.index % this.column + : this.index - this.blocks[this.row].Index); } - function Fmove (domain, vari, args) + function prev_group () { - var ic = domain.context; - var pos = args[0].IsInt ? args[0].val : args[0].Position (ic); - ic.move (pos); - return args[0]; + var col = get_col.call (this); + var nitems; + if (this.column > 0) + { + this.index -= this.column; + if (this.index >= 0) + nitems = this.column; + else + { + var lastcol = (this.total - 1) % this.column; + this.index = (col < lastcol ? this.total - lastcol + col + : this.total - 1); + this.row = this.blocks.length - 1; + nitems = lastcol + 1; + } + while (this.blocks[this.row].Index > this.index) + this.row--; + } + else + { + this.row = this.row > 0 ? this.row - 1 : this.blocks.length - 1; + nitems = this.blocks[this.row].Count (); + this.index = (this.blocks[this.row].Index + + (col < nitems ? col : nitems - 1)); + } + return nitems; } - function Fmark (domain, vari, args) + function next_group () { - args[0].Mark (domain.context); - return args[0]; + var col = get_col.call (this); + var nitems; + if (this.column > 0) + { + this.index += this.column - col; + if (this.index < this.total) + { + if (this.index + col >= this.total) + { + nitems = this.total - this.index; + this.index = this.total - 1; + } + else + { + nitems = this.column; + this.index += col; + } + } + else + { + this.index = col; + this.row = 0; + } + while (this.blocks[this.row].Index + this.blocks[this.row].Count () + <= this.index) + this.row++; + } + else + { + this.row = this.row < this.blocks.length - 1 ? this.row + 1 : 0; + nitems = this.blocks[this.row].Count (); + this.index = (this.blocks[this.row].Index + + (col < nitems ? col : nitems - 1)); + } + return nitems; } - function Fpushback (domain, vari, args) + function prev () { - var arg = (args[0].IsInt ? args[0].Intval - : args[0].IsStr ? new KeySeq (args[0]) - : args[0]); - domain.context.pushback (arg) - return args[0]; - } + if (this.index == 0) + { + this.index = this.total - 1; + this.row = this.blocks.length - 1; + } + else + { + this.index--; + if (this.blocks[this.row].Index > this.index) + this.row--; + } + } - function Fundo (domain, vari, args) + function next () { - var ic = domain.context; - var n = args.length == 0 ? -2 : args[0].val; - if (n < 0) - ic.keys.val.splice (ic.keys.length + n, -n); + this.index++; + if (this.index == this.total) + { + this.index = 0; + this.row = 0; + } else - ic.keys.val.splice (n, ic.keys.length); - ic.reset (); - return Xex.nil; + { + var b = this.blocks[this.row]; + if (this.index == b.Index + b.Count ()) + this.row++; + } } - function Fcommit (domain, vari, args) + function first () { - domain.context.commit (); - return Xex.nil; + this.index -= get_col.call (this); + while (this.blocks[this.row].Index > this.index) + this.row--; } - function Funhandle (domain, vari, args) - { - domain.context.commit (); - return Xex.Fthrow (domain, vari, Xex.CatchTag._mimtag); - } - - function Fshift (domain, vari, args) + function last () { - var ic = domain.context; - var state_name = args[0].val; + var b = this.blocks[this.row]; + if (this.column > 0) + { + if (this.index + 1 < this.total) + { + this.index += this.column - get_col.call (this) + 1; + while (b.Index + b.Count () <= this.index) + b = this.blocks[++this.row]; + } + } + else + this.index = b.Index + b.Count () - 1; + } + + MIM.Candidates.prototype.Current = function () + { + var b = this.blocks[this.row]; + return b.get (this.index - b.Index); + } + + 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) + { + case '@<': first.call (this); break; + case '@>': last.call (this); break; + case '@-': prev.call (this); break; + case '@+': next.call (this); break; + case '@[': prev_group.call (this); break; + case '@]': next_group.call (this); break; + default: break; + } + } + 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 (); + } + 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) + { + var c = block.get (start - block.Index); + group.push (c); + start++; + if (start == block.Index + block.Count ()) + block = this.blocks[row++]; + } + return group; + } +}) (); + +MIM.im_domain = new Xex.Domain ('input-method', null, null); +MIM.im_domain.DefType (MIM.KeySeq.prototype); +MIM.im_domain.DefType (MIM.Marker.prototype); +MIM.im_domain.DefType (MIM.Selector.prototype); +MIM.im_domain.DefType (MIM.Rule.prototype); +MIM.im_domain.DefType (MIM.Map.prototype); +MIM.im_domain.DefType (MIM.State.prototype); + +(function () { + var im_domain = MIM.im_domain; + + function Finsert (domain, vari, args) + { + var text; + if (args[0].type == 'integer') + text = String.fromCharCode (args[0].val); + else + text = args[0].val; + domain.context.ins (text, null); + return args[0]; + } + + function Finsert_candidates (domain, vari, args) + { + var ic = domain.context; + 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]; + } + + function Fdelete (domain, vari, args) + { + var ic = domain.context; + var pos = args[0].IsInt ? args[0].Intval () : args[0].Position (ic); + return new Xex.IntTerm (ic.del (pos)); + } + + function Fselect (domain, vari, args) + { + var ic = domain.context; + var can = ic.candidates; + + if (can) + { + var old_text = can.Current (); + var new_text = can.Select (args[0]); + ic.rep (old_text, new_text, can); + } + else + Xex.Log ('no candidates at ' + ic.cursor_pos + ' of ' + ic.candidate_table.table.length); + return args[0]; + } + + function Fshow (domain, vari, args) + { + domain.context.candidate_show = true; + domain.context.changed |= MIM.ChangedStatus.CandidateShow; + return Xex.nil; + } + + function Fhide (domain, vari, args) + { + domain.context.candidate_show = false; + domain.context.changed |= MIM.ChangedStatus.CandidateShow; + return Xex.nil; + } + + function Fchar_at (domain, vari, args) + { + return new Xex.IntTerm (args[0].CharAt (domain.context)); + } + + function Fmove (domain, vari, args) + { + var ic = domain.context; + var pos = args[0].IsInt ? args[0].val : args[0].Position (ic); + ic.move (pos); + return new Xex.IntTerm (pos); + } + + function Fmark (domain, vari, args) + { + args[0].Mark (domain.context); + return args[0]; + } + + function Fpushback (domain, vari, args) + { + var a = (args[0].IsInt ? args[0].Intval () + : args[0].IsStr ? new KeySeq (args[0]) + : args[0]); + domain.context.pushback (a); + return args[0]; + } + + function Fpop (domain, vari, args) + { + var ic = domain.context; + if (ic.key_head < ic.keys.val.length) + ic.keys.val.splice (ic.keys_head, 1); + return Xex.nil; + } + + function Fundo (domain, vari, args) + { + var ic = domain.context; + var n = args.length == 0 ? -2 : args[0].val; + Xex.Log ('undo with arg ' + args[0]); + if (n < 0) + ic.keys.val.splice (ic.keys.val.length + n, -n); + else + ic.keys.val.splice (n, ic.keys.val.length); + ic.reset (); + return Xex.nil; + } + + function Fcommit (domain, vari, args) + { + domain.context.commit (); + return Xex.nil; + } + + function Funhandle (domain, vari, args) + { + domain.context.commit (); + return Xex.Fthrow (domain, vari, Xex.CatchTag._mimtag); + } + + function Fshift (domain, vari, args) + { + var ic = domain.context; + var state_name = args[0].val; var state = ic.im.state_list[state_name]; if (! state) throw ("Unknown state: " + state_name); @@ -1839,6 +2235,17 @@ MIM.im_domain.DefType (MIM.State.prototype); return args[0]; } + function Fshiftback (domain, vari, args) + { + domain.context.shift (null); + return Xex.nil; + } + + function Fkey_count (domain, vari, args) + { + return new Xex.IntTerm (domain.context.key_head); + } + function Fsurrounding_flag (domain, vari, args) { return new Xex.IntTerm (-1); @@ -1848,19 +2255,19 @@ MIM.im_domain.DefType (MIM.State.prototype); im_domain.DefSubr (Finsert_candidates, "insert-candidates", false, 1, 1); im_domain.DefSubr (Fdelete, "delete", false, 1, 1); im_domain.DefSubr (Fselect, "select", false, 1, 1); - //im_domain.DefSubr (Fshow, "show-candidates", false, 0, 0); - //im_domain.DefSubr (Fhide, "hide-candidates", false, 0, 0); + im_domain.DefSubr (Fshow, "show-candidates", false, 0, 0); + im_domain.DefSubr (Fhide, "hide-candidates", false, 0, 0); im_domain.DefSubr (Fmove, "move", false, 1, 1); im_domain.DefSubr (Fmark, "mark", false, 1, 1); im_domain.DefSubr (Fpushback, "pushback", false, 1, 1); - //im_domain.DefSubr (Fpop, "pop", false, 0, 0); + im_domain.DefSubr (Fpop, "pop", false, 0, 0); im_domain.DefSubr (Fundo, "undo", false, 0, 1); im_domain.DefSubr (Fcommit, "commit", false, 0, 0); im_domain.DefSubr (Funhandle, "unhandle", false, 0, 0); im_domain.DefSubr (Fshift, "shift", false, 1, 1); - //im_domain.DefSubr (Fshiftback, "shiftback", false, 0, 0); + im_domain.DefSubr (Fshiftback, "shiftback", false, 0, 0); im_domain.DefSubr (Fchar_at, "char-at", false, 1, 1); - //im_domain.DefSubr (Fkey_count, "key-count", false, 0, 0); + im_domain.DefSubr (Fkey_count, "key-count", false, 0, 0); im_domain.DefSubr (Fsurrounding_flag, "surrounding-text-flag", false, 0, 0); }) (); @@ -1922,10 +2329,9 @@ 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) - MIM.log ('var:' + this.domain.variables[vname.val].name); } } parsers['command-list'] = function (node) @@ -1967,8 +2373,8 @@ MIM.im_domain.DefType (MIM.State.prototype); alert ('inclusion fail'); continue; } - for (var mapname in im.map_list) - this.map_list[mapname] = im.map_list[mapname]; + for (var mname in im.map_list) + this.map_list[mname] = im.map_list[mname]; } else { @@ -1982,7 +2388,20 @@ MIM.im_domain.DefType (MIM.State.prototype); this.domain.map_list = this.map_list; for (node = node.firstElement (); node; node = node.nextElement ()) { - if (node.nodeName == 'state') + if (node.nodeName == 'xi:include') + { + var im = include (node); + if (! im) + alert ('inclusion fail'); + for (var sname in im.state_list) + { + state = im.state_list[sname]; + if (! this.initial_state) + this.initial_state = state; + this.state_list[sname] = state; + } + } + else if (node.nodeName == 'state') { var state = Xex.Term.Parse (this.domain, node); if (! state.title) @@ -2044,337 +2463,77 @@ MIM.im_domain.DefType (MIM.State.prototype); this.target = target; this.domain = new Xex.Domain ('context', im.domain, this); this.active = true; + this.range = new Array (); + this.range[0] = this.range[1] = 0; + this.state = null; + this.initial_state = this.im.initial_state; + this.keys = new MIM.KeySeq (); + this.marker_positions = new Array (); + this.candidate_table = new MIM.CandidateTable (); this.reset (); - this.spot = 0; } MIM.CandidateTable = function () - { - this.table = new Array (); - } - - MIM.CandidateTable.prototype.get = function (from) - { - for (var i = 0; i < this.table.length; i++) - { - var elt = this.table[i]; - if (elt.from <= from && elt.to > from) - return elt.val; - } - } - - MIM.CandidateTable.prototype.put = function (from, to, candidates) - { - for (var i = 0; i < this.table.length; i++) - { - var elt = this.table[i]; - if (elt.from >= from && elt.from < to - || elt.to >= from && elt.to < to) - { - elt.from = from; - elt.to = to; - elt.val = candidates; - return; - } - } - this.table.push ({ from: from, to: to, val: candidates }); - } - - MIM.CandidateTable.prototype.adjust = function (from, to, inserted) - { - var diff = inserted - (to - from); - for (var i = 0; i < this.table.length; i++) - { - var elt = this.table[i]; - if (elt.from >= to) - { - elt.from += diff; - elt.to += diff; - } - } - } - - MIM.CandidateTable.prototype.clear = function () - { - this.table.length = 0; - } - - function Block (index, term) - { - this.Index = index; - if (term.IsStr) - this.Data = term.val; - else if (term.IsList) - { - this.Data = new Array (); - for (var i = 0; i < term.val.length; i++) - this.Data.push (term.val[i].val); - } - } - - Block.prototype.Count = function () { return this.Data.length; } - Block.prototype.get = function (i) - { - return (this.Data instanceof Array ? this.Data[i] : this.Data.charAt (i)); - } - - function fill_group (start) - { - var nitems = this.group.length; - var r = this.row; - var b = this.blocks[r]; - - if (start < b.Index) - while (start < b.Index) - b = this.blocks[--r]; - else - while (start >= b.Index + b.Count ()) - b = this.blocks[++r]; - this.row = r; - - var count = b.Count (); - start -= b.Index; - for (var i = 0; i < nitems; i++, start++) - { - if (start >= count) - { - r++; - if (r == this.blocks.Length) - return i; - b = this.blocks[r]; - count = b.Count (); - start = 0; - } - this.group[i] = b.get (start); - } - return nitems; - } - - function Candidates (candidates, column) - { - this.column = column; - this.row = 0; - this.index = 0; - this.total = 0; - this.blocks = new Array (); - - for (var i = 0; i < candidates.length; i++) - { - var block = new Block (this.total, candidates[i]); - this.blocks.push (block); - this.total += block.Count (); - } - } - - Candidates.prototype.Column = function () - { - return (this.column > 0 ? this.index % this.column - : this.index - this.blocks[this.row].Index); - } - - Candidates.prototype.GroupLength = function () - { - if (this.column > 0) - { - var start = this.index - (this.index % this.column); - return (start + this.column <= this.total ? this.column - : this.total - start); - } - return this.blocks[this.row].Count; - } - - Candidates.prototype.Current = function () - { - var b = this.blocks[this.row]; - return b.get (this.index - b.Index); - } - - Candidates.prototype.PrevGroup = function () - { - var col = this.Column (); - var nitems; - if (this.column > 0) - { - this.index -= this.column; - if (this.index >= 0) - nitems = this.column; - else - { - var lastcol = (this.total - 1) % this.column; - this.index = (col < lastcol ? this.total - lastcol + col - : this.total - 1); - this.row = this.blocks.length - 1; - nitems = lastcol + 1; - } - while (this.blocks[this.row].Index > this.index) - this.row--; - } - else - { - this.row = this.row > 0 ? this.row - 1 : this.blocks.length - 1; - nitems = this.blocks[this.row].Count (); - this.index = (this.blocks[this.row].Index - + (col < nitems ? col : nitems - 1)); - } - return nitems; - } - - Candidates.prototype.NextGroup = function () - { - var col = this.Column (); - var nitems; - if (this.column > 0) - { - this.index += this.column - col; - if (this.index < this.total) - { - if (this.index + col >= this.total) - { - nitems = this.total - this.index; - this.index = this.total - 1; - } - else - { - nitems = this.column; - this.index += col; - } - } - else - { - this.index = col; - this.row = 0; - } - while (this.blocks[this.row].Index > this.index) - this.row++; - } - else - { - this.row = this.row < this.blocks.length - 1 ? this.row + 1 : 0; - nitems = this.blocks[this.row].Count (); - this.index = (this.blocks[this.row].Index - + (col < nitems ? col : nitems - 1)); - } - return nitems; - } - - Candidates.prototype.Prev = function () - { - if (this.index == 0) - { - this.index = this.total - 1; - this.row = this.blocks.length - 1; - } - else - { - this.index--; - if (this.blocks[this.row].Index > this.index) - this.row--; - } - } - - Candidates.prototype.Next = function () - { - this.index++; - if (this.index == this.total) - { - this.index = 0; - this.row = 0; - } - else - { - var b = this.blocks[this.row]; - if (this.index == b.Index + b.Count ()) - this.row++; - } + { + this.table = new Array (); } - Candidates.prototype.First = function () + MIM.CandidateTable.prototype.get = function (pos) { - this.index -= this.Column (); - while (this.blocks[this.row].Index > this.index) - this.row--; + for (var i = 0; i < this.table.length; i++) + { + var elt = this.table[i]; + if (elt.from < pos && pos <= elt.to) + return elt.val; + } } - Candidates.prototype.Last = function () + MIM.CandidateTable.prototype.put = function (from, to, candidates) { - var b = this.blocks[this.row]; - if (this.column > 0) + for (var i = 0; i < this.table.length; i++) { - if (this.index + 1 < this.total) + var elt = this.table[i]; + if (elt.from < to && elt.to > from) { - this.index += this.column - this.Column () + 1; - while (b.Index + b.Count () <= this.index) - b = this.blocks[++this.row]; + elt.from = from; + elt.to = to; + elt.val = candidates; + return; } } - else - this.index = b.Index + b.Count () - 1; + this.table.push ({ from: from, to: to, val: candidates }); } - Candidates.prototype.Select = function (selector) + MIM.CandidateTable.prototype.adjust = function (from, to, inserted) { - if (selector.type == 'selector') + var diff = inserted - (to - from); + if (diff == 0) + return; + for (var i = 0; i < this.table.length; i++) { - switch (selector.val) + var elt = this.table[i]; + if (elt.from >= to) { - case '@<': this.First (); break; - case '@>': this.Last (); break; - case '@-': this.Prev (); break; - case '@+': this.Next (); break; - case '@[': this.PrevGroup (); break; - case '@]': this.NextGroup (); break; - default: break; + elt.from += diff; + elt.to += diff; } - return this.Current (); - } - 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--; } - return this.Current (); } - function detach_candidates (ic) + MIM.CandidateTable.prototype.clear = function () { - ic.candidate_table.clear (); - ic.candidates = null; - ic.changed |= (MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos - | ChangedStatus.CandidateList - | ChangedStatus.CandidateIndex - | ChangedStatus.CandidateShow); + this.table.length = 0; } function set_cursor (prefix, pos) { this.cursor_pos = pos; - if (pos > 0) - this.candidates = this.candidate_table.get (pos - 1); - else - this.candidates = null; + var candidates = this.candidate_table.get (pos); + if (this.candidates != candidates) + { + this.candidates = candidates; + this.changed |= MIM.ChangedStatus.CandidateList; + } } function save_state () @@ -2394,67 +2553,68 @@ MIM.im_domain.DefType (MIM.State.prototype); function handle_key () { - var out = this.keymap.Lookup (this.keys, this.key_head); + Xex.Log ('Key(' + this.key_head + ') "' + this.keys.val[this.key_head] + + '" in ' + this.state.name + ':' + this.keymap.name + + " key/state/commit-head/len:" + + this.key_head + '/' + this.state_key_head + '/' + this.commit_key_head + '/' + this.keys.val.length); + var out = this.state.keymap.Lookup (this.keys, this.state_key_head); var sub = out.map; - var branch_actions = this.state.keymap.actions; - MIM.log ('handling ' + this.keys.val[this.key_head] - + ' in ' + this.state.name + ':' + this.keymap.name); - this.key_head = out.index; - if (sub != this.keymap) + if (out.index > this.key_head) { - + this.key_head = out.index; + Xex.Log (' with submap', false, true); restore_state.call (this); this.keymap = sub; - MIM.log ('submap found'); - if (this.keymap.actions) + if (sub.map_actions) { - MIM.log ('taking map actions:'); - if (! this.take_actions (this.keymap.actions)) + Xex.Log ('taking map actions:'); + if (! this.take_actions (sub.map_actions)) return false; } - else if (this.keymap.submaps) + else if (sub.submaps) { - MIM.log ('no map actions'); + Xex.Log ('no map actions'); for (var i = this.state_key_head; i < this.key_head; i++) { - MIM.log ('inserting key:' + this.keys.val[i].key); - this.insert (this.keys.val[i].key, null); + Xex.Log ('inserting key:' + this.keys.val[i].key); + this.ins (this.keys.val[i].key, null); } } - if (! this.keymap.submaps) + if (! sub.submaps) { - MIM.log ('terminal:'); - if (this.keymap.branch_actions != null) + Xex.Log ('terminal:'); + if (this.keymap.branch_actions) { - MIM.log ('branch actions:'); - if (! this.take_actions (branch_actions)) + Xex.Log ('branch actions:'); + if (! this.take_actions (this.keymap.branch_actions)) return false; } - if (this.keymap != this.state.keymap) + if (sub != this.state.keymap) this.shift (this.state); } } else { - MIM.log ('no submap'); + Xex.Log (' without submap', false, true); + this.keymap = sub; var current_state = this.state; var map = this.keymap; - if (branch_actions) + if (map.branch_actions) { - MIM.log ('branch actions'); - if (! this.take_actions (this.keymap.branch_actions)) + Xex.Log ('branch actions:'); + if (! this.take_actions (map.branch_actions)) return false; } if (map == this.keymap) { - MIM.log ('no state change'); + Xex.Log ('no state change'); if (map == this.initial_state.keymap && this.key_head < this.keys.val.length) { - MIM.log ('unhandled'); + Xex.Log ('unhandled'); return false; } if (this.keymap != current_state.keymap) @@ -2469,24 +2629,16 @@ MIM.im_domain.DefType (MIM.State.prototype); proto = { reset: function () { - this.produced = null; - this.preedit = ''; - this.preedit_saved = ''; this.cursor_pos = 0; - this.marker_positions = {}; - this.candidates = null; this.candidate_show = false; - this.state = null; this.prev_state = null; - this.initial_state = this.im.initial_state; this.title = this.initial_state.title; this.state_preedit = ''; this.state_key_head = 0; this.state_var_values = {}; this.state_pos = 0; - this.keymap = null; - this.keys = new MIM.KeySeq (); this.key_head = 0; + this.commit_key_head = 0; this.key_unhandled = false; this.unhandled_key = null; this.changed = MIM.ChangedStatus.None; @@ -2495,10 +2647,11 @@ MIM.im_domain.DefType (MIM.State.prototype); this.produced = ''; this.preedit = ''; this.preedit_saved = ''; - this.marker_positions = {}; - this.candidate_table = new MIM.CandidateTable (); + this.candidate_table.clear (); this.candidates = null; this.candidate_show = false; + for (var elt in this.marker_positions) + this.marker_positions[elt] = 0; this.shift (this.initial_state); }, @@ -2506,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); @@ -2516,60 +2671,120 @@ MIM.im_domain.DefType (MIM.State.prototype); GetSurroundingChar: function (pos) { - if (pos < 0 ? this.caret_pos < - pos : this.target.value.length < pos) - return 0; - return this.target.value.charCodeAt (this.caret_pos + pos); + if (pos < 0) + { + pos += this.range[0]; + if (pos < 0) + return 0; + } + else + { + pos += this.range[1]; + if (pos >= this.target.value.length) + return 0; + } + return this.target.value.charCodeAt (pos); }, + DelSurroundText: function (pos) + { + var text; + if (pos < 0) + { + pos += this.range[0]; + if (pos <= 0) + { + pos = 0; text = ''; + } + else + text = this.target.value.substring (0, pos); + if (this.range[0] < this.target.value.length) + text += this.target.value.substring (this.range[0]); + this.target.value = text; + this.range[1] -= this.range[0] - pos; + this.range[0] = pos; + } + else + { + pos += this.range[1]; + text = this.target.value.substring (0, this.range[1]); + if (pos >= this.target.value.length) + pos = this.target.value.length; + else + text += this.target.value.substring (pos); + this.target.value = text; + } + }, + adjust_markers: function (from, to, inserted) { var diff = inserted - (to - from); - for (var m in this.marker_positions) - if (this.marker_positions[m] > from) - this.marker_positions[m] = (this.marker_positions[m] >= to - ? pos + diff : 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) + for (var name in this.marker_positions) + { + var pos = this.marker_positions[name]; + if (pos > from) + { + if (pos >= to) + this.marker_positions[name] += diff; + else if (pos > from) + this.marker_positions[name] = 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) }, - insert: function (text, candidates) + 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); }, del: function (pos) { var deleted = pos - this.cursor_pos; - if (pos < 0) + if (pos < this.cursor_pos) { - this.DelSurroundText (pos); - pos = 0; + if (pos < 0) + { + this.DelSurroundText (pos); + deleted = - this.cursor_pos; + pos = 0; + } + if (pos < this.cursor_pos) + this.preedit_replace (pos, this.cursor_pos, '', null); } - else if (pos > this.preedit.length) + else { - this.DelSurroundText (pos - this.preedit.length); - pos = this.preedit.length; + if (pos > this.preedit.length) + { + this.DelSurroundText (pos - this.preedit.length); + deleted = this.preedit.length - this.cursor_pos; + pos = this.preedit.length; + } + if (pos > this.cursor_pos) + this.preedit_replace (this.cursor_pos, pos, '', null); } - if (pos < this.cursor_pos) - this.preedit = (this.predit.substring (0, pos) - + this.preedit.substring (this.cursor_pos)); - else - this.preedit = (this.preedit.substring (0, this.cursor_pos) - + this.predit.substring (pos)); return deleted; }, @@ -2640,6 +2855,9 @@ MIM.im_domain.DefType (MIM.State.prototype); this.candidate_table.clear (); this.produced += this.preedit; this.preedit_replace.call (this, 0, this.preedit.length, '', null); + this.preedit_saved = ''; + this.state_pos = 0; + this.commit_key_head = this.key_head; } }, @@ -2647,13 +2865,10 @@ MIM.im_domain.DefType (MIM.State.prototype); { if (state == null) { - MIM.log ("shifting back to previous"); if (this.prev_state == null) return; state = this.prev_state; } - else - MIM.log ("shifting to " + state.name); if (state == this.initial_state) { @@ -2661,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; } } @@ -2676,7 +2891,6 @@ MIM.im_domain.DefType (MIM.State.prototype); this.changed |= MIM.ChangedStatus.StateTitle; this.state = state; this.keymap = state.keymap; - this.state_key_head = this.key_head; save_state.call (this); }, @@ -2684,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; @@ -2704,6 +2919,10 @@ MIM.im_domain.DefType (MIM.State.prototype); this.unhandled_key = this.keys.val[this.key_head]; this.keys.val.splice (this.key_head, this.key_head + 1); } + if (this.state_key_head > 0) + this.state_key_head--; + if (this.commit_key_head > 0) + this.commit_key_head--; this.key_unhandled = true; break; } @@ -2714,14 +2933,31 @@ MIM.im_domain.DefType (MIM.State.prototype); break; } } + if (this.keymap == this.initial_state.keymap) + this.commit (); + + if (this.commit_key_head > 0) + { + this.keys.val.splice (0, this.commit_key_head); + this.key_head -= this.commit_key_head; + this.state_key_head -= this.commit_key_head; + this.commit_key_head = 0; + } if (this.key_unhandled) { this.keys.val.length = 0; + //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 - && this.preedit.length == 0); + && this.produced.length == 0); } } @@ -2789,22 +3025,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 false; + 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); @@ -2828,12 +3106,6 @@ MIM.add_event_listener = function (e) { listener.call (target, e || window.event); }; }); -MIM.log = function (msg) -{ - var node = document.getElementById ('log'); - node.value = msg + "\n" + node.value; -} - MIM.debug_print = function (event, ic) { if (! MIM.debug) @@ -2841,10 +3113,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'); @@ -2852,11 +3124,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; @@ -2864,14 +3136,22 @@ MIM.debug_print = function (event, ic) MIM.debug_nodes['status' + index].innerHTML = 'no IM'; MIM.debug_nodes['keymap' + index].innerHTML = ic.state.name; MIM.debug_nodes['preedit' + index].innerHTML = ic.preedit; + if (index == 0) + { + MIM.debug_nodes.keypress.innerHTML = ''; + MIM.debug_nodes.status1.innerHTML = ''; + MIM.debug_nodes.keymap1.innerHTML = ''; + MIM.debug_nodes.preedit1.innerHTML = '' + } }; -MIM.get_range = function (target, range) +MIM.get_range = function (target, ic) { + var from, to; if (target.selectionStart != null) // for Mozilla { - range[0] = target.selectionStart; - range[1] = target.selectionEnd; + from = target.selectionStart; + to = target.selectionEnd; } else // for IE { @@ -2880,68 +3160,216 @@ MIM.get_range = function (target, range) rr.moveToElementText (target); rr.setEndPoint ('EndToEnd', range); - range[0] = rr.text.length - r.text.length; - range[1] = rr.text.length; + from = rr.text.length - r.text.length; + to = rr.text.length; } -} + 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 + if (ic.preedit.length > 0) { - var scrollTop = target.scrollTop; - target.setSelectionRange (ic.spot, ic.spot + ic.preedit.length); - target.scrollTop = scrollTop; + 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 // IE + else if (ic.bar) + ic.bar.style.display = 'none' +}; + +MIM.update = function (target, ic, for_focus_out) +{ + var text = target.value; + target.value = (text.substring (0, ic.range[0]) + + ic.produced + + ic.preedit + + text.substring (ic.range[1])); + ic.range[0] += ic.produced.length; + ic.range[1] = ic.range[0] + ic.preedit.length; + MIM.set_caret (target, ic); + if (! for_focus_out) { - var range = target.createTextRange (); - range.moveStart ('character', ic.spot); - range.moveEnd ('character', ic.spot + ic.preedit.length); - range.select (); + 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 () { - var range = new Array (); - - MIM.check_range = function (target, ic) + MIM.show = function (ic) { - MIM.get_range (target, range); - if (range[0] != ic.spot || range[1] - range[0] != ic.preedit.length - || target.value.substring (range[0], range[1]) != ic.preedit) + 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 { - MIM.log ('reset:' + ic.spot + '-' + (ic.spot + ic.preedit.length) - + '/' + range[0] + '-' + range[1]); - ic.reset (); + 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'); } - target.value = (target.value.substring (0, range[0]) - + target.value.substring (range[1])); - ic.spot = range[0]; + ic.can_node.style.display = 'block'; + } + + MIM.hide = function (ic) + { + if (ic.can_node) + ic.can_node.style.display = 'none'; } }) (); -MIM.update = function (target, ic) +MIM.focus_in = function (event) { - var text = target.value; - target.value = (text.substring (0, ic.spot) - + ic.produced - + ic.preedit - + text.substring (ic.spot)); - ic.spot += ic.produced.length; - MIM.set_caret (target, ic); -}; + var target = event.target; + var ic = target.mim_ic; + Xex.Log ("Focus in " + target.tagName); + MIM.get_range (target, ic) + ic.Filter (MIM.Key.FocusIn); + setTimeout (function () { MIM.update (target, ic, false); }, 100); + return true; +} -MIM.reset_ic = function (event) +MIM.focus_out = function (event) { - if (event.target.mim_ic) - { - var ic = event.target.mim_ic; - var pos = ic.spot + ic.preedit.length; - ic.reset (); - if (pos > ic.spot) - event.target.setSelectionRange (pos, pos); - } + var target = event.target; + var ic = target.mim_ic; + Xex.Log ("Focus out " + target.tagName); + ic.Filter (MIM.Key.FocusOut); + MIM.update (target, ic, true); + return true; }; MIM.keydown = function (event) @@ -2953,24 +3381,39 @@ MIM.keydown = function (event) var ic = target.mim_ic; if (! ic || ic.im != MIM.current) { - MIM.log ('creating IC'); + target.mim_ic = null; + Xex.Log ('creating IC'); ic = new MIM.IC (MIM.current, target); + if (ic.im.load_status != MIM.LoadStatus.Loaded) + return true; target.mim_ic = ic; - MIM.add_event_listener (target, 'blur', MIM.reset_ic); + MIM.add_event_listener (target, 'focus', MIM.focus_in); + MIM.add_event_listener (target, 'blur', MIM.focus_out); } - if (ic.im.load_status != MIM.LoadStatus.Loaded) - return; - MIM.check_range (target, ic); + MIM.get_range (target, ic) MIM.debug_print (event, ic); ic.key = MIM.decode_key_event (event); + if (ic.key) + { + try { + var result = ic.Filter (ic.key); + } catch (e) { + Xex.Log ('Error' + e); + throw (e); + } + MIM.update (target, ic, false); + if (! ic.key_unhandled) + event.preventDefault (); + } }; MIM.keypress = function (event) { - if (! (event.target.type == "text" || event.target.type == "textarea")) + var target = event.target; + if (! (target.type == "text" || target.type == "textarea")) return; - var ic = event.target.mim_ic; + var ic = target.mim_ic; var i; try { @@ -2984,77 +3427,293 @@ MIM.keypress = function (event) return; } - MIM.log ("filtering " + ic.key); - var result = ic.Filter (ic.key); - MIM.update (event.target, ic); + try { + var result = ic.Filter (ic.key); + } catch (e) { + Xex.Log ('Error:' + e); + throw (e); + } + MIM.update (target, ic, false); if (! ic.key_unhandled) event.preventDefault (); + } catch (e) { + Xex.Log ("error:" + e); + event.preventDefault (); } finally { MIM.debug_print (event, ic); } + return; }; -MIM.select_im = function (event) -{ - var target = event.target.parentNode; - while (target.tagName != "SELECT") - 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; - MIM.log ('select IM: ' + im.name); - } -}; +(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' }, + 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 = {}; + for (name in MIM.imlist[lang]) + list[name] = MIM.imlist[lang][name]; + } + if (! list) + for (name in MIM.imlist[lang]) + Xex.Log ('no category ' + lang + '-' + name); + } + } -MIM.destroy_menu = function (event) -{ - if (event.target.tagName == "SELECT") - document.getElementsByTagName ('body')[0].removeChild (event.target); -}; + var destroy_timer; + var last_target; -MIM.select_menu = function (event) -{ - var target = 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); + } + } - if (! ((target.type == "text" || target.type == "textarea") - && event.which == 1 && event.ctrlKey)) - return; + function destroy_menu () { + if (! destroy_timer) + destroy_timer = setTimeout (destroy, 1000); + return true; + } - var 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]) - { - 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++; - } - sel.size = idx; - document.getElementsByTagName ('body')[0].appendChild (sel); -}; + 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; + } + if (last_target && target.parentLi != last_target) + { + last_target.style.backgroundColor = 'white'; + if (target.menu_level < last_target.menu_level) + { + last_target = last_target.parentLi; + last_target.style.backgroundColor = 'white'; + } + 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) + { + if (! target.parentNode || ! target.parentNode.menu_level) + { + event.preventDefault (); + return false; + } + target = target.parentNode; + } + 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; + var nobr = document.createElement ('nobr'); + nobr.innerHTML = text; + li.appendChild (nobr); + return li; + } + + var menu; + + 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.onmousedown = select_im; + menu.onmouseover = show_submenu; + menu.onmouseout = destroy_menu; + for (var catname in lang_category) + { + var cat = lang_category[catname]; + var li = create_li (1, catname); + var sub = create_ul ('hidden'); + for (var langname in cat) + { + 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.appendChild (sub); + menu.appendChild (li); + } + 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['zh']['py-gb']; + }; +}) (); MIM.test = function () { @@ -3079,18 +3738,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 ('xexlog'); + Xex.Log (null); MIM.init (); };