/*vietuni.js - V.1.618 - R.11.11.01 @QDJTGSLLA@P*Veni*Vidi*Vici*
* by Tran Anh Tuan [tuan@physik.hu-berlin.de]
* Copyright (c) 2001, 2002 AVYS e.V.. All Rights Reserved.
*
* Originally published and documented at http://www.avys.de/
* You may use this code without fee on noncommercial web sites.
* You may NOT alter the code and then call it another name and/or resell it.
* The copyright notice must remain intact on srcipts.
*/

// interface for HTML:
//


var supported = (document.all || document.getElementById);
var disabled = false;
var charmapid = 1;
var keymodeid = 0;
var linebreak = 0;
var theTyper = null;

reset = function(){}
initTyper = telexingVietUC;

function setTypingMode(mode) {
  keymodeid = mode;
  if (theTyper) theTyper.keymode= initKeys();
  if (!supported && !disabled) {
    alert("Xin loi, trinh duyet web cua ban khong cho phep dung VietTyping.\n");
    disabled = true;
  }
}

initCharMap = function() { return new CVietUniCodeMap(); }

initKeys = function() {
  switch (keymodeid) {
    case 4: return new CTelexKeys();
    case 2: return new CVniKeys();
    case 3: return new CViqrKeys();
    case 1: return new CAllKeys();
    default: return new CVKOff();
  }
}

function telexingVietUC (txtarea) {
  txtarea.vietarea= true;
  txtarea.onkeyup= null;
  if (!supported) return;
  txtarea.onkeypress= vietTyping;
  txtarea.getCurrentWord= getCurrentWord;
  txtarea.replaceWord= replaceWord;
  txtarea.onkeydown= onKeyDown;
  txtarea.onmousedown= onMouseDown;
  if(!theTyper) theTyper = new CVietString("");
}

function getEvt(evt) {
  if (typeof(evt)=='string') return evt.charCodeAt(0);
  return document.all? event.keyCode: (evt && evt.which)? evt.which: 0;
}

function onKeyDown(evt) {
  var c= getEvt(evt);
  if ((c==10) || (c==13)) { reset(1); linebreak= 1; }
  else if ((c<49) && (c!=16) && (c!=20)) { linebreak= 0; reset(c==32); }
  return true;
}

function onMouseDown(evt) { reset(0); linebreak= 0; return true; }

function vietTyping(evt) {
  var c= getEvt(evt);
  theTyper.value= this.getCurrentWord();
  var changed= ((c>32) && theTyper.typing(c));
  if (changed) this.replaceWord(theTyper.value);
  return !changed;
}

function getCurrentWord() {
  if(!document.all) return this.value;
  var caret = this.document.selection.createRange();
  if (caret.text) return null;
  var backward = -10;
  do {
    var caret2 = caret.duplicate();
    caret2.moveStart("character", backward++);
  } while (caret2.parentElement() != this && backward <0);
  this.curword = caret2.duplicate();
  return caret2.text;
}

function replaceWord(newword) {
  if(!document.all) { this.value= newword; return; }
  this.curword.text = newword;
  this.curword.collapse(false);
}
// end interface


// "class": CVietString
//
function CVietString(str) {
  this.value= str;
  this.keymode= initKeys();
  this.charmap= initCharMap();
  this.ctrlchar= '-';
  this.changed= 0;

  this.typing= typing;
  this.Compose= Compose;
  this.Correct= Correct;
  this.findCharToChange= findCharToChange;
  return this;
}

function typing(ctrl) {
  this.changed = 0;
  this.ctrlchar = String.fromCharCode(ctrl);
  if (linebreak) linebreak= 0; else this.keymode.getAction(this);
  this.Correct();
  return this.changed;
}

function Compose(type) {
  if(!this.value) return;
  var info = this.findCharToChange(type);
  if (!info || !info[0]) return;
  var telex;
  if (info[0]=='\\') telex= [1,this.ctrlchar,1];
  else if (type>6) telex= this.charmap.getAEOWD(info[0], type, info[3]);
  else telex= this.charmap.getDau(info[0], type);
  if (!(this.changed = telex[0])) return;
  this.value = this.value.replaceAt(info[1],telex[1],info[2]);
  if (!telex[2]) { spellerror= 1; this.value+= this.ctrlchar; }
}

function Correct() {
  if (this.charmap.maxchrlen || !document.all) return 0;
  var tmp= this.value;
  if ('nNcC'.indexOf(this.ctrlchar)>=0) tmp+= this.ctrlchar;
  var er= /[^\x01-\x7f](hn|hc|gn)$/i.exec(tmp);
  if (er) {
    this.value= tmp.substring(0,tmp.length-2)+er[1].charAt(1)+er[1].charAt(0);
    this.changed= 1;
  }
  else if(!this.changed) return 0;
  er= /\w([^\x01-\x7f])(\w*)([^\x01-\x7f])\S*$/.exec(this.value);
  if (!er) return 0;
  var i= this.charmap.isVowel(er[1]);
  var ri= (i-1)%24 + 1, ci= (i-ri)/24;
  var i2= this.charmap.isVowel(er[3]);
  if (!ci || !i2) return 0;
  var ri2= (i2-1)%24 + 1, ci2= (i2-ri2)/24;
  var nc= this.charmap.charAt(ri)+ er[2]+ this.charmap.charAt(ci*24+ri2);
  this.value= this.value.replace(new RegExp(er[1]+er[2]+er[3],'g'), nc);
}

function findCharToChange(type) {
  var lastchars= this.charmap.lastCharsOf(this.value, 5);
  var i= 0, c=lastchars[0][0], chr=0;
  if (c=='\\') return [c,this.value.length-1,1];
  if (type==15) while (!(chr=this.charmap.isVD(c))) {
    if ((c < 'A') || (i>=4) || !(c=lastchars[++i][0])) return null;
  }
  else while( "cghmnptCGHMNPT".indexOf(c)>=0) {
    if ((c < 'A') || (i>=2) || !(c=lastchars[++i][0])) return null;
  }
  c = lastchars[0][0].toLowerCase();
  var pc = lastchars[1][0].toLowerCase();
  var ppc = lastchars[2][0].toLowerCase();
  if (i==0 && type!=15) {
    if ( (chr=this.charmap.isVowel(lastchars[1][0]))
      && ("uyoia".indexOf(c)>=0) && !this.charmap.isUO(pc,c)
      && !((pc=='o' && c=='a') || (pc=='u' && c=='y'))
      && !((ppc=='q' && pc=='u') || (ppc=='g' && pc=='i')) ) ++i;
    if (c=='a' && (type==9 || type==7)) i= 0;
  }
  c= lastchars[i][0];
  if ((i==0 || chr==0) && type!=15) chr= this.charmap.isVowel(c);
  if (!chr) return null;
  var clen= lastchars[i][1], isuo=0;
  if ((i>0) && (type==7 || type==8 || type==11)) {
    isuo=this.charmap.isUO(lastchars[i+1][0],c);
    if (isuo) { chr=isuo; clen+=lastchars[++i][1]; isuo=1; }
  }
  var pos= this.value.length;
  for (var j=0; j<= i; j++) pos -= lastchars[j][1];
  return [chr, pos, clen, isuo];
}
// end CVietString


// character-map template
//
function CVietCharMap(){
  this.vietchars = null;
  this.length = 149;
  this.chr_cache = new Array(20);
  this.ind_cache = new Array(20);
  this.cptr = 0;
  this.caching= function(chr, ind) {
    this.chr_cache[this.cptr] = chr;
    this.ind_cache[this.cptr++] = ind;
    this.cptr %= 20;
  }
  return this;
}

CVietCharMap.prototype.charAt= function(ind){
  var chrcode = this.vietchars[ind];
  return chrcode ? String.fromCharCode(chrcode) : null;
}

CVietCharMap.prototype.isVowel= function(chr){
  var i = 0;
  while ((i<20) && (chr != this.chr_cache[i])) ++i;
  if (i<20) return this.ind_cache[i];

  i = this.length-5;
  while ((chr != this.charAt(i)) && i) --i;
  this.caching(chr, i);
  return i;
}

CVietCharMap.prototype.isVD= function (chr){
  var ind = this.length-5;
  while ((chr != this.charAt(ind)) && (ind < this.length)) ++ind;
  return (ind<this.length)? ind: 0;
}

CVietCharMap.prototype.isUO= function (c1, c2){
  if (!c1 || !c2) return 0;
  var ind1 = this.isVowel(c1);
  var ci = (ind1-1)%12;
  if ((ci!=9) && (ci!=10)) return 0;
  var ind2 = this.isVowel(c2);
  ci = (ind2-1)%12;
  if ((ci!=6) && (ci!=7) && (ci!=8)) return 0;
  return [ind1,ind2];
}

CVietCharMap.prototype.getDau= function (ind, type){
  var accented= (ind < 25)? 0: 1;
  var ind_i= (ind-1) % 24 +1;
  var charset= (type == 6)? 0 : type;
  if ((type== 6) && !accented) return [0];
  var newind= charset*24 + ind_i;
  if (newind == ind) newind= ind_i;
  var c
