From: handa Date: Wed, 24 Mar 2010 08:59:48 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: ALPHA~4 X-Git-Url: http://git.chise.org/gitweb/?a=commitdiff_plain;h=d64052eb9999afb5618be26c44d033cc8ce33c54;p=m17n%2Fm17n-lib-js.git *** empty log message *** --- diff --git a/Makefile b/Makefile index 58383c4..123272e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ DIST=/project/web/extra/m17n-lib/mim-js/ -FILES=index.html test.html imlist.xml xex.js mim.js /usr/local/work/m17n-db-xml/MIM/*.mimx +FILES=index.html test.html imlist.xml xex.js mim.js loadxml.html /usr/local/work/m17n-db-xml/MIM/*.mimx upload: @updated=""; \ diff --git a/index2.html b/index2.html new file mode 100644 index 0000000..94c85b7 --- /dev/null +++ b/index2.html @@ -0,0 +1,95 @@ + + + + + + M17N Input Method by JavaScript + + + + + + +

m17n-lib input method in Javascript (tested only on Chrome)

+The default input method is latn-post. +Click an input area by Control-Mouse-1 to select any other input method.
+ + +
textarea
text
+
debug Info
+ + + + +
keystatuskeycodekeymappreeditrange
down0
press0
+ + + diff --git a/loadxml.html b/loadxml.html new file mode 100644 index 0000000..d5ca545 --- /dev/null +++ b/loadxml.html @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/mim.js b/mim.js index abde0a9..2461427 100644 --- a/mim.js +++ b/mim.js @@ -31,7 +31,7 @@ var MIM = { // Boolean flag to tell if MIM is running in debug mode or not. debug: false, // List of main input methods. - imlist: {}, + imlist: null, // List of extra input methods; imextra: {}, // Global input method data @@ -1686,47 +1686,56 @@ MIM.im_domain.DefType (MIM.State.prototype); return (! this.key_unhandled && this.produced.length == 0); } - } + }; - // Load the list of input methods. - var node = Xex.Load (MIM.server, "imlist.xml"); - for (node = node.firstChild; node; node = node.nextSibling) - if (node.nodeName == 'input-method') - { - var lang = null, name = null, extra_id = null, file = null; + MIM.Load = function () { + if (MIM.imlist) + return; + MIM.imlist = {}; + // Load the list of input methods. + var node = Xex.Load (MIM.server, "imlist.xml"); + for (node = node.firstChild; node; node = node.nextSibling) + if (node.nodeName == 'input-method') + { + var lang = null, name = null, extra_id = null, file = null; - for (var n = node.firstChild; n; n = n.nextSibling) - { - if (n.nodeName == 'language') - lang = n.firstChild.nodeValue; - else if (n.nodeName == 'name') - name = n.firstChild.nodeValue; - else if (n.nodeName == 'extra-id') - extra_id = n.firstChild.nodeValue; - else if (n.nodeName == 'filename') - file = n.firstChild.nodeValue; - } - 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); - } + for (var n = node.firstChild; n; n = n.nextSibling) + { + if (n.nodeName == 'language') + lang = n.firstChild.nodeValue; + else if (n.nodeName == 'name') + name = n.firstChild.nodeValue; + else if (n.nodeName == 'extra-id') + extra_id = n.firstChild.nodeValue; + else if (n.nodeName == 'filename') + file = n.firstChild.nodeValue; + } + if ((lang == 'ja' && name == 'anthy') + || (lang == 'en' && name == 'ispell')) + continue; + 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; } - 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; + node = undefined; + MIM.current = MIM.imlist['t']['latn-post']; + } }) (); (function () { @@ -2106,6 +2115,7 @@ MIM.keydown = function (event) if (! (target.type == "text" || target.type == "textarea")) return; + MIM.Load (); var ic = target.mim_ic; if (! ic || ic.im != MIM.current) { @@ -2284,6 +2294,9 @@ MIM.keypress = function (event) var nodes = target.getElementsByTagName ('ul'); for (var i = 0; i < nodes.length; i++) nodes[i].style.visibility = 'hidden'; + nodes = target.getElementsByTagName ('pre'); + for (var i = 0; i < nodes.length; i++) + nodes[i].style.visibility = 'hidden'; document.getElementsByTagName ('body')[0].removeChild (target); } } @@ -2316,16 +2329,32 @@ MIM.keypress = function (event) 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'; + var nodes = last_target.getElementsByTagName ('ul'); + for (var i = 0; i < nodes.length; i++) + nodes[i].style.visibility = 'hidden'; + nodes = last_target.getElementsByTagName ('pre'); + for (var i = 0; i < nodes.length; i++) + nodes[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'; + if (false) + { + target.lastChild.style.visibility = 'visible'; + target.lastChild.style.left = target.clientWidth + 'px'; + } + else + { + var left = target.clientWidth; + for (var n = target.firstElement ().nextElement (); n; n = n.nextElement ()) + { + n.style.visibility = 'visible'; + n.style.left = left + 'px'; + left += n.clientWidth; + } + } } event.preventDefault (); } @@ -2353,6 +2382,7 @@ MIM.keypress = function (event) function create_ul (visibility) { var ul = document.createElement ('ul'); + ul.name = 'elm'; ul.style.position = 'absolute'; ul.style.margin = '0px'; ul.style.padding = '0px'; @@ -2380,6 +2410,21 @@ MIM.keypress = function (event) return li; } + function create_sep () + { + var sep = document.createElement ('pre'); + sep.name = 'elm'; + sep.innerHTML = ' '; + sep.style.position = 'absolute'; + sep.style.margin = '0px'; + sep.style.padding = '2px'; + sep.style.border = '2px solid black'; + sep.style.top = '-1px'; + sep.style.backgroundColor = 'black'; + sep.style.visibility = 'hidden'; + return sep; + } + var menu; function create_menu (event) @@ -2391,6 +2436,7 @@ MIM.keypress = function (event) return; if (! menu) { + MIM.Load (); categorize_im (); menu = create_ul ('visible'); menu.style.fontFamily = 'sans-serif'; @@ -2403,6 +2449,7 @@ MIM.keypress = function (event) { var cat = lang_category[catname]; var li = create_li (1, catname); + li.appendChild (create_sep ()); var sub = create_ul ('hidden'); for (var langname in cat) { @@ -2411,6 +2458,7 @@ MIM.keypress = function (event) continue; var sub_li = create_li (2, lang.name); sub_li.parentLi = li; + sub_li.appendChild (create_sep ()); var subsub = create_ul ('hidden'); for (var name in lang.list) { @@ -2438,6 +2486,5 @@ MIM.keypress = function (event) MIM.add_event_listener (window, 'keydown', MIM.keydown); MIM.add_event_listener (window, 'keypress', MIM.keypress); MIM.add_event_listener (window, 'mousedown', create_menu); - MIM.current = MIM.imlist['t']['latn-post']; }; }) (); diff --git a/mim2.js b/mim2.js new file mode 100644 index 0000000..7f78838 --- /dev/null +++ b/mim2.js @@ -0,0 +1,2488 @@ +// mim.js -- M17N Input Method driver +// Copyright (C) 2010 +// National Institute of Advanced Industrial Science and Technology (AIST) +// Registration Number H15PRO112 + +// This file is part of the m17n database; a sub-part of the m17n +// library. + +// The m17n library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. + +// The m17n library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with the m17n library; if not, write to the Free +// Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +// Please note that the code is not yet matured. + +var MIM = { + // URL of the input method server. + server: "http://www.m17n.org/common/mim-js", + // Boolean flag to tell if MIM is active or not. + enabled: true, + // Boolean flag to tell if MIM is running in debug mode or not. + debug: false, + // List of main input methods. + imlist: {}, + // List of extra input methods; + imextra: {}, + // Global input method data + im_global: null, + // Currently selected input method. + current: false, + + // enum + LoadStatus: { NotLoaded:0, Loading:1, Loaded:2, Error:-1 }, + ChangedStatus: { + None: 0x00, + StateTitle: 0x01, + PreeditText:0x02, + CursorPos: 0x04, + CandidateList:0x08, + CandidateIndex:0x10, + CandidateShow:0x20, + Preedit: 0x06, // PreeditText | CursorPos + Candidate: 0x38 // CandidateList | CandidateIndex | CandidateShow + }, + KeyModifier: { + SL: 0x00400000, + SR: 0x00800000, + S: 0x00C00000, + CL: 0x01000000, + CR: 0x02000000, + C: 0x03000000, + AL: 0x04000000, + AR: 0x08000000, + A: 0x0C000000, + ML: 0x04000000, + MR: 0x08000000, + M: 0x0C000000, + G: 0x10000000, + s: 0x20000000, + H: 0x40000000, + High: 0x70000000, + All: 0x7FC00000 + }, + Error: { + ParseError: "parse-error" + } +}; + +//if (window.location.hostname == 'localhost') +// MIM.server = 'http://localhost/mim'; + +(function () { + var keysyms = new Array (); + keysyms["bs"] = "BackSpace"; + keysyms["lf"] = "Linefeed"; + keysyms["cr"] = keysyms["enter"] = "Return"; + keysyms["esc"] = "Escape"; + keysyms["spc"] = "space"; + 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; + + for (i = 0; i < len - 1; i++) + if (! MIM.KeyModifier.hasOwnProperty (parts[i])) + return false; + var key = parts[len - 1]; + if (key.length > 1) + { + key = keysyms[key.toLowerCase ()]; + if (key) + { + if (len > 1) + { + str = parts[0]; + for (i = 1; i < len - 1; i++) + str += '-' + parts[i]; + str += '-' + key; + } + else + str = key; + } + } + if (has_modifier) + { + parts = new Array (); + parts.push (str); + return parts; + } + return str; + } + + MIM.Key = function (val) + { + this.key; + 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) + throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val); + if (this.key instanceof Array) + { + this.key = this.key[0]; + this.has_modifier = true; + } + } + else if (typeof val == 'number' || val instanceof Number) + this.key = String.fromCharCode (val); + else + throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val); + } + + 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 (); + + if (seq) + { + if (seq.IsList) + { + var len = seq.val.length; + for (var i = 0; i < len; i++) + { + 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); + this.val.push (key); + if (key.has_modifier) + this.has_modifier = true; + } + } + else if (seq.IsStr) + { + var len = seq.val.length; + for (var i = 0; i < len; i++) + this.val.push (new MIM.Key (seq.val.charCodeAt (i))); + } + else + throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + seq); + } + } + + var proto = new Xex.Term ('keyseq'); + proto.Clone = function () { return this; } + proto.Parser = function (domain, node) + { + var seq = new Array (); + for (node = node.firstChild; node; node = node.nextSibling) + if (node.nodeType == 1) + { + var term = Xex.Term.Parse (domain, node); + return new MIM.KeySeq (term); + } + throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid keyseq"); + } + proto.toString = function () + { + var len = this.val.length; + if (len == 0) + return ''; + var first = true; + var str = ''; + for (var i = 0; i < len; i++) + { + if (first) + first = false; + else if (this.has_modifier) + str += ' '; + str += this.val[i].toString (); + } + return str + ''; + } + + MIM.KeySeq.prototype = proto; +}) (); + +(function () { + MIM.Marker = function () { } + MIM.Marker.prototype = new Xex.Term ('marker'); + MIM.Marker.prototype.CharAt = function (ic) + { + var p = this.Position (ic); + if (p < 0 || p >= ic.preedit.length) + return 0; + return ic.preedit.charCodeAt (p); + } + + 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 (); + MIM.PredefinedMarker.prototype.Position = function (ic) + { + if (typeof this.pos == 'number') + return this.pos; + return this.pos (ic); + } + + var predefined = { } + + function def_predefined (name, position) + { + predefined[name] = new MIM.PredefinedMarker (name); + predefined[name].pos = position; + } + + def_predefined ('@<', 0); + def_predefined ('@>', function (ic) { return ic.preedit.length; }); + def_predefined ('@-', function (ic) { return ic.cursor_pos - 1; }); + def_predefined ('@+', function (ic) { return ic.cursor_pos + 1; }); + def_predefined ('@[', function (ic) { + if (ic.cursor_pos > 0) + { + var pos = ic.cursor_pos; + return ic.preedit.FindProp ('candidates', pos - 1).from; + } + return 0; + }); + def_predefined ('@]', function (ic) { + if (ic.cursor_pos < ic.preedit.length - 1) + { + var pos = ic.cursor_pos; + return ic.preedit.FindProp ('candidates', pos).to; + } + return ic.preedit.length; + }); + for (var i = 0; i < 10; i++) + def_predefined ("@" + i, i); + predefined['@first'] = predefined['@<']; + predefined['@last'] = predefined['@>']; + predefined['@previous'] = predefined['@-']; + predefined['@next'] = predefined['@+']; + predefined['@previous-candidate-change'] = predefined['@[']; + predefined['@next-candidate-change'] = predefined['@]']; + + MIM.SurroundMarker = function (name) + { + this.val = name; + this.distance = parseInt (name.slice (1)); + if (isNaN (this.distance)) + throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name); + } + MIM.SurroundMarker.prototype = new MIM.Marker (); + MIM.SurroundMarker.prototype.Position = function (ic) + { + 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) + { + var name = node.firstChild.nodeValue; + if (name.charAt (0) == '@') + { + var n = predefined[name]; + if (n) + return n; + 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.FloatingMarker (name);; + } +}) (); + +MIM.Selector = function (name) +{ + this.val = name; +} +MIM.Selector.prototype = new Xex.Term ('selector'); + +(function () { + var selectors = {}; + selectors["@<"] = selectors["@first"] = new MIM.Selector ('@<'); + selectors["@="] = selectors["@current"] = new MIM.Selector ('@='); + selectors["@>"] = selectors["@last"] = new MIM.Selector ('@>'); + selectors["@-"] = selectors["@previous"] = new MIM.Selector ('@-'); + selectors["@+"] = selectors["@next"] = new MIM.Selector ('@+'); + selectors["@["] = selectors["@previous-group"] = new MIM.Selector ('@['); + selectors["@]"] = selectors["@next-group"] = new MIM.Selector ('@]'); + + MIM.Selector.prototype.Parser = function (domain, node) + { + var name = node.firstChild.nodeValue; + var s = selectors[name]; + if (! s) + throw new Xex.ErrTerm (MIM.Error.ParseError, + "Invalid selector: " + name); + return s; + } +}) (); + +MIM.Rule = function (keyseq, actions) +{ + this.keyseq = keyseq; + if (actions) + this.actions = actions; +} +MIM.Rule.prototype = new Xex.Term ('rule'); +MIM.Rule.prototype.Parser = function (domain, node) +{ + var n; + 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.Term.Parse (domain, n); + if (keyseq.type != 'keyseq') + throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node); + 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 () +{ + return ''; +} + +MIM.Map = function (name) +{ + this.name = name; + this.rules = new Array (); +}; + +(function () { + var proto = new Xex.Term ('map'); + + proto.Parser = function (domain, node) + { + var name = node.attributes['mname'].nodeValue; + if (! name) + throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map"); + var map = new MIM.Map (name); + for (var n = node.firstChild; n; n = n.nextSibling) + if (n.nodeType == 1) + map.rules.push (Xex.Term.Parse (domain, n)); + return map; + } + + proto.toString = function () + { + var str = ''; + var len = this.rules.length; + for (i = 0; i < len; i++) + str += this.rules[i]; + return str + ''; + } + + MIM.Map.prototype = proto; +}) (); + +Xex.CatchTag._mimtag = new Xex.SymTerm ('@mimtag'); + +MIM.Action = function (domain, terms) +{ + var args = new Array (); + args.push (Xex.CatchTag_.mimtag); + for (var i = 0; i < terms.length; i++) + args.push (terms[i]); + this.action = Xex.Funcall.prototype.New (domain, 'catch', null, args); +} + +MIM.Action.prototype.Run = function (domain) +{ + var result = this.action.Eval (domain); + if (result.type == 'error') + { + domain.context.Error = result.toString (); + return false; + } + return (result != Xex.CatchTag._mimtag); +} + +MIM.Keymap = function () +{ + this.name = 'TOP'; + this.submaps = null; +}; + +(function () { + var proto = {}; + + 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 = false; + + name += key.key; + if (! keymap.submaps) + keymap.submaps = {}; + else + sub = keymap.submaps[key.key]; + if (! sub) + keymap.submaps[key.key] = sub = new MIM.Keymap (); + keymap = sub; + keymap.name = name; + } + if (rule.actions) + keymap.map_actions = rule.actions; + if (branch_actions) + keymap.branch_actions = branch_actions; + } + + 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], 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++; + return sub.Lookup (keys, index); + } + return { map: this, index: index }; + } + + MIM.Keymap.prototype = proto; +}) (); + +MIM.State = function (name) +{ + this.name = name; + this.keymap = new MIM.Keymap (); +}; + +(function () { + var proto = new Xex.Term ('state'); + + proto.Parser = function (domain, node) + { + var map_list = domain.map_list; + var name = node.attributes['sname'].nodeValue; + if (! name) + throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map"); + var state = new MIM.State (name); + for (node = node.firstElement (); node; node = node.nextElement ()) + { + if (node.nodeName == 'title') + state.title = node.firstChild.nodeValue; + 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], + branch_actions); + else if (node.nodeName == 'state-hook') + state.keymap.map_actions = branch_actions; + else if (node.nodeName == 'catch-all-branch') + state.keymap.branch_actions = branch_actions; + } + } + return state; + } + + proto.toString = function () + { + return '' + this.keymap + ''; + } + + MIM.State.prototype = proto; +}) (); + +(function () { + 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)); + } + + MIM.Candidates = function (ic, candidates, column) + { + this.ic = ic; + 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 (); + } + } + + function get_col () + { + 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.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 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) + { + 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 (false); + 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 ()) + { + var vname = node.attributes['vname'].nodeValue; + if (this != MIM.im_global) + { + var vari = get_global_var (vname); + if (vari != null) + this.domain.Defvar (vname, vari.desc, vari.val, vari.range); + } + 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 mname in im.map_list) + this.map_list[mname] = im.map_list[mname]; + } + 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 == '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) + state.title = this.title; + if (! this.initial_state) + this.initial_state = state; + this.state_list[state.name] = state; + } + } + delete this.domain.map_list; + } + + MIM.IM = function (lang, name, extra_id, file) + { + this.lang = lang; + this.name = name; + this.extra_id = extra_id; + this.file = file; + this.load_status = MIM.LoadStatus.NotLoaded; + this.domain = new Xex.Domain (this.lang + '-' + + (this.name != 'nil' + ? this.name : this.extra_id), + MIM.im_domain, null); + } + + var proto = { + Load: function () + { + var node = Xex.Load (MIM.server, this.file); + if (! node) + { + this.load_status = MIM.LoadStatus.Error; + return false; + } + this.map_list = {}; + this.initial_state = null; + this.state_list = {}; + for (node = node.firstElement (); node; node = node.nextElement ()) + { + var name = node.nodeName; + var parser = parsers[name]; + if (parser) + parser.call (this, node); + } + this.load_status = MIM.LoadStatus.Loaded; + return true; + } + } + + MIM.IM.prototype = proto; + + 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 (false); + } + + MIM.CandidateTable = function () + { + this.table = new Array (); + } + + MIM.CandidateTable.prototype.get = function (pos) + { + for (var i = 0; i < this.table.length; i++) + { + var elt = this.table[i]; + if (elt.from < pos && pos <= elt.to) + 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 < to && elt.to > from) + { + 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); + if (diff == 0) + return; + 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 set_cursor (prefix, pos) + { + this.cursor_pos = pos; + var candidates = this.candidate_table.get (pos); + if (this.candidates != candidates) + { + this.candidates = candidates; + this.changed |= MIM.ChangedStatus.CandidateList; + } + } + + function save_state () + { + this.state_var_values = this.domain.SaveValues (); + this.state_preedit = this.preedit; + this.state_key_head = this.key_head; + this.state_pos = this.cursor_pos; + } + + function restore_state () + { + this.domain.RestoreValues (this.state_var_values); + this.preedit = this.state_preedit; + set_cursor.call (this, "restore", this.state_pos); + } + + function handle_key () + { + 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; + + if (out.index > this.key_head) + { + this.key_head = out.index; + Xex.Log (' with submap', -1); + restore_state.call (this); + this.keymap = sub; + if (sub.map_actions) + { + Xex.Log ('taking map actions:'); + if (! this.take_actions (sub.map_actions)) + return false; + } + else if (sub.submaps) + { + Xex.Log ('no map actions'); + for (var i = this.state_key_head; i < this.key_head; i++) + { + Xex.Log ('inserting key:' + this.keys.val[i].key); + this.ins (this.keys.val[i].key, null); + } + } + if (! sub.submaps) + { + Xex.Log ('terminal:'); + if (this.keymap.branch_actions) + { + Xex.Log ('branch actions:'); + if (! this.take_actions (this.keymap.branch_actions)) + return false; + } + if (sub != this.state.keymap) + this.shift (this.state); + } + } + else + { + Xex.Log (' without submap', -1); + this.keymap = sub; + var current_state = this.state; + var map = this.keymap; + + if (map.branch_actions) + { + Xex.Log ('branch actions:'); + if (! this.take_actions (map.branch_actions)) + return false; + } + + if (map == this.keymap) + { + Xex.Log ('no state change'); + if (map == this.initial_state.keymap + && this.key_head < this.keys.val.length) + { + Xex.Log ('unhandled'); + return false; + } + if (map != current_state.keymap) + this.shift (current_state); + else if (this.keymap.actions == null) + this.shift (this.initial_state); + } + } + return true; + } + + MIM.IC.prototype = { + reset: function (clear_keys) + { + this.cursor_pos = 0; + this.prev_state = null; + this.title = this.initial_state.title; + this.state_preedit = ''; + this.state_key_head = 0; + this.state_var_values = {}; + this.state_pos = 0; + this.key_head = 0; + if (clear_keys) + this.keys.val.length = 0; + this.commit_key_head = 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 = ''; + this.preedit = ''; + this.preedit_saved = ''; + if (this.candidate_show) + MIM.hide (this); + 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); + }, + + catch_args: new Array (Xex.CatchTag._mimtag, null), + + 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); + var term = new Xex.Funcall (func_catch, null, this.catch_args); + term = term.Eval (this.domain); + return (! term.IsSymbol || term.val != '@mimtag'); + }, + + GetSurroundingChar: function (pos) + { + if (pos < 0) + { + pos += this.range[0]; + if (pos < 0) + return -1; + } + else + { + pos += this.range[1]; + if (pos >= this.target.value.length) + return -1; + } + 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 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.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 + newlen, candidates) + if (this.cursor_pos >= to) + set_cursor.call (this, 'adjust', this.cursor_pos + text.length - (to - from)); + else if (this.cursor_pos > from) + set_cursor.call (this, 'adjust', from) + }, + + ins: function (text, candidates) + { + this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates); + }, + + 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 < this.cursor_pos) + { + 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) + { + 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); + } + return deleted; + }, + + show: function () + { + this.candidate_show = true; + this.changed |= MIM.ChangedStatus.CandidateShow; + }, + + hide: function () + { + this.candidate_show = false; + this.changed |= MIM.ChangedStatus.CandidateShow; + }, + + move: function (pos) + { + if (pos < 0) + pos = 0; + else if (pos > this.preedit.length) + pos = this.preedit.length; + if (pos != this.cursor_pos) + { + set_cursor.call (this, 'move', pos); + this.changed |= MIM.ChangedStatus.Preedit; + } + }, + + pushback: function (n) + { + if (n instanceof MIM.KeySeq) + { + if (this.key_head > 0) + this.key_head--; + if (this.key_head < this.keys.val.length) + this.keys.val.splice (this.key_head, + this.keys.val.length - this.key_head); + for (var i = 0; i < n.val.length; i++) + this.keys.val.push (n.val[i]); + return; + } + if (n > 0) + { + this.key_head -= n; + if (this.key_head < 0) + this.key_head = 0; + } + else if (n == 0) + this.key_head = 0; + else + { + this.key_head = - n; + if (this.key_head > this.keys.val.length) + this.key_head = this.keys.val.length; + } + }, + + pop: function () + { + if (this.key_head < this.keys.val.length) + this.keys.val.splice (this.key_head, 1); + }, + + commit: function () + { + if (this.preedit.length > 0) + { + 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; + } + }, + + shift: function (state) + { + if (state == null) + { + if (this.prev_state == null) + return; + state = this.prev_state; + } + + if (state == this.initial_state) + { + if (this.state) + { + this.commit (); + this.keys.val.splice (0, this.key_head); + this.key_head = this.state_key_head = this.commit_key_head = 0; + this.prev_state = null; + } + } + else + { + if (state != this.state) + this.prev_state = this.state; + } + if (state != this.state && state.keymap.map_actions) + this.take_actions (state.keymap.map_actions); + if (! this.state || this.state.title != state.title) + this.changed |= MIM.ChangedStatus.StateTitle; + this.state = state; + this.keymap = state.keymap; + save_state.call (this); + }, + + Filter: function (key) + { + if (! this.active) + { + Xex.Log ("active = false"); + this.key_unhandled = true; + this.unhandled_key = key; + return false; + } + if (key.key == '_reload') + return true; + this.changed = MIM.ChangedStatus.None; + this.produced = ''; + this.key_unhandled = false; + this.keys.val.push (key); + var count = 0; + while (this.key_head < this.keys.val.length) + { + if (! handle_key.call (this)) + { + 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); + } + 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; + } + if (++count == 10) + { + this.reset (true); + this.key_unhandled = true; + 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); + } + }; + + MIM.create_list = function (node) { + // Load the list of input methods. + MIM.A = node; + alert ('create-list called: node=' + MIM.A); + var node = node.firstElement; + for (node = node.firstChild; node; node = node.nextSibling) + if (node.nodeName == 'input-method') + { + var lang = null, name = null, extra_id = null, file = null; + + for (var n = node.firstChild; n; n = n.nextSibling) + { + if (n.nodeName == 'language') + lang = n.firstChild.nodeValue; + else if (n.nodeName == 'name') + name = n.firstChild.nodeValue; + else if (n.nodeName == 'extra-id') + extra_id = n.firstChild.nodeValue; + else if (n.nodeName == 'filename') + file = n.firstChild.nodeValue; + } + if ((lang == 'ja' && name == 'anthy') + || (lang == 'en' && name == 'ispell')) + continue; + 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; + MIM.current = MIM.imlist['t']['latn-post']; + } +}) (); + +(function () { + var keys = new Array (); + keys[0x09] = 'Tab'; + keys[0x08] = 'BackSpace'; + keys[0x0D] = 'Return'; + keys[0x1B] = 'Escape'; + keys[0x20] = 'space'; + keys[0x21] = 'Page_Up'; + keys[0x22] = 'Page_Down'; + 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] = "Num_Lock"; + keys[0xF0] = "Caps_Lock"; + + var keyids = {}; + keyids['U+0008'] = 'BackSpace'; + keyids['U+0009'] = 'Tab'; + keyids['U+0018'] = 'Cancel'; + keyids['U+001B'] = 'Escape'; + keyids['U+0020'] = 'space'; + keyids['U+007F'] = 'Delete'; + keyids['PageUp'] = 'Page_Up'; + keyids['PageDown'] = 'Page_Down'; + + 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 = ''; + var shifted = event.shiftKey; + 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; + var c = parseInt (RegExp.$1, 16); + // Chrome sets, for instance, "U+0x00C1" when the key 'A' + // is typed with control or alt/meta key. + if (c >= 0x80) + c -= 0x80; + if (c >= 0x41 && c <= 0x5A && ! event.shiftKey) + c += 0x20; + key = String.fromCharCode (c); + shifted = false; + } + if (shifted) 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); + } +}) (); + +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 () { }; + +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 (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; + ic.reset (true); + ic.range[0] = from; + ic.range[1] = to; + return false; +}; + +(function () { + var style_props = { + width: 'width', + height: 'height', + padingLeft: 'padding-left', + paddingRight: 'padding-right', + paddingTop: 'padding-top', + paddintBottom: 'padding-bottom', + marginRight: 'margin-right', + marginTop: 'margin-top', + 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[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; + return from_style; + } + + var temp; // Temporary 'div' element + + 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) + { + var styles = copy_style (target, temp); + ic.abs_top = (parseInt (styles.marginTop) + + parseInt (styles.borderTopWidth) + + parseInt (styles.paddingTop)); + ic.abs_left = (parseInt (styles.marginLeft) + + parseInt (styles.borderLeftWidth) + + parseInt (styles.paddingLeft)); + for (var elm = target.offsetParent; elm; elm = elm.offsetParent) + { + ic.abs_top += elm.offsetTop; + ic.abs_left += elm.offsetLeft; + } + temp.ic = ic; + } + temp.firstChild.innerText = target.value.substring (0, ic.range[0]); + temp.lastChild.innerText + = (ic.range[0] == ic.range[1] ? "|" + : target.value.substring (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.update_bar = function (target, ic) +{ + if (ic.preedit.length > 0) + { + 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 + 'px'; + ic.bar.style.left = ic.abs_x0 + 'px'; + ic.bar.style.minWidth = ic.bar.style.maxWidth = (ic.abs_x1 - ic.abs_x0) + 'px'; + } + 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.update_bar (target, ic); + if (! for_focus_out) + { + var pos = ic.range[0] + ic.cursor_pos; + if (target.setSelectionRange) // Mozilla + { + var scrollTop = target.scrollTop; + target.setSelectionRange (pos, pos); + target.scrollTop = scrollTop; + } + else // IE + { + var range = target.createTextRange (); + range.moveStart ('character', pos); + range.moveEnd ('character', pos); + range.select (); + } + } +}; + +(function () { + MIM.show = function (ic) + { + if (! ic.candidates) + return; + var target = ic.target; + MIM.get_preedit_pos (target, ic); + if (! ic.can_node) + { + ic.can_node = document.createElement ('table'); + ic.can_node.style.position = 'absolute'; + ic.can_node.style.display = 'none'; + ic.can_node.style.backgroundColor = "white"; + ic.can_node.style.border = "1px solid black"; + document.getElementsByTagName ('body')[0].appendChild (ic.can_node); + } + + if (ic.changed & MIM.ChangedStatus.CandidateList) + { + while (ic.can_node.childNodes.length > 0) + ic.can_node.removeChild (ic.can_node.firstChild); + var tr = document.createElement ('tr'); + var group = ic.candidates.CurrentGroup (); + var td = document.createElement ('td'); + td.innerHTML = group[0][1] + '/' + group[0][0]; + td.style.color = 'white'; + td.style.backgroundColor = 'black'; + tr.appendChild (td); + for (var i = 1; i < group.length; i++) + { + var td = document.createElement ('td'); + td.noWrap = true; + td.innerHTML = (i < 10 ? i : i == 10 ? '0' : String.fromCharCode (0x60 + (i - 10))) + '.' + group[i]; + if (i == group[0][2] + 1) + td.style.backgroundColor = 'lightblue'; + tr.appendChild (td); + } + ic.can_node.appendChild (tr); + ic.can_node.style.top = (ic.abs_y + 10) + 'px'; + ic.can_node.style.left = ic.abs_x0 + 'px'; + } + else + { + var td = ic.can_node.firstElement ().firstElement ().nextElement (); + var col = ic.candidates.CurrentCol (); + for (var i = 0; td; td = td.nextElement ()) + td.style.backgroundColor = (i++ == col ? 'lightblue' : 'white'); + } + ic.can_node.style.display = 'block'; + } + + MIM.hide = function (ic) + { + if (ic.can_node) + ic.can_node.style.display = 'none'; + } + + function real_focus_in (target, ic) + { + if (MIM.get_range (target, ic)) + ic.Filter (MIM.Key.FocusIn); + MIM.update (target, ic, false); + } + + var focus_in_timer; + + MIM.focus_in = function (event) + { + var target = event.target; + Xex.Log ("Focus in " + target.tagName); + focus_in_timer + = setTimeout (function () {real_focus_in (target, target.mim_ic)} , 10); + return true; + } + + MIM.click = function (event) + { + var target = event.target; + Xex.Log ("Click in " + target.tagName); + if (focus_in_timer) + { + clearTimeout (focus_in_timer); + focus_in_timer = null; + } + real_focus_in (target, target.mim_ic); + } + +}) (); + +MIM.focus_out = function (event) +{ + var target = event.target; + var ic = target.mim_ic; + Xex.Log ("Focus out " + target.tagName); + MIM.get_range (target, ic); + MIM.debug_print (event, ic); + ic.Filter (MIM.Key.FocusOut); + MIM.update (target, ic, true); + return true; +}; + +MIM.keydown = function (event) +{ + var target = event.target; + 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 true; + target.mim_ic = ic; + MIM.add_event_listener (target, 'focus', MIM.focus_in); + MIM.add_event_listener (target, 'blur', MIM.focus_out); + MIM.add_event_listener (target, 'click', MIM.click); + } + 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) +{ + var target = event.target; + 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 (true); + return; + } + + 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; +}; + +(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); + } + } + + var destroy_timer; + var last_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'; + nodes = target.getElementsByTagName ('pre'); + for (var i = 0; i < nodes.length; i++) + nodes[i].style.visibility = 'hidden'; + document.getElementsByTagName ('body')[0].removeChild (target); + } + } + + function destroy_menu () { + if (! destroy_timer) + destroy_timer = setTimeout (destroy, 1000); + return true; + } + + function show_submenu (event) + { + var target = event.target; + if (! target.menu_level) + { + if (! target.parentNode || ! target.parentNode.menu_level) + return true; + target = target.parentNode; + } + if (destroy_timer) + { + clearTimeout (destroy_timer); + destroy_timer = null; + } + if (last_target && target.parentLi != last_target) + { + last_target.style.backgroundColor = 'white'; + while (target.menu_level < last_target.menu_level) + { + last_target = last_target.parentLi; + last_target.style.backgroundColor = 'white'; + } + var nodes = last_target.getElementsByTagName ('ul'); + for (var i = 0; i < nodes.length; i++) + nodes[i].style.visibility = 'hidden'; + nodes = last_target.getElementsByTagName ('pre'); + for (var i = 0; i < nodes.length; i++) + nodes[i].style.visibility = 'hidden'; + } + last_target = target; + target.style.backgroundColor = 'yellow'; + if (target.menu_level < 3) + { + if (false) + { + target.lastChild.style.visibility = 'visible'; + target.lastChild.style.left = target.clientWidth + 'px'; + } + else + { + var left = target.clientWidth; + for (var n = target.firstElement ().nextElement (); n; n = n.nextElement ()) + { + n.style.visibility = 'visible'; + n.style.left = left + 'px'; + left += n.clientWidth; + } + } + } + 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.name = 'elm'; + 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; + } + + function create_sep () + { + var sep = document.createElement ('pre'); + sep.name = 'elm'; + sep.innerHTML = ' '; + sep.style.position = 'absolute'; + sep.style.margin = '0px'; + sep.style.padding = '2px'; + sep.style.border = '2px solid black'; + sep.style.top = '-1px'; + sep.style.backgroundColor = 'black'; + sep.style.visibility = 'hidden'; + return sep; + } + + 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); + li.appendChild (create_sep ()); + 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; + sub_li.appendChild (create_sep ()); + 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); + Xex.Load (MIM.server, "imlist.xml", MIM.create_list); + }; +}) (); diff --git a/xex.js b/xex.js index 81353b2..e5f9c84 100644 --- a/xex.js +++ b/xex.js @@ -1359,4 +1359,30 @@ Xex.Load = function (server, file) obj.overrideMimeType ('text/xml'); obj.send (''); return (obj.responseXML && obj.responseXML.firstChild); -} +}; + +(function () { + function getxml (event) + { + var parser = new DOMParser (); + Xex.xml = parser.parseFromString (event.data, 'text/xml'); + }; + + Xex.LoadTesting = function (server, file) + { + var body = document.getElementsByTagName ('body')[0]; + Xex.xml = undefined; + window.addEventListener ('message', getxml, false); + var iframe = document.createElement ('iframe'); + iframe.src = server + '/loadxml.html#' + file; + alert ('iframe created'); + body.appendChild (iframe); +/* + while (! Xex.xml) + alert ('wait loading ' + file); + window.removeEventListener ('message', getxml, false); + body.removeChild (iframe); + return (Xex.xml.firstChild); +*/ + } +}) (); diff --git a/xex2.js b/xex2.js new file mode 100644 index 0000000..144e9af --- /dev/null +++ b/xex2.js @@ -0,0 +1,1383 @@ +// xex.js -- Xex (XML Expression) interpreter +// Copyright (C) 2010 +// National Institute of Advanced Industrial Science and Technology (AIST) +// Registration Number H15PRO112 + +// This file is part of the m17n database; a sub-part of the m17n +// library. + +// The m17n library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. + +// The m17n library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with the m17n library; if not, write to the Free +// Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +// Please note that the code is not yet matured. + +var Xex = {}; + +(function () { // Logging + // The logging node containing tracing information. + var log = null; + // Number of lines. + var lines; + // Style properties of the logging node. + var styles = { border: '1px solid black', + font: 'normal normal normal small monospace', + width: '100%', + minHeight: '300px', + maxHeight: '300px', + overflow: 'auto' }; + + // Toggle logging on and off. PARENT if non-null specifies the + // parent of the log node. The log node is appended to PARENT. + // If PARENT is null, 'body' node is assumed. + Xex.LogToggle = function (parent) + { + if (log) + { + log.parentNode.removeChild (log); + log = null; + return; + } + if (! parent) + parent = document.getElementsByTagName ('body')[0]; + log = document.createElement ('ol'); + for (var prop in styles) + log.style[prop] = styles[prop]; + parent.appendChild (log); + lines = 0; + return log; + } + + // Log ARG (string). INDENT if specified is a number of columns to + // indent. If INDENT is -1, ARG is appended to the last log. + Xex.Log = function (arg, indent) + { + if (! log) + return; + if (! arg) + { + while (log.childNodes.length > 0) + log.removeChild (log.firstChild); + lines = 0; + } + else + { + var node; + if (indent == -1) + log.lastChild.innerText += arg; + else + { + lines++; + if (lines >= 256) + { + node = log.firstElement (); + log.start = lines - 254; + } + else + node = document.createElement ('li'); + if (indent != undefined) + node.style.textIndent = (indent + 1) + 'em'; + else + node.style.textIndent = '0px'; + node.innerText = arg; + log.appendChild (node); + log.scrollTop = log.scrollHeight; + } + } + } +}) (); + +Xex.Error = { + UnknownError: "unknown-error", + WrongArgument: "wrong-argument", + // Load time errors. + InvalidInteger: "invalid-integer", + TermTypeInvalid: "term-type-invalid", + FunctionConflict: "function-conflict", + VariableTypeConflict: "variable-type-conflict", + VariableRangeConflict: "variable-range-conflict", + VariableWrongRange: "variable-wrong-range", + VariableWrongValue: "variable-wrong-value", + + UnknownFunction: "unknown-function", + MacroExpansionError: "macro-expansion-error", + NoVariableName: "no-variable-name", + NoFunctionName: "no-funcion-name", + + // Run time errors. + ArithmeticError: "arithmetic-error", + WrongType: "wrong-type", + IndexOutOfRange: "index-out-of-range", + ValueOutOfRange: "value-out-of-range", + NoLoopToBreak: "no-loop-to-break", + UncaughtThrow: "uncaught-throw" +}; + +Xex.Variable = function (name, desc, val, range) +{ + if (name) + this.name = name; + if (desc) + this.desc = desc; + this.val = val; + if (range) + this.range = range; +} + +Xex.Variable.prototype = { + clone: function () + { + return new Xex.Variable (this.name, this.desc, this.val, this.range); + }, + equals: function (obj) + { + return ((obj instanceof Xex.Variable) + && obj.name == this.name); + }, + SetValue: function (term) + { + this.val = term; + return term; + } +} + + +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.Function.apply (this, [name, with_var, min_args, max_args]); + this.builtin = builtin; +} + +Xex.Subrountine.prototype.Call = function (domain, vari, args) +{ + var newargs = new Array (); + for (var i = 0; i < args.length; i++) + { + newargs[i] = args[i].Eval (domain); + if (domain.Thrown ()) + return newargs[i]; + } + return this.builtin (domain, vari, newargs) +} + +Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args) +{ + Xex.Function.apply (this, [name, with_var, min_args, max_args]); + this.builtin = builtin; +} + +Xex.SpecialForm.prototype.Call = function (domain, vari, args) +{ + return this.builtin (domain, vari, args) +} + +Xex.Lambda = function (name, min_args, max_args, args, body) +{ + Xex.Function.apply (this, [name, false, min_args, max_args]); + this.args = args; + this.body = body; +} + +Xex.Lambda.prototype.Call = function (domain, vari, args) +{ + var current = domain.bindings; + var result = Xex.Zero; + var limit = max_args >= 0 ? args.length : args.length - 1; + var i; + + try { + for (i = 0; i < limit; i++) + { + result = args[i].Eval (domain); + if (domain.Thrown ()) + return result; + domain.Bind (this.args[i], result); + } + if (max_args < 0) + { + var list = new Array (); + for (i = 0; i < args[limit].length; i++) + { + result = args[limit].Eval (domain); + if (domain.Thrown ()) + return result; + list[i] = result; + } + domain.Bind (this.args[limit], list); + } + try { + domain.Catch (Xex.CatchTag.Return); + for (var term in this.body) + { + result = term.Eval (domain); + if (domain.Thrown ()) + return result; + } + } finally { + domain.Uncatch (); + } + } finally { + domain.UnboundTo (current); + } + return result; +} + +Xex.Macro = function (name, min_args, max_args, args, body) +{ + Xex.Function.apply (this, [name, false, min_args, max_args]); + this.args = args; + this.body = body; +} + +Xex.Macro.prototype.Call = function (domain, vari, args) +{ + var current = domain.bindings; + var result = Xex.Zero; + var i; + + try { + for (i = 0; i < args.length; i++) + domain.Bind (this.args[i], args[i]); + try { + domain.Catch (Xex.CatchTag.Return); + for (var i in this.body) + { + result = this.body[i].Eval (domain); + if (domain.Thrown ()) + break; + } + } finally { + domain.Uncatch (); + } + } finally { + domain.UnboundTo (current); + } + return result; +} + +Xex.Bindings = function (vari) +{ + this.vari = vari; + this.old_value = vari.val; +} + +Xex.Bindings.prototype.UnboundTo = function (boundary) +{ + for (var b = this; b != boundary; b = b.next) + b.vari.val = b.old_value; + return boundary; +} + +Xex.Bind = function (bindings, vari, val) +{ + var b = new Xex.Bindings (vari); + b.vari.val = val; + b.next = bindings; + return b; +} + +Xex.CatchTag = { + Return: 0, + Break: 1 +} + +Xex.Domain = function (name, parent, context) +{ + this.name = name; + this.context = context; + this.depth = 0; + + if (name != 'basic' && ! parent) + parent = Xex.BasicDomain + this.parent = parent; + this.termtypes = {}; + this.functions = {}; + this.variables = {}; + if (parent) + { + var elt; + for (elt in parent.termtypes) + this.termtypes[elt] = parent.termtypes[elt]; + for (elt in parent.functions) + this.functions[elt] = parent.functions[elt]; + for (elt in parent.variables) + { + var vari = parent.variables[elt]; + this.variables[elt] = new Xex.Variable (vari.name, vari.desc, + vari.val, vari.range); + } + } + + this.call_stack = new Array (); + this.bindings = null; + this.catch_stack = new Array (); + this.catch_count = 0; + this.caught = false; +}; + +Xex.Domain.prototype = { + CallStackCount: function () { return this.call_stack.length; }, + CallStackPush: function (term) { this.call_stack.push (term); }, + CallStackPop: function () { this.call_stack.pop (); }, + Bind: function (vari, val) + { + this.bindings = Xex.Bind (this.bindings, vari, val); + }, + UnboundTo: function (boundary) + { + if (this.bindings) + this.bindings = this.bindings.UnboundTo (boundary); + }, + Catch: function (tag) { this.catch_stack.push (tag); this.catch_count++; }, + Uncatch: function () + { + this.catch_stack.pop (); + if (this.catch_count > this.catch_stack.length) + this.catch_count--; + }, + Thrown: function () + { + if (this.catch_count < this.catch_stack.length) + { + this.caught = (this.catch_count == this.catch_stack.length - 1); + return true; + } + this.caught = false; + return false; + }, + ThrowReturn: function () + { + for (var i = this.catch_stack.length - 1; i >= 0; i--) + { + this.catch_count--; + if (this.catch_stack[i] == Xex.CatchTag.Return) + break; + } + }, + ThrowBreak: function () + { + if (this.catch_stack[this.catch_stack.length - 1] != Xex.CatchTag.Break) + throw new Xex.ErrTerm (Xex.Error.NoLoopToBreak, + "No surrounding loop to break"); + this.catch_count--; + }, + ThrowSymbol: function (tag) + { + var i = this.catch_count; + for (var j = this.catch_stack.length - 1; j >= 0; j--) + { + i--; + if (Xex.CatchTag.Matches (this.catch_stack[i], tag)) + { + this.catch_count = i; + return; + } + } + throw new Xex.ErrTerm (Xex.Error.UncaughtThrow, + "No corresponding catch: " + tag); + }, + DefType: function (obj) + { + var type = obj.type; + if (this.termtypes[type]) + throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid, + "Already defined: " + type); + if (this.functions[type]) + throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid, + "Already defined as a funciton or a macro: " + + type); + this.termtypes[type] = obj.Parser; + }, + DefSubr: function (builtin, name, with_var, min_args, max_args) + { + this.functions[name] = new Xex.Subrountine (builtin, name, with_var, + min_args, max_args); + }, + DefSpecial: function (builtin, name, with_var, min_args, max_args) + { + this.functions[name] = new Xex.SpecialForm (builtin, name, with_var, + min_args, max_args); + }, + Defun: function (name, min_args, max_args, args, body) + { + this.functions[name] = new Xex.Lambda (name, min_args, max_args, + args, body); + }, + 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, + args, body); + }, + DefAlias: function (alias, fname) + { + 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); + this.functions[alias] = func; + }, + Defvar: function (name, desc, val, range) + { + var vari = new Xex.Variable (name, desc, val, range); + this.variables[name] = vari; + return vari; + }, + GetFunc: function (name) + { + var func = this.functions[name]; + if (! func) + throw new Xex.ErrTerm (Xex.Error.UnknownFunction, + "Unknown function: " + name); + return func; + }, + CopyFunc: function (domain, name) + { + var func = this.functions[name]; + domain.DefunByFunc (func); + return true; + }, + CopyFuncAll: function (domain) + { + for (var elt in this.functions) + domain.DefunByFunc (this.functions[elt]); + }, + GetVarCreate: function (name) + { + var vari = this.variables[name]; + if (! vari) + vari = this.variables[name] = new Xex.Variable (name, null, + Xex.Zero, null); + return vari; + }, + GetVar: function (name) { return this.variables[name]; }, + SaveValues: function () + { + values = {}; + for (var elt in this.variables) + values[elt] = this.variables[elt].val.Clone (); + return values; + }, + RestoreValues: function (values) + { + var name; + for (name in values) + { + var vari = this.variables[name]; + vari.val = values[name]; + } + } +}; + +Xex.Term = function (type) { this.type = type; } +Xex.Term.prototype = { + IsTrue: function () { return true; }, + Eval: function (domain) { return this.Clone (); }, + Clone: function (domain) { return this; }, + equals: function (obj) + { + return (this.type == obj.type + && this.val != undefined + && obj.val == this.val); + }, + Matches: function (obj) { return this.equals (obj); }, + toString: function () + { + 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"); } +}; + +Node.prototype.firstElement = function () +{ + for (var n = this.firstChild; n; n = n.nextSibling) + if (n.nodeType == 1) + return n; + return null; +} + +Node.prototype.nextElement = function () +{ + 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 = null, range; + if (vari) + { + desc = vari.description; + val = vari.val; + range = vari.range; + } + 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 == null) + 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') + parse_defun_body (domain, n); + else if (n.nodeName == 'defvar') + parse_defvar (domain, n); + else + { + if (! terms) + terms = new Array (); + terms.push (Xex.Term.Parse (domain, n)); + } + } + return terms; + } +}) (); + +Xex.Varref = function (vname) +{ + this.val = vname; +}; + +(function () { + var proto = new Xex.Term ('varref'); + + proto.Clone = function () { return new Xex.Varref (this.val); } + proto.Eval = function (domain) + { + var vari = domain.GetVarCreate (this.val); + Xex.Log (this.ToString () + '=>' + vari.val, domain.depth); + return vari.val; + } + + proto.Parser = function (domain, node) + { + 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, vname, args) +{ + this.func = func; + this.vname = vname; + this.args = args || null_args; +}; + +(function () { + var proto = new Xex.Term ('funcall'); + + proto.Parser = function (domain, node) + { + var fname = node.nodeName; + var attr; + + if (fname == 'funcall') + fname = node.attributes['fname'].nodeValue; + var func = domain.GetFunc (fname); + var vname; + attr = node.attributes['vname']; + vname = attr != undefined ? attr.nodeValue : null; + var args = Xex.Term.Parse (domain, node.firstElement (), null); + return new Xex.Funcall (func, vname, args); + } + + proto.New = function (domain, fname, vname, args) + { + var func = domain.GetFunc (fname); + var funcall = new Xex.Funcall (func, vname, args); + if (func instanceof Xex.Macro) + funcall = funcall.Eval (domain); + return funcall; + } + + proto.Eval = function (domain) + { + 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 () + { + return new Xex.Funcall (this.func, this.vari, this.args); + } + + proto.equals = function (obj) + { + return (obj.type == 'funcall' + && obj.func == this.func + && obj.vari.equals (this.vari) + && obj.args.length == this.func.length); + } + + proto.toString = function () + { + var arglist = '' + var len = this.args.length; + var str = '<' + this.func.name; + if (this.vname) + str += ' vname="' + this.vname + '"'; + if (len == 0) + 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; +}) (); + +Xex.ErrTerm = function (ename, message, stack) +{ + this.ename = ename; + this.message = message; + this.stack = stack; +}; + +(function () { + var proto = new Xex.Term ('error'); + + proto.IsError = true; + + proto.Parser = function (domain, node) + { + return new Xex.ErrTerm (node.attributes['ename'].nodeValue, + node.innerText, false); + } + + proto.CallStack = function () { return stack; } + + proto.SetCallStack = function (value) { statck = value; } + + proto.Clone = function () + { + return new Xex.ErrTerm (ename, message, false); + } + + proto.equals = function (obj) + { + return (obj.IsError + && obj.ename == ename && obj.message == message + && (obj.stack ? (stack && stack.length == obj.stack.length) + : ! stack)); + } + + proto.Matches = function (obj) + { + return (obj.IsError && obj.ename == ename); + } + + proto.toString = function () + { + return '' + this.message + ''; + } + + Xex.ErrTerm.prototype = proto; +}) (); + +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) + { + var str = node.firstChild.nodeValue; + + if (str.charAt (0) == '?' && str.length == 2) + return new Xex.IntTerm (str.charCodeAt (1)); + return new Xex.IntTerm (parseInt (node.firstChild.nodeValue)); + } + Xex.IntTerm.prototype = proto; +}) (); + +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 ? node.firstChild.nodeValue : ''); + } + Xex.StrTerm.prototype = proto; +}) (); + +Xex.SymTerm = function (str) { this.val = str; }; +(function () { + var proto = new Xex.Term ('symbol'); + proto.IsSymbol = true; + proto.IsTrue = function () { return this.val != 'nil'; } + proto.Clone = function () { return new Xex.SymTerm (this.val); } + proto.Parser = function (domain, node) + { + return new Xex.SymTerm (node.firstChild.nodeValue); + } + Xex.SymTerm.prototype = proto; +}) (); + +Xex.LstTerm = function (list) { this.val = list; }; +(function () { + var proto = new Xex.Term ('list'); + proto.IsList = true; + proto.IsTrue = function () { return this.val.length > 0; } + proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); } + + proto.equals = function (obj) + { + if (obj.type != 'list' || obj.val.length != this.val.length) + return false; + var i, len = this.val.length; + for (i = 0; i < len; i++) + if (! this.val[i].equals (obj.val[i])) + return false; + return true; + } + + proto.Parser = function (domain, node) + { + var list = Xex.Term.Parse (domain, node.firstElement (), null); + return new Xex.LstTerm (list); + } + + proto.toString = function () + { + var len = this.val.length; + + if (len == 0) + return ''; + var str = ''; + for (var i = 0; i < len; i++) + str += this.val[i].toString (); + return str + ''; + } + Xex.LstTerm.prototype = proto; +}) (); + +(function () { + var basic = new Xex.Domain ('basic', null, null); + + function Fset (domain, vari, args) + { + 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) + vari.SetValue (term); + return term; + } + + function Fadd (domain, vari, args) + { + var n = vari ? vari.val.Intval () : 0; + var len = args.length; + + for (var i = 0; i < len; i++) + 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 *= args[i].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; + for (var i = 0; i < len; i++) + { + var result = args[i].Eval (domain); + if (domain.Thrown ()) + return result; + if (! result.IsTrue ()) + return Xex.Zero; + } + return Xex.One; + } + + function For (domain, vari, args) + { + var len = args.length; + for (var i = 0; i < len; i++) + { + var result = args[i].Eval (domain); + if (domain.Thrown ()) + return result; + 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; + var len = args.length; + + for (var i = 0; i < len; i++) + { + result = args[i].Eval (domain); + if (domain.Thrown ()) + return result; + } + return result; + } + + function Fif (domain, vari, args) + { + var result = args[0].Eval (domain); + + if (domain.Thrown ()) + return result; + if (result.IsTrue ()) + return args[1].Eval (domain); + if (args.length == 2) + return Xex.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; + domain.caught = false; + for (var i = idx; i < terms.length; i++) + { + result = terms[i].Eval (domain); + if (domain.Thrown ()) + return result; + } + return result; + } + + function Fcatch (domain, vari, args) + { + var caught = false; + var result; + + if (args[0].IsError) + { + try { + result = eval_terms (domain, args, 1); + } catch (e) { + if (e instanceof Xex.ErrTerm) + { + if (! args[0].Matches (e)) + throw e; + if (vari) + vari.SetValue (e); + return Xex.One; + } + } + } + else if (args[0].IsSymbol) + { + try { + domain.Catch (args[0].val); + result = eval_terms (domain, args, 1); + if (domain.caught) + { + if (vari != null) + vari.SetValue (result); + return Xex.One; + } + return Xex.Zero; + } finally { + domain.Uncatch (); + } + } + throw new Xex.ErrTerm (Xex.Error.WrongArgument, + "Not a symbol nor an error: " + args[0]); + } + + function Fthrow (domain, vari, args) + { + if (args[0].IsSymbl) + { + domain.ThrowSymbol (args[0]); + return (args[args.length - 1]); + } + if (args[0].IsError) + { + throw args[0]; + } + throw new Xex.ErrTerm (Xex.Error.WrongArgument, + "Not a symbol nor an error:" + args[0]); + } + + 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 ("||", "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); + basic.DefType (Xex.Varref.prototype); + basic.DefType (Xex.ErrTerm.prototype); + basic.DefType (Xex.IntTerm.prototype); + basic.DefType (Xex.StrTerm.prototype); + basic.DefType (Xex.SymTerm.prototype); + basic.DefType (Xex.LstTerm.prototype); + +}) (); + +Xex.Zero = new Xex.IntTerm (0); +Xex.One = new Xex.IntTerm (1); +Xex.nil = new Xex.SymTerm ('nil'); + +Xex.LoadOld = function (server, file) +{ + var obj = new XMLHttpRequest (); + var url = server ? server + '/' + file : file; + obj.open ('GET', url, false); + obj.overrideMimeType ('text/xml'); + obj.send (''); + return (obj.responseXML && obj.responseXML.firstChild); +}; + +Xex.Load = function (server, file, callback) +{ + var body = document.getElementsByTagName ('body')[0]; + var iframe = document.createElement ('iframe'); + function receiver (event) + { + alert ('receiver called:'+event.data); + var parser = new DOMParser (); + var xml = parser.parseFromString (event.data, 'text/xml'); + window.removeEventListener ('message', receiver, false); + body.removeChild (iframe); + callback (xml); + }; + Xex.xml = undefined; + window.addEventListener ('message', receiver, false); + iframe.src = server + '/loadxml.html#' + file; + alert ('iframe created'); + body.appendChild (iframe); +}; +