// 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 = '';
}
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);
};
function load_im (node, im)
{
//alert ('Loading IM (' + im + ':' + im.lang + '-' + im.name + ')');
im.map_list = {};
im.initial_state = null;
im.state_list = {};
for (node = node.firstElement (); node; node = node.nextElement ())
{
var name = node.nodeName;
//Xex.Log ('parsing ' + name);
var parser = parsers[name];
if (parser)
parser.call (im, node);
}
//alert ('initial state = ' + im.initial_state);
im.load_status = MIM.LoadStatus.Loaded;
}
MIM.IM.prototype = {
Load: function ()
{
this.load_status = MIM.LoadStatus.Loading;
Xex.Load (MIM.server, this.file, load_im, this);
}
};
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.
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;
MIM.im_global.Load ();
}
else
{
MIM.im_global = new MIM.IM ('t', 'nil', 'global', null);
MIM.im_global.load_status = MIM.LoadStatus.Error;
}
MIM.current = MIM.imlist['t']['latn-post'];
MIM.current.Load ();
}
}) ();
(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 for ' + MIM.current.lang + '-' + MIM.current.name);
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;
MIM.current.Load ();
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);
};
}) ();