X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=xex.js;h=ffcfc70f584377edce80a224a56535808c1dfb0d;hb=b80d723bac900d147bed053b5bf432fc70a0991c;hp=0024512758601d09c1a7f4cb3f47586906e75c5a;hpb=02ddd0caed0fdf2abfefc8365fb18f6bd876dda0;p=m17n%2Fm17n-lib-js.git diff --git a/xex.js b/xex.js index 0024512..ffcfc70 100644 --- a/xex.js +++ b/xex.js @@ -1,35 +1,23 @@ // -* coding: utf-8; -* -var Xex = {}; - -Xex.Alist = function () -{ - this.count = 0; -} - -Xex.Alist.prototype.put = function (key, val) -{ - this.count++; - return (this[key] = val); -} -Xex.Alist.prototype.clone = function () -{ - var alist = new Xex.Alist (); - for (key in this) - alist[key] = this[key]; - return alist; -} -Xex.Alist.prototype.toString = function () -{ - var str = 'alist:'; - for (key in this) - str += '"' + key + '"'; - return str; -} - -// Xex.alist = new Xex.Alist (); -// Xex.alist.put ('abc', "ABC"); -// alert (Xex.alist['abc']); +var Xex = { + LogNode: null, + Log: function (arg, indent) + { + if (! Xex.LogNode) + return; + 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; + } + } +}; Xex.Error = { UnknownError: "unknown-error", @@ -45,7 +33,8 @@ Xex.Error = { UnknownFunction: "unknown-function", MacroExpansionError: "macro-expansion-error", - NoVariableName: "no-variable-anme", + NoVariableName: "no-variable-name", + NoFunctionName: "no-funcion-name", // Run time errors. ArithmeticError: "arithmetic-error", @@ -56,35 +45,43 @@ Xex.Error = { UncaughtThrow: "uncaught-throw" }; -Xex.Variable = function (domain, name, val) +Xex.Variable = function (domain, name, desc, val, range) { this.domain = domain; this.name = name; + this.desc = desc; this.val = val; + this.range = range; } -Xex.Variable.prototype.clone = function () { - return new Xex.Variable (this.domain, this.name, this.value); +Xex.Variable.prototype.clone = function () +{ + return new Xex.Variable (this.domain, this.name, this.desc, + this.val, this.range); } -Xex.Variable.prototype.Equals = function (obj) { +Xex.Variable.prototype.Equals = function (obj) +{ return ((obj instanceof Xex.Variable) && obj.name == this.name); } -Xex.Variable.prototype.SetValue = function (term) { +Xex.Variable.prototype.SetValue = function (term) +{ this.val = term; return term; } -Xex.Function = function (name, with_var, min_args, max_args) { +Xex.Function = function (name, with_var, min_args, max_args) +{ this.name = name; this.with_var = with_var; this.min_args = min_args; this.max_args = max_args; }; -Xex.Subrountine = function (builtin, name, with_var, min_args, max_args) { +Xex.Subrountine = function (builtin, name, with_var, min_args, max_args) +{ this.name = name; this.with_var = with_var; this.min_args = min_args; @@ -94,7 +91,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); @@ -121,7 +118,6 @@ Xex.SpecialForm.prototype.Call = function (domain, vari, args) Xex.Lambda = function (name, min_args, max_args, args, body) { this.name = name; - this.with_var = with_var; this.min_args = min_args; this.max_args = max_args; this.args = args; @@ -175,7 +171,6 @@ Xex.Lambda.prototype.Call = function (domain, vari, args) Xex.Macro = function (name, min_args, max_args, args, body) { this.name = name; - this.with_var = with_var; this.min_args = min_args; this.max_args = max_args; this.args = args; @@ -193,9 +188,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; } @@ -352,7 +347,7 @@ Xex.Domain.prototype = { this.functions[name] = new Xex.Lambda (name, min_args, max_args, args, body); }, - DefunByFunc: function (func) { this.functions[func.Name] = func; }, + DefunByFunc: function (func) { this.functions[func.name] = func; }, Defmacro: function (name, min_args, max_args, args, body) { this.functions[name] = new Xex.Macro (name, min_args, max_args, @@ -362,30 +357,20 @@ Xex.Domain.prototype = { { var func = this.functions[fname]; + if (! func) + throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname); if (this.termtypes[alias]) throw new Xex.ErrTerm (Xex.Error.FunctionConflict, "Already defined as a term type: " + alias); if (this.functions[alias]) throw new Xex.ErrTerm (Xex.Error.FunctionConflict, "Already defined as a function: " + alias); - if (! func) - throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname); this.functions[alias] = func; }, - Defvar: function (name) + Defvar: function (name, desc, val, range) { - var vari = this.variables[name]; - if (vari) - { - if (vari.Typed) - throw new Xex.ErrTerm (Xex.Error.VariableTypeConflict, - "Not a non-typed variable: " + name); - } - else - { - vari = new Xex.Variable (this, name, Xex.Zero); - this.variables[name] = vari; - } + var vari = new Xex.Variable (this, name, desc, val, range); + this.variables[name] = vari; return vari; }, GetFunc: function (name) @@ -393,7 +378,7 @@ Xex.Domain.prototype = { var func = this.functions[name]; if (! func) throw new Xex.ErrTerm (Xex.Error.UnknownFunction, - "Unknown function: " + this + ':' + name); + "Unknown function: " + name); return func; }, CopyFunc: function (domain, name) @@ -411,7 +396,8 @@ Xex.Domain.prototype = { { var vari = this.variables[name]; if (! vari) - vari = this.variables[name] = new Xex.Variable (this, name, Xex.Zero); + vari = this.variables[name] = new Xex.Variable (this, name, null, + Xex.Zero, null); return vari; }, GetVar: function (name) { return this.variables[name]; }, @@ -450,52 +436,182 @@ Xex.Term.prototype = { if (this.val != undefined) return '<' + this.type + '>' + this.val + ''; return '<' + this.type + '/>'; - } + }, + Intval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType, + "Not an integer"); }, + Strval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType, + "Not a string"); } }; -Xex.ParseTerm = function (domain, node) +Node.prototype.firstElement = function () { - var name = node.nodeName; - var parser = domain.termtypes[name]; - - if (parser) - return parser (domain, node); - if (name == 'defun' || name == 'defmacro') - { - name = parse_defun_head (domain, node); - parse_defun_body (domain, node); - return new Xex.StrTerm (name); - } - if (name == 'defvar') - { - name = parse_defvar (doamin, node); - return new Xex.StrTerm (nanme); - } - - return new Xex.Funcall.prototype.Parser (domain, node); + for (var n = this.firstChild; n; n = n.nextSibling) + if (n.nodeType == 1) + return n; + return null; } -Xex.ParseTermList = function (domain, node) +Node.prototype.nextElement = function () { - for (var n = node; n; n = n.nextSibling) + for (var n = this.nextSibling; n; n = n.nextSibling) if (n.nodeType == 1) + return n; + return null; +}; + +(function () { + function parse_defvar (domain, node) + { + var name = node.attributes['vname'].nodeValue; + if (! name) + throw new Xex.ErrTerm (Xex.Error.NoVariableName, node, ''); + var vari = domain.variables[name]; + var desc, val, range; + if (vari) { - if (n.nodeName == 'defun' || n.nodeName == 'defmacro') - Xex.parse_defun_head (domain, n); + desc = vari.description; + val = vari.val; + range = vari.range; } - var terms = new Array (); - for (var n = node; n; n = n.nextSibling) - if (n.nodeType == 1) + node = node.firstElement (); + if (node && node.nodeName == 'description') + { + desc = node.firstChild.nodeValue; + node = node.nextElement (); + } + if (node) + { + val = Xex.Term.Parse (domain, node); + node = node.nextElement (); + if (node && node.nodeName == 'possible-values') + for (node = node.firstElement (); node; node = node.nextElement ()) + { + var pval; + if (node.nodeName == 'range') + { + if (! val.IsInt) + throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid, + 'Range not allowed for ' + name); + pval = new Array (); + for (var n = node.firstElement (); n; n = n.nextElement ()) + { + var v = Xex.Term.Parse (domain, n); + if (! v.IsInt) + throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid, + 'Invalid range value: ' + val); + pval.push (v); + } + } + else + { + pval = Xex.Term.Parse (domain, node); + if (val.type != pval.type) + throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid, + 'Invalid possible value: ' + pval); + } + if (! range) + range = new Array (); + range.push (pval); + } + } + if (! val) + val = Xex.Zero; + domain.Defvar (name, desc, val, range); + return name; + } + + function parse_defun_head (domain, node) + { + var name = node.attributes['fname'].nodeValue; + if (! name) + throw new Xex.ErrTerm (Xex.Error.NoFunctionName, node, ''); + var args = new Array (); + var nfixed = 0, noptional = 0, nrest = 0; + + node = node.firstElement (); + if (node && node.nodeName == 'args') + { + var n; + for (n = n.firstElement (); n; n = n.nextElement ()) + { + if (n.nodeName == 'fixed') + nfixed++; + else if (n.nodeName == 'optional') + noptional++; + else if (n.nodeName == 'rest') + nrest++; + else + throw new Xex.ErrTerm (Xex.Error.WrongType, n, n.nodeName); + } + if (nrest > 1) + throw new Xex.ErrTerm (Xex.Error.WrongType, n, 'Too many '); + for (n = node.firstElement (); n; n = n.nextElement ()) + args.push (domain.DefVar (n.attributes['vname'].nodeValue)); + } + args.min_args = nfixed; + args.max_args = nrest == 0 ? nfixed + noptional : -1; + + if (node.nodeName == 'defun') + domain.Defun (name, args, null); + else + domain.Defmacro (name, args, null); + return name; + } + + function parse_defun_body (domain, node) + { + var name = node.attributes['fname'].nodeValue; + var func = domain.GetFunc (name); + var body; + for (node = node.firstElement (); node; node = node.nextElement ()) + if (node.nodeName != 'description' && node.nodeName != 'args') + break; + body = Xex.Term.Parse (domain, node, null); + func.body = body; + } + + Xex.Term.Parse = function (domain, node, stop) + { + if (arguments.length == 2) + { + var name = node.nodeName; + var parser = domain.termtypes[name]; + + if (parser) + return parser (domain, node); + if (name == 'defun' || name == 'defmacro') + { + name = parse_defun_head (domain, node); + parse_defun_body (domain, node); + return new Xex.StrTerm (name); + } + if (name == 'defvar') + { + name = parse_defvar (domain, node); + return new Xex.StrTerm (name); + } + return new Xex.Funcall.prototype.Parser (domain, node); + } + for (var n = node; n && n != stop; n = n.nextElement ()) + if (n.nodeName == 'defun' || n.nodeName == 'defmacro') + parse_defun_head (domain, n); + var terms = null; + for (var n = node; n && n != stop; n = n.nextElement ()) { if (n.nodeName == 'defun' || n.nodeName == 'defmacro') - Xex.parse_defun_body (domain, n); + parse_defun_body (domain, n); else if (n.nodeName == 'defvar') - Xex.parse_defvar (domain, n); + parse_defvar (domain, n); else - terms.push (Xex.ParseTerm (domain, n)); + { + if (! terms) + terms = new Array (); + terms.push (Xex.Term.Parse (domain, n)); + } } - return terms; -} + return terms; + } +}) (); Xex.Varref = function (vname) { @@ -510,6 +626,7 @@ Xex.Varref = function (vname) { if (! this.vari || this.vari.domain != domain) this.vari = domain.GetVarCreate (this.val); + Xex.Log (this.ToString () + '=>' + this.vari.val, domain.depth); return this.vari.val; } @@ -518,6 +635,11 @@ Xex.Varref = function (vname) return new Xex.Varref (node.attributes['vname'].nodeValue); } + proto.ToString = function () + { + return ''; + } + Xex.Varref.prototype = proto; }) (); @@ -539,12 +661,12 @@ Xex.Funcall = function (func, vari, args) var attr; if (fname == 'funcall') - fname = node.attributes['fname'] + fname = node.attributes['fname'].nodeValue; var func = domain.GetFunc (fname); var vari; attr = node.attributes['vname']; - vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : false; - var args = Xex.ParseTermList (domain, node.firstChild); + vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : null; + var args = Xex.Term.Parse (domain, node.firstElement (), null); return new Xex.Funcall (func, vari, args); } @@ -560,7 +682,16 @@ Xex.Funcall = function (func, vari, args) proto.Eval = function (domain) { - return this.func.Call (domain, this.vari, this.args); + if (! (this.func instanceof Xex.Subrountine)) + Xex.Log (this, domain.depth); + domain.depth++; + var result; + try { + result = this.func.Call (domain, this.vari, this.args); + } finally { + Xex.Log (this + ' => ' + result, --domain.depth); + } + return result; } proto.Clone = function () @@ -580,11 +711,18 @@ Xex.Funcall = function (func, vari, args) { var arglist = '' var len = this.args.length; + var str = '<' + this.func.name; + if (this.vari) + str += ' vname="' + this.vari.name + '"'; 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; @@ -642,6 +780,7 @@ Xex.IntTerm = function (num) { this.val = num; }; (function () { var proto = new Xex.Term ('integer'); proto.IsInt = true; + proto.Intval = function () { return this.val; }; proto.IsTrue = function () { return this.val != 0; } proto.Clone = function () { return new Xex.IntTerm (this.val); } proto.Parser = function (domain, node) @@ -659,11 +798,12 @@ Xex.StrTerm = function (str) { this.val = str; }; (function () { var proto = new Xex.Term ('string'); proto.IsStr = true; + proto.Strval = function () { return this.val; }; proto.IsTrue = function () { return this.val.length > 0; } 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; }) (); @@ -701,7 +841,7 @@ Xex.LstTerm = function (list) { this.val = list; }; proto.Parser = function (domain, node) { - var list = Xex.ParseTermList (domain, node.firstChild); + var list = Xex.Term.Parse (domain, node.firstElement (), null); return new Xex.LstTerm (list); } @@ -724,27 +864,128 @@ Xex.LstTerm = function (list) { this.val = list; }; function Fset (domain, vari, args) { - return vari.SetValue (args[0]); + if (! vari) + throw new Xex.ErrTerm (Xex.Error.NoVariableName, + 'No variable name to set'); + vari.SetValue (args[0]); + 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 Xex.IntTerm (n); - if (vari != null) + if (vari) vari.SetValue (term); return term; } function Fadd (domain, vari, args) { - var n = vari ? vari.val.val : 0; + var n = vari ? vari.val.Intval () : 0; var len = args.length; for (var i = 0; i < len; i++) - n += args[i].val; + n += args[i].Intval (); + return maybe_set_intvar (vari, n); + } + + function Fmul (domain, vari, args) + { + var n = vari ? vari.val.Intval () : 1; + for (var i = 0; i < args.length; i++) + n *= arg.Intval (); + return maybe_set_intvar (vari, n); + } + + function Fsub (domain, vari, args) + { + var n, i; + + if (! vari) + { + n = args[0].Intval (); + i = 1; + } + else + { + n = vari.val.Intval (); + i = 0; + } + while (i < args.length) + n -= args[i++].Intval (); + return maybe_set_intvar (vari, n); + } + + function Fdiv (domain, vari, args) + { + var n, i; + + if (! vari == null) + { + n = args[0].Intval (); + i = 1; + } + else + { + n = vari.val.Intval (); + i = 0; + } + while (i < args.length) + n /= args[i++].Intval (); return maybe_set_intvar (vari, n); } + function Fmod (domain, vari, args) + { + return maybe_set_intvar (vari, args[0].Intval () % args[1].Intval ()); + } + + function Flogior (domain, vari, args) + { + var n = vari == null ? 0 : vari.val; + for (var i = 0; i < args.length; i++) + n |= args[i].val; + 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; @@ -753,7 +994,7 @@ Xex.LstTerm = function (list) { this.val = list; }; var result = args[i].Eval (domain); if (domain.Thrown ()) return result; - if (! result.IsTrue) + if (! result.IsTrue ()) return Xex.Zero; } return Xex.One; @@ -767,12 +1008,78 @@ Xex.LstTerm = function (list) { this.val = list; }; var result = args[i].Eval (domain); if (domain.Thrown ()) return result; - if (result.IsTrue) + if (result.IsTrue ()) return Xex.One; } return Xex.Zero; } + function Feq (domain, vari, args) + { + for (var i = 1; i < args.length; i++) + if (! args[i - 1].Equals (args[i])) + return Xex.Zero; + 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 (); + + for (var i = 1; i < args.length; i++) + { + var n1 = args[i].Intval (); + if (n >= n1) + return Xex.Zero; + n = n1; + } + return Xex.One; + } + + function Fle (domain, vari, args) + { + var n = args[0].Intval (); + for (var i = 1; i < args.length; i++) + { + var n1 = args[i].Intval (); + if (n > n1) + return Xex.Zero; + n = n1; + } + return Xex.One; + } + + function Fgt (domain, vari, args) + { + var n = args[0].Intval (); + for (var i = 1; i < args.length; i++) + { + var n1 = args[i].Intval (); + if (n <= n1) + return Xex.Zero; + n = n1; + } + return Xex.One; + } + + function Fge (domain, vari, args) + { + var n = args[0].Intval (); + for (var i = 1; i < args.Length; i++) + { + var n1 = args[i].Intval (); + if (n < n1) + return Xex.Zero; + n = n1; + } + return Xex.One; + } + function Fprogn (domain, vari, args) { var result = Xex.One; @@ -793,13 +1100,35 @@ Xex.LstTerm = function (list) { this.val = list; }; if (domain.Thrown ()) return result; - if (result.IsTrue) + if (result.IsTrue ()) return args[1].Eval (domain); - if (args.Length == 2) + if (args.length == 2) return Zero; return args[2].Eval (domain); } + function Fcond (domain, vari, args) + { + for (var i = 0; i < args.length; i++) + { + var list = args[i]; + var result = list.val[0].Eval (domain); + if (result.IsTrue ()) + { + for (var j = 1; j < list.val.length; j++) + { + domain.depth++; + result = list.val[j].Eval (domain); + domain.depth--; + if (domain.Thrown ()) + return result; + } + return result; + } + } + return Xex.Zero; + } + function eval_terms (domain, terms, idx) { var result = Xex.Zero; @@ -871,14 +1200,65 @@ Xex.LstTerm = function (list) { this.val = list; }; Xex.BasicDomain = basic; basic.DefSubr (Fset, "set", true, 1, 1); + basic.DefAlias ("=", "set"); + 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"); + basic.DefSubr (Fsub, "sub", true, 1, -1); + basic.DefAlias ("-", "sub"); + basic.DefSubr (Fdiv, "div", true, 1, -1); + basic.DefAlias ("/", "div"); + basic.DefSubr (Fmod, "mod", true, 1, 2); + 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 (Feq, "eq", false, 2, -1); + basic.DefAlias ("==", "eq"); + 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); + basic.DefAlias ("<=", "le"); + basic.DefSubr (Fgt, "gt", false, 2, -1); + basic.DefAlias (">", "gt"); + basic.DefSubr (Fge, "ge", false, 2, -1); + basic.DefAlias (">=", "ge"); basic.DefSubr (Fthrow, "throw", false, 1, 2); + //basic.DefSubr (Fappend, "append", true, 0, -1); + //basic.DefSubr (Fconcat, "concat", true, 0, -1); + //basic.DefSubr (Fnth, "nth", false, 2, 2); + //basic.DefSubr (Fcopy, "copy", false, 1, 1); + //basic.DefSubr (Fins, "ins", true, 2, 2); + //basic.DefSubr (Fdel, "del", true, 2, 2); + //basic.DefSubr (Feval, "eval", false, 1, 1); + //basic.DefSubr (Fbreak, "break", false, 0, 1); + //basic.DefSubr (Freturn, "return", false, 0, 1); + //basic.DefSubr (Fthrow, "throw", false, 1, 2); + basic.DefSpecial (Fand, "and", false, 1, -1); + basic.DefAlias ("&&", "and"); basic.DefSpecial (For, "or", false, 1, -1); - basic.DefAlias ("=", "set"); + basic.DefAlias ("||", "or"); basic.DefSpecial (Fprogn, "progn", false, 1, -1); + basic.DefAlias ("expr", "progn"); basic.DefSpecial (Fif, "if", false, 2, 3); + //basic.DefSpecial (Fwhen, "when", false, 1, -1); + //basic.DefSpecial (Floop, "loop", false, 1, -1); + //basic.DefSpecial (Fwhile, "while", false, 1, -1); + basic.DefSpecial (Fcond, "cond", false, 1, -1); + //basic.DefSpecial (Fforeach, "foreach", true, 2, -1); + //basic.DefSpecial (Fquote, "quote", false, 1, 1); + //basic.DefSpecial (Ftype, "type", false, 1, 1); basic.DefSpecial (Fcatch, "catch", true, 2, -1); basic.DefType (Xex.Funcall.prototype); @@ -893,6 +1273,7 @@ Xex.LstTerm = function (list) { this.val = list; }; Xex.Zero = new Xex.IntTerm (0); Xex.One = new Xex.IntTerm (1); +Xex.nil = new Xex.SymTerm ('nil'); Xex.Load = function (server, file) { @@ -911,8 +1292,10 @@ var MIM = { enabled: true, // Boolean flag to tell if MIM is running in debug mode or not. debug: false, - // List of registered input methods. - im_list: {}, + // List of main input methods. + imlist: {}, + // List of extra input methods; + imextra: {}, // Global input method data im_global: null, // Currently selected input method. @@ -965,6 +1348,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; @@ -1002,7 +1387,9 @@ var MIM = { { 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) @@ -1035,12 +1422,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; @@ -1065,7 +1454,7 @@ var MIM = { for (node = node.firstChild; node; node = node.nextSibling) if (node.nodeType == 1) { - var term = Xex.ParseTerm (domain, node); + var term = Xex.Term.Parse (domain, node); return new MIM.KeySeq (term); } throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid keyseq"); @@ -1097,24 +1486,16 @@ var MIM = { MIM.Marker.prototype.CharAt = function (ic) { var p = this.Position (ic); - if (p < 0) - return ic.GetSurroundingChar (p); - else if (pos >= 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 (); @@ -1165,7 +1546,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); } @@ -1174,6 +1555,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) { @@ -1183,12 +1575,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);; } }) (); @@ -1197,6 +1589,7 @@ MIM.Selector = function (name) this.val = name; } MIM.Selector.prototype = new Xex.Term ('selector'); + (function () { var selectors = {}; selectors["@<"] = selectors["@first"] = new MIM.Selector ('@<'); @@ -1232,10 +1625,10 @@ MIM.Rule.prototype.Parser = function (domain, node) for (n = node.firstChild; n && n.nodeType != 1; n = n.nextSibling); if (! n) throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node); - var keyseq = Xex.ParseTerm (domain, n); + var keyseq = Xex.Term.Parse (domain, n); if (keyseq.type != 'keyseq') throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node); - var actions = Xex.ParseTermList (domain, n.nextSibling); + var actions = Xex.Term.Parse (domain, n.nextElement (), null); return new MIM.Rule (keyseq, actions); } MIM.Rule.prototype.toString = function () @@ -1248,6 +1641,7 @@ MIM.Map = function (name) this.name = name; this.rules = new Array (); }; + (function () { var proto = new Xex.Term ('map'); @@ -1259,7 +1653,7 @@ MIM.Map = function (name) var map = new MIM.Map (name); for (var n = node.firstChild; n; n = n.nextSibling) if (n.nodeType == 1) - map.rules.push (Xex.ParseTerm (domain, n)); + map.rules.push (Xex.Term.Parse (domain, n)); return map; } @@ -1299,22 +1693,25 @@ MIM.Action.prototype.Run = function (domain) 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; + var name = ''; for (var i = 0; i < len; i++) { var key = keyseq.val[i]; - var sub; + var sub = false; + name += key.key; if (! keymap.submaps) keymap.submaps = {}; else @@ -1322,17 +1719,19 @@ MIM.Keymap = function () if (! sub) keymap.submaps[key.key] = sub = new MIM.Keymap (); keymap = sub; + keymap.name = name; } - keymap.actions = rule.actions; + keymap.map_actions = rule.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) { @@ -1355,6 +1754,7 @@ MIM.State = function (name) this.name = name; this.keymap = new MIM.Keymap (); }; + (function () { var proto = new Xex.Term ('state'); @@ -1365,21 +1765,21 @@ MIM.State = function (name) if (! name) throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map"); var state = new MIM.State (name); - for (node = node.firstChild; node; node = node.nextSibling) + for (node = node.firstElement (); node; node = node.nextElement ()) { - if (node.nodeType != 1) - continue; - if (node.nodeName == 'branch') + if (node.nodeName == 'title') + state.title = node.firstChild.nodeValue; + else { - state.keymap.Add (map_list[node.attributes['mname'].nodeValue]); - state.keymap.actions = Xex.ParseTermList (domain, node.firstChild); + var n = node.firstElement (); + if (node.nodeName == 'branch') + state.keymap.Add (map_list[node.attributes['mname'].nodeValue], + Xex.Term.Parse (domain, n, null)); + else if (node.nodeName == 'state-hook') + state.enter_actions = Xex.Term.Parse (domain, n, null); + else if (node.nodeName == 'catch-all-branch') + state.fallback_actions = Xex.Term.Parse (domain, n, null); } - else if (node.nodeName == 'state-hook') - state.enter_actions = Xex.ParseTermList (domain, node.firstChild); - else if (node.nodeName == 'catch-all-branch') - state.fallback_actions = Xex.ParseTermList (domain, node.firstChild); - else if (node.nodeName == 'title') - state.title = node.firstChild.nodeValue; } return state; } @@ -1392,58 +1792,525 @@ 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) + function Block (index, term) { - domain.context.insert (args[0].val, null); + 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); + } } - im_domain.DefSubr (Finsert, "insert", false, 1, 1); -}) (); - -(function () { - var parsers = { }; - parsers['description'] = function (node) - { - this.description = node.firstChild.nodeValue; - } - parsers['title'] = function (node) + Block.prototype.Count = function () { return this.Data.length; } + Block.prototype.get = function (i) { - this.title = node.firstChild.nodeValue; + return (this.Data instanceof Array ? this.Data[i] : this.Data.charAt (i)); } - parsers['map-list'] = function (node) + + MIM.Candidates = function (candidates, column) { - for (node = node.firstChild; node; node = node.nextSibling) + this.column = column; + this.row = 0; + this.index = 0; + this.total = 0; + this.blocks = new Array (); + + for (var i = 0; i < candidates.length; i++) { - if (node.nodeType != 1 || node.nodeName != 'map') - continue; - var map = Xex.ParseTerm (this.domain, node); - this.map_list[map.name] = map; + var block = new Block (this.total, candidates[i]); + this.blocks.push (block); + this.total += block.Count (); } } - parsers['state-list'] = function (node) + + function get_col () { - this.domain.map_list = this.map_list; - for (node = node.firstChild; node; node = node.nextSibling) + return (this.column > 0 ? this.index % this.column + : this.index - this.blocks[this.row].Index); + } + + function prev_group () + { + 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 next_group () + { + 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.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 prev () + { + 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 next () + { + 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++; + } + } + + function first () + { + this.index -= get_col.call (this); + while (this.blocks[this.row].Index > this.index) + this.row--; + } + + function last () + { + 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) + { + 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.cal (this); break; + default: break; + } + 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 (); + } +}) (); + +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 (args, gsize ? gsize.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; + if (n < 0) + ic.keys.val.splice (ic.keys.length + n, -n); + else + ic.keys.val.splice (n, ic.keys.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); + ic.shift (state); + 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); + } + + im_domain.DefSubr (Finsert, "insert", false, 1, 1); + 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 (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 (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 (Fchar_at, "char-at", false, 1, 1); + im_domain.DefSubr (Fkey_count, "key-count", false, 0, 0); + im_domain.DefSubr (Fsurrounding_flag, "surrounding-text-flag", false, 0, 0); +}) (); + + +(function () { + function get_global_var (vname) + { + if (MIM.im_global.load_status == MIM.LoadStatus.NotLoaded) + MIM.im_global.Load () + return MIM.im_global.domain.variables[vname]; + } + + function include (node) + { + node = node.firstElement (); + if (node.nodeName != 'tags') + return null; + + var lang = null, name = null, extra = null; + for (node = node.firstElement (); node; node = node.nextElement ()) + { + if (node.nodeName == 'language') + lang = node.firstChild.nodeValue; + else if (node.nodeName == 'name') + name = node.firstChild.nodeValue; + else if (node.nodeName == 'extra-id') + extra = node.firstChild.nodeValue; + } + if (! lang || ! MIM.imlist[lang]) + return null; + if (! extra) + { + if (! name || ! (im = MIM.imlist[lang][name])) + return null; + } + else + { + if (! (im = MIM.imextra[lang][extra])) + return null; + } + if (im.load_status != MIM.LoadStatus.Loaded + && (im.load_status != MIM.LoadStatus.NotLoaded || ! im.Load ())) + return null; + return im; + } + + var parsers = { }; + + parsers['description'] = function (node) + { + this.description = node.firstChild.nodeValue; + } + parsers['variable-list'] = function (node) + { + for (node = node.firstElement (); node; node = node.nextElement ()) { - if (node.nodeType != 1 || node.nodeName != 'state') - continue; - var state = Xex.ParseTerm (this.domain, node); - if (! state.title) - state.title = this.title; - if (! this.initial_state) - this.initial_state = state; - this.state_list[state.name] = state; + var vname = node.attributes['vname'].nodeValue; + if (this != MIM.im_global) + { + var vari = get_global_var (vname); + if (vari != null) + this.domain.Defvar (vname); + } + vname = Xex.Term.Parse (this.domain, node) + } + } + parsers['command-list'] = function (node) + { + } + parsers['macro-list'] = function (node) + { + for (var n = node.firstElement (); n; n = n.nextElement ()) + if (n.nodeName == 'xi:include') + { + var im = include (n); + if (! im) + alert ('inclusion fail'); + else + for (var macro in im.domain.functions) + { + var func = im.domain.functions[macro]; + if (func instanceof Xex.Macro) + im.domain.CopyFunc (this.domain, macro); + } + n = n.previousSibling; + node.removeChild (n.nextSibling); + } + Xex.Term.Parse (this.domain, node.firstElement (), null); + } + parsers['title'] = function (node) + { + this.title = node.firstChild.nodeValue; + } + parsers['map-list'] = function (node) + { + for (node = node.firstElement (); node; node = node.nextElement ()) + { + if (node.nodeName == 'xi:include') + { + var im = include (node); + if (! im) + { + alert ('inclusion fail'); + continue; + } + for (var mapname in im.map_list) + this.map_list[mapname] = im.map_list[mapname]; + } + else + { + var map = Xex.Term.Parse (this.domain, node); + this.map_list[map.name] = map; + } + } + } + parsers['state-list'] = function (node) + { + this.domain.map_list = this.map_list; + for (node = node.firstElement (); node; node = node.nextElement ()) + { + if (node.nodeName == 'state') + { + var state = Xex.Term.Parse (this.domain, node); + if (! state.title) + state.title = this.title; + if (! this.initial_state) + this.initial_state = state; + this.state_list[state.name] = state; + } } delete this.domain.map_list; } @@ -1455,7 +2322,9 @@ MIM.im_domain.DefType (MIM.State.prototype); this.extra_id = extra_id; this.file = file; this.load_status = MIM.LoadStatus.NotLoaded; - this.domain = new Xex.Domain (this.lang + '-' + this.name, + this.domain = new Xex.Domain (this.lang + '-' + + (this.name != 'nil' + ? this.name : this.extra_id), MIM.im_domain, null); } @@ -1471,10 +2340,8 @@ MIM.im_domain.DefType (MIM.State.prototype); this.map_list = {}; this.initial_state = null; this.state_list = {}; - for (node = node.firstChild; node; node = node.nextSibling) + for (node = node.firstElement (); node; node = node.nextElement ()) { - if (node.nodeType != 1) - continue; var name = node.nodeName; var parser = parsers[name]; if (parser) @@ -1487,15 +2354,23 @@ MIM.im_domain.DefType (MIM.State.prototype); MIM.IM.prototype = proto; - MIM.IC = function (im) + MIM.IC = function (im, target) { if (im.load_status == MIM.LoadStatus.NotLoaded) im.Load (); if (im.load_status != MIM.LoadStatus.Loaded) alert ('im:' + im.name + ' error:' + im.load_status); this.im = im; + 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 (); } @@ -1504,12 +2379,12 @@ MIM.im_domain.DefType (MIM.State.prototype); this.table = new Array (); } - MIM.CandidateTable.prototype.get = function (from) + MIM.CandidateTable.prototype.get = function (pos) { for (var i = 0; i < this.table.length; i++) { var elt = this.table[i]; - if (elt.from <= from && elt.to > from) + if (elt.from < pos && pos <= elt.to) return elt.val; } } @@ -1519,8 +2394,7 @@ MIM.im_domain.DefType (MIM.State.prototype); 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) + if (elt.from < to && elt.to > from) { elt.from = from; elt.to = to; @@ -1534,6 +2408,8 @@ MIM.im_domain.DefType (MIM.State.prototype); MIM.CandidateTable.prototype.adjust = function (from, to, inserted) { var diff = inserted - (to - from); + if (diff == 0) + return; for (var i = 0; i < this.table.length; i++) { var elt = this.table[i]; @@ -1550,13 +2426,20 @@ 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; - if (pos > 0) - this.candidates = this.candidate_table.get (pos - 1); - else - this.candidates = null; + this.candidates = this.candidate_table.get (pos); } function save_state () @@ -1579,28 +2462,36 @@ MIM.im_domain.DefType (MIM.State.prototype); var out = this.keymap.Lookup (this.keys, this.key_head); var sub = out.map; - alert ('handling ' + this.keys.val[this.key_head]); + Xex.Log ('handling ' + this.keys.val[this.key_head] + + ' in ' + this.state.name + ':' + this.keymap.name); this.key_head = out.index; if (sub != this.keymap) { + restore_state.call (this); this.keymap = sub; - alert ('submap found, taking map actions:' + sub.actions); - if (this.keymap.actions != null) + Xex.Log ('submap found'); + if (this.keymap.map_actions) { - if (! this.take_actions (this.keymap.actions)) + Xex.Log ('taking map actions:'); + if (! this.take_actions (this.keymap.map_actions)) return false; } - else if (this.keymap.submaps != null) + else if (this.keymap.submaps) { + Xex.Log ('no map actions'); for (var i = this.state_key_head; i < this.key_head; i++) - this.preedit_replace (this.cursor_pos, this.cursor_pos, - 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 == null) + if (! this.keymap.submaps) { + Xex.Log ('terminal:'); if (this.keymap.branch_actions != null) { + Xex.Log ('branch actions:'); if (! this.take_actions (this.keymap.branch_actions)) return false; } @@ -1610,21 +2501,29 @@ MIM.im_domain.DefType (MIM.State.prototype); } else { + Xex.Log ('no submap'); var current_state = this.state; + var map = this.keymap; - if (this.keymap.branch_actions != null) + if (map.branch_actions) { - if (! this.take_actions (this.keymap.branch_actions)) + Xex.Log ('branch actions'); + if (! this.take_actions (map.branch_actions)) return false; } - if (this.state == current_state) + + if (map == this.keymap) { - if (this.state == this.initial_state + Xex.Log ('no state change'); + if (map == this.initial_state.keymap && this.key_head < this.keys.val.length) - return false; - if (this.keymap != this.state.keymap) - this.shift (this.state); - else if (this.keymap.branch_actions == null) + { + Xex.Log ('unhandled'); + return false; + } + if (this.keymap != current_state.keymap) + this.shift (current_state); + else if (this.keymap.actions == null) this.shift (this.initial_state); } } @@ -1632,42 +2531,32 @@ MIM.im_domain.DefType (MIM.State.prototype); } proto = { - init: function () + reset: function () { - this.produced = null; - this.preedit = ''; + Xex.Log ('reseting ' + this.im.lang); 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.keys.val.length = 0; this.key_unhandled = false; this.unhandled_key = null; this.changed = MIM.ChangedStatus.None; this.error_message = ''; this.title = this.initial_state.title; - this.produced = null; + this.produced = ''; this.preedit = ''; - this.marker_positions = {}; - this.candidate_table = new MIM.CandidateTable (); + this.preedit_saved = ''; + this.candidate_table.clear (); this.candidates = null; this.candidate_show = false; - }, - - reset: function () - { - this.init (); - this.state_var_values = {}; + for (var elt in this.marker_positions) + this.marker_positions[elt] = 0; this.shift (this.initial_state); }, @@ -1685,19 +2574,66 @@ 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); + 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; + } + } if (this.cursor_pos >= to) set_cursor.call (this, 'adjust', this.cursor_pos + diff); else if (this.cursor_pos > from) @@ -1714,30 +2650,47 @@ MIM.im_domain.DefType (MIM.State.prototype); this.candidate_table.put (from, from + text.length, candidates) }, - 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); + this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos; + }, + del: function (pos) { - if (pos < 0) + var deleted = pos - this.cursor_pos; + 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)); + if (deleted != 0) + this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos; + return deleted; }, show: function () @@ -1806,43 +2759,42 @@ 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_replace.call (this, 0, this.preedit.length, '', null); } }, shift: function (state) { if (state == null) - { - if (this.prev_state == null) - return; - state = this.prev_state; - } + { + if (this.prev_state == null) + return; + state = this.prev_state; + } if (state == this.initial_state) { - this.commit (); - this.keys.val.splice (0, this.key_head); - this.key_head = 0; - if (state != this.state) + if (this.state) { - this.domain.RestoreValues (this.state_initial_var_values); - if (state.enter_actions != null) - take_actions.call (state.enter_actions); + this.commit (); + this.keys.val.splice (0, this.key_head); + this.key_head = 0; + this.prev_state = null; } - this.prev_state = null; } else - { - if (state != this.state && state.enter_actions != null) - take_actions.call (state.enter_actions); - this.prev_state = this.state; + { + if (state != this.state) + this.prev_state = this.state; } - save_state.call (this); + if (state != this.state && state.enter_actions) + this.take_actions (state.enter_actions); if (! this.state || this.state.title != state.title) this.changed |= MIM.ChangedStatus.StateTitle; this.state = state; this.keymap = state.keymap; + this.state_key_head = this.key_head; + save_state.call (this); }, Filter: function (key) @@ -1864,16 +2816,29 @@ MIM.im_domain.DefType (MIM.State.prototype); { if (! handle_key.call (this)) { - this.unhandled_key = this.keys.val[this.key_head++]; + if (this.key_head < this.keys.val.length) + { + this.unhandled_key = this.keys.val[this.key_head]; + this.keys.val.splice (this.key_head, this.key_head + 1); + } this.key_unhandled = true; break; } if (++count == 10) - break; + { + this.reset (); + this.key_unhandled = true; + break; + } } - this.keys.val.splice (0, this.key_head); - this.key_head = 0; - return (! this.key_unhandled && this.produced.length == 0); + if (this.key_unhandled) + { + this.keys.val.length = 0; + this.key_head = this.state_key_head = this.commit_key_head = 0; + } + return (! this.key_unhandled + && this.produced.length == 0 + && this.preedit.length == 0); } } @@ -1883,7 +2848,7 @@ MIM.im_domain.DefType (MIM.State.prototype); for (node = node.firstChild; node; node = node.nextSibling) if (node.nodeName == 'input-method') { - var lang, name, extra_id, file; + var lang = null, name = null, extra_id = null, file = null; for (var n = node.firstChild; n; n = n.nextSibling) { @@ -1896,47 +2861,592 @@ MIM.im_domain.DefType (MIM.State.prototype); else if (n.nodeName == 'filename') file = n.firstChild.nodeValue; } - if (! MIM.im_list[lang]) - MIM.im_list[lang] = {}; - MIM.im_list[lang][name] = new MIM.IM (lang, name, extra_id, file); + if (name && name != 'nil') + { + if (! MIM.imlist[lang]) + MIM.imlist[lang] = {}; + MIM.imlist[lang][name] = new MIM.IM (lang, name, extra_id, file); + } + else if (extra_id && extra_id != 'nil') + { + if (! MIM.imextra[lang]) + MIM.imextra[lang] = {}; + MIM.imextra[lang][extra_id] = new MIM.IM (lang, name, extra_id, file); + } } + if (MIM.imextra.t && MIM.imextra.t.global) + MIM.im_global = MIM.imextra.t.global; + else + { + MIM.im_global = new MIM.IM ('t', 'nil', 'global', null); + MIM.im_global.load_status = MIM.LoadStatus.Error; + } node = undefined; }) (); (function () { + var keys = new Array (); + keys[0x09] = 'tab'; + keys[0x08] = 'backspace'; + keys[0x0D] = 'return'; + keys[0x1B] = 'escape'; + keys[0x20] = 'space'; + keys[0x21] = 'pageup'; + keys[0x22] = 'pagedown'; + keys[0x23] = 'end'; + keys[0x24] = 'home'; + keys[0x25] = 'left'; + keys[0x26] = 'up'; + keys[0x27] = 'right'; + keys[0x28] = 'down'; + keys[0x2D] = 'insert'; + keys[0x2E] = 'delete'; + for (var i = 1; i <= 12; i++) + keys[111 + i] = "f" + i; + 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.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 ; + } + else + key = String.fromCharCode (key); + } + if (event.altKey) key = "A-" + key ; + if (event.ctrlKey) key = "C-" + key ; + return new MIM.Key (key); + } +}) (); - MIMTEST = function (name) { this.nam = name; }; +MIM.add_event_listener + = (window.addEventListener + ? function (target, type, listener) { + target.addEventListener (type, listener, false); + } + : window.attachEvent + ? function (target, type, listener) { + target.attachEvent ('on' + type, + function() { + listener.call (target, window.event); + }); + } + : function (target, type, listener) { + target['on' + type] + = function (e) { listener.call (target, e || window.event); }; + }); + +MIM.debug_print = function (event, ic) +{ + if (! MIM.debug) + return; + if (! MIM.debug_nodes) + { + MIM.debug_nodes = new Array (); + 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'); + MIM.debug_nodes['preedit1'] = document.getElementById ('preedit1'); + } + var target = event.target; + var code = event.keyCode; + 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 + '/' + event.keyIdentifier; + index = (event.type == 'keydown' ? '0' : '1'); + if (ic) + MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status; + else + 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, ic) +{ + var from, to; + if (target.selectionStart != null) // for Mozilla + { + from = target.selectionStart; + to = target.selectionEnd; + } + else // for IE + { + var r = document.selection.createRange (); + var rr = r.duplicate (); + + rr.moveToElementText (target); + rr.setEndPoint ('EndToEnd', range); + 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)) + return true; + ic.range[0] = from; + ic.range[1] = to; + return false; +} + +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 + { + var range = target.createTextRange (); + range.moveStart ('character', ic.range[0]); + range.moveEnd ('character', ic.range[1]); + range.select (); + } +}; + +MIM.update = function (target, ic) +{ + 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); +}; + +MIM.reset_ic = function (event) +{ + if (event.target.mim_ic) + { + var target = event.target; + var ic = target.mim_ic; + if (ic.preedit.length > 0) + event.target.setSelectionRange (ic.range[1], ic.range[1]); + ic.reset (); + } +}; + +MIM.keydown = function (event) +{ + var target = event.target; + if (target.id == 'log') + return; + if (! (target.type == "text" || target.type == "textarea")) + return; + + var ic = target.mim_ic; + if (! ic || ic.im != MIM.current) + { + target.mim_ic = null; + Xex.Log ('creating IC'); + ic = new MIM.IC (MIM.current, target); + if (ic.im.load_status != MIM.LoadStatus.Loaded) + return; + target.mim_ic = ic; + MIM.add_event_listener (target, 'blur', MIM.reset_ic); + MIM.get_range (target, ic) + } + else + { + if (! MIM.get_range (target, ic)) + ic.reset (); + } + 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); + if (! ic.key_unhandled) + event.preventDefault (); + } +}; + +MIM.keypress = function (event) +{ + var target = event.target; + if (target.id == 'log') + return; + if (! (target.type == "text" || target.type == "textarea")) + return; + + var ic = target.mim_ic; + var i; + + try { + if (ic.im.load_status != MIM.LoadStatus.Loaded) + return; + if (! ic.key) + ic.key = MIM.decode_key_event (event); + if (! ic.key) + { + ic.reset (); + 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); + if (! ic.key_unhandled) + event.preventDefault (); + } catch (e) { + Xex.Log ("error:" + e); + event.preventDefault (); + } finally { + MIM.debug_print (event, ic); + } + return; +}; - function testshow () { alert (this.nam); }; +(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); + } + } + + var destroy_timer; + var last_target; - MIMTEST.prototype.show = function () { testshow.call (this); } - MIMTEST.prototype.cut = function (from, to) { - this.val.splice (from, to -from); return this.val; } + 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); + } + } - MIMTEST2 = function (name) { this.nam = name; - this.val = new Array (1,2,3,4,5);} + function destroy_menu () { + if (! destroy_timer) + destroy_timer = setTimeout (destroy, 1000); + } - MIMTEST2.prototype = MIMTEST.prototype; + 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) + { + 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 (); + } - var x = new MIMTEST2 ('test2'); + 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; + } + + 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.onclick = 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); + } + 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 () { - var im = MIM.im_list['t']['latn-post']; - var ic = new MIM.IC (im); - - document.AIM = im; + var im = MIM.imlist['t']['latn-post']; + var ic = new MIM.IC (im, null); ic.Filter (new MIM.Key ('a')); ic.Filter (new MIM.Key ("'")); if (true) - document.getElementById ('text').value = ic.preedit; + document.getElementById ('text').value = ic.produced + ic.preedit; else { try { document.getElementById ('text').value - = Xex.ParseTerm (domain, body).Eval (domain).toString (); + = Xex.Term.Parse (domain, body).Eval (domain).toString (); } catch (e) { if (e instanceof Xex.ErrTerm) alert (e); @@ -1944,3 +3454,12 @@ MIM.test = function () } } } + + +MIM.init_debug = function () +{ + MIM.debug = true; + Xex.LogNode = document.getElementById ('log'); + Xex.Log (null); + MIM.init (); +};