/*vietuni5.js
* by Tran Anh Tuan [tuan@physik.hu-berlin.de] - R. 26.04.01
* Copyright (c) 2001, 2002 AVYS e.V.. All Rights Reserved.
*
* Originally published and documented at http://www.avys.de/
* This code is free for you to use on non-commercial web sites.
* If you want to, keep this copyright notice intact to make friends with AVYS :-)
*/


// interface for HTML
//

function VNOff() { return 0; }

var disabled = false;
var charmapID = 0;
var typingMode = VNOff;
var theTyper = null;

// for HTML-call:
//------------------------

telexingVietUC = initTyper;

function setCharMap(mapID) { 
   charmapID = mapID; 
   if(theTyper) theTyper.charmap = initCharMap();
}

function setTypingMode(mode) {
  switch (mode) {
     case 1: typingMode = telexTyping; break;
     case 2: typingMode = vniTyping; break;
     case 3: typingMode = viqrTyping; break;
     default: typingMode = VNOff;
  }
  if (theTyper) theTyper.typing = typingMode;
  if (disabled) return true;
  if (!document.all && !document.getElementById) {
     errmsg = "Xin loi, trinh duyet web cua ban khong cho phep dung VietTyping.\n"
     errmsg += "Mac du vay ban van co the go~ chu Viet nhu binh thuong\n"
     errmsg += "roi bam vao nut \"chuyen ddoi\" khi dda~ xong.";
     alert(errmsg);
     disabled = true;  
  }
}

function convertAtOnce(txtarea) {
  if(!txtarea) return;
  if(!theTyper) theTyper = new vietString("");
  txtarea.value = theTyper.doConvertIt(txtarea.value);
}
// end from HTML


function initCharMap() { 
  switch (charmapID) {
     case 0: return new VietUniCodeMap();
     case 1: return new VietVniMap();
     case 2: return new VietTCVNMap();
     case 3: return new VietVISCIIMap();
     case 4: return new VietVPSMap();
     default: return new VietUniCodeMap();
  }
}

function initTyper(txtarea,evt) {
  if (document.all) telexingVietUC = ieTyping; 
  else if (document.getElementById) telexingVietUC = ns6Typing;
  else telexingVietUC = VNOff;
}

// event & typing:
//------------------------

function ieTyping(txtarea,evt) {
  if(!txtarea) return; 
  var c = event.keyCode;
  var curword= getCurrentWord(txtarea);
  if(!theTyper) theTyper = new vietString(curword);
  else theTyper.setValue(curword);
  if (c < 49) {
     if (theTyper.onEndWord(curword)) replaceWord(txtarea, theTyper.value);
     return;
  }
  if (theTyper.typing()) replaceWord(txtarea, theTyper.value);
}

function ns6Typing(txtarea,evt) {
  if(!txtarea) return; 
  var c = evt ? evt.keyCode: 50;
  if(!theTyper) theTyper = new vietString(txtarea.value);
  else theTyper.setValue(txtarea.value);
  if (c < 49) {
     if (theTyper.onEndWord(txtarea.value)) txtarea.value = theTyper.value; 
     return;
  }
  if (theTyper.typing()) txtarea.value = theTyper.value;
}


// for IE4+,5+: 
function getCurrentWord(txtarea) {
  var caret = document.selection.createRange();
  var backward = -6;
  do {
     var caret2 = caret.duplicate();
     caret2.moveStart("character", backward++);    
  } while (caret2.parentElement() != txtarea && backward <0);
  txtarea.curword = caret2.duplicate(); 
  return caret2.text;
}

function replaceWord(txtarea, newword) {
  txtarea.curword.text = newword;
  txtarea.curword.collapse(false);
}
// end IE-special

///////////////////////////////////////////
// end interface



// vietString Obj
// 
function vietString(str) {
  var i = str.length-1;
  while((i>=0) && (str.charCodeAt(i) <= 32)) --i;
  var tmp =  new String(str);
  if(i>=0) tmp = tmp.substring(0,i+1);

  this.value = new String(tmp);
  this.charmap = initCharMap();
  this.changed = 0;

  this.setValue = setValue;
  this.typing = typingMode;
  this.telexDau = telexDau;
  this.telexAEOWD = telexAEOWD;
  this.onEndWord = onEndWord;
  this.findCharToChange = findCharToChange;
  this.doConvertIt = doConvertIt;
  return this;
}

function setValue(str) {
  var i = str.length-1;
  while((i>=0) && (str.charCodeAt(i) <= 32)) --i;
  var tmp =  new String(str);
  if(i>=0) tmp = tmp.substring(0,i+1);
  this.value = tmp;
}

function  telexTyping() {
  var lastchar = this.value.charAt(this.value.length-1);
  lastchar = lastchar.toLowerCase();
  this.changed = 0;
  switch (lastchar)  {
    case 's': this.telexDau(1); break;  // s, S: sac
    case 'f': this.telexDau(2); break;  // f, F: huyen
    case 'j': this.telexDau(3); break;  // j, J: nang
    case 'r': this.telexDau(4); break;  // r, R: hoi
    case 'x': this.telexDau(5); break;  // x, X: nga
    case 'a': this.telexAEOWD(1); break;
    case 'e': this.telexAEOWD(2); break;
    case 'o': this.telexAEOWD(3); break;
    case 'w': this.telexAEOWD(4); break;
    case 'd': this.telexAEOWD(5); break;
    case 'z': this.telexDau(6); break;  // z, Z: xo'a da^'u
  }
  return this.changed;
}

function  viqrTyping() {
  if (this.value.length < 2) return 0;
  var lastchar = this.value.charAt(this.value.length-1);
  var prevc = this.value.charAt(this.value.length-2); 
  if ((prevc=='\\') && ((lastchar=='?') || (lastchar=='.'))) {
    this.value= this.value.substring(0, this.value.length-2)+ lastchar;
    return (this.changed = 1);
  }
  var ctrlchar = lastchar;
  var germanKB = (prevc == '´') || (prevc == '`') || (prevc == '^');   
  if (germanKB) {
    this.value = this.value.substring(0, this.value.length-1);
    ctrlchar = prevc;
  }
  this.changed = 0;
  switch (ctrlchar)  {
    case '/' : case "'": case "´": this.telexDau(1); break;
    case '`': this.telexDau(2); break;
    case '.': this.telexDau(3); break;
    case '?': this.telexDau(4); break;
    case '~': this.telexDau(5); break;
    case '^': this.telexAEOWD(1);
              if(!this.changed) this.telexAEOWD(2);
              if(!this.changed) this.telexAEOWD(3); break;
    case '(': case '+': case '*': this.telexAEOWD(4); break;
    case 'd': case 'D': this.telexAEOWD(5); break;
    case '-': this.telexDau(6); break;
  }
  if (germanKB) {
    this.value += lastchar;
    if( this.changed ) { this.typing(); return true; }
  }
  return this.changed;
}


function  vniTyping() {
  var lastchar = this.value.charAt(this.value.length-1);
  this.changed = 0;
  switch (lastchar)  {
    case '1': this.telexDau(1); break;  // 1 -> sa('c
    case '2': this.telexDau(2); break;  // 2 -> huye^`n
    case '5': this.telexDau(3); break;  // 5 -> nang
    case '3': this.telexDau(4); break;  // 3 -> hoi
    case '4': this.telexDau(5); break;  // 4 -> nga~
    case '7': 
    case '8': this.telexAEOWD(4); break;  // 7,8 -> a(, o+, u+
    case '9': this.telexAEOWD(5); break;  // 9 -> dd
    case '6': this.telexAEOWD(1);         // 6 -> a^,e^ or  o^
              if(!this.changed) this.telexAEOWD(2);
              if(!this.changed) this.telexAEOWD(3); break;

    case '0': this.telexDau(6); break;  // 0 -> xoa dau
  }
  return this.changed;
}

function doConvertIt(txt) {
  var i = 0;
  this.value = "";
  while (i < txt.length) {
    this.value += txt.charAt(i++);
    this.typing();
  }
  return this.value;
}

function onEndWord(str) {
  if (typingMode == viqrTyping) {
    this.setValue(str); 
    var c = this.value.charAt(this.value.length-1);
    if ((c == '´') || (c == '`') || (c == '^')) return this.typing();
  }
  if (!this.charmap.vni) return false;
  var i = str.length-1, changed=false;
  while((i>0) && (str.charCodeAt(i) < 65)) --i; //seek word end
  while((i>0) && (str.charCodeAt(i) > 32)) --i; //seek word start
  var tmp = str.substring(0,i);
  while (i < str.length) {  
    var c = str.charCodeAt(i++);
    var c1 = c%256;      
    var c2 = (c-c1)/256;
    tmp += String.fromCharCode(c1);
    if(c2) { tmp += String.fromCharCode(c2); changed=true;}
  }
  this.value = tmp;
  return changed;   
}


function telexAEOWD(type) {
  var index = this.value.length-1;
  if (index == 0) return;
  var lastchar = this.value.charCodeAt(index);
  var prevchar = this.value.charCodeAt(index-1);  
  var telex = this.charmap.getTelexAEOWD (prevchar, type);
  this.changed =  telex[0];
  if (!this.changed) return;
  this.value= this.value.substring(0,index-1)+ String.fromCharCode(telex[1]);
  if (!telex[2]) this.value += String.fromCharCode(lastchar);
}


function telexDau( type ) {
  if( this.value.length-1 <= 0) return;
  var tochangepos = this.findCharToChange(); 
  if (tochangepos < 0) return;
  var telex = this.charmap.getTelexDau(this.value.charCodeAt(tochangepos), type);
  this.changed =  telex[0];
  if (!this.changed) return;
  var tmp1 = this.value.substring(0,tochangepos);
  var tmp2 = this.value.substring(tochangepos+1, this.value.length);
  this.value = tmp1 + String.fromCharCode(telex[1]) + tmp2;
  if (telex[2]) this.value = this.value.substring(0, this.value.length-1);
}


function findCharToChange() {
  var lastindex = this.value.length-1;
  if (lastindex < 1) return -1;
  var index = lastindex-1;

  var i = this.value.charCodeAt(index); 
  while((index> lastindex-4) && (index>=0) && (i> 64)
     && (this.charmap.canHaveAccent(i)< 0)) {i= this.value.charCodeAt(--index);}
  if (index <= 0) return index;
  if (i < 65) return -1;
  if (index <= lastindex-4) return -1;

  if (index == lastindex-3) { 
    var tmp = this.value.substring(index+1,lastindex);
    tmp = tmp.toLowerCase();
    if ((tmp != "ng") && (tmp != "ch") && (tmp != "nh")) index = -1;
    return index;  
  }
  if (index == lastindex-2) { 
    var regexp = "bdfghjklqrs";
    var c = this.value.charAt(index+1);  
    c = c.toLowerCase();
    if( (c >= 'v') || (regexp.indexOf(c) >= 0)) index = -1;
    return index;
  }
  var c = this.value.charAt(index);
  var prevc = this.value.charAt(index-1);  
  var regexp = "uUyYoOiIaA";

  if ( (this.charmap.canHaveAccent(prevc.charCodeAt(0)) >= 0) && 
         (regexp.indexOf(c) >= 0) ) {
     --index; 
     if ( ((prevc=='o') || (prevc=='O')) && ((c=='a') || (c=='A')) ) ++index;
     if ( ((prevc=='u') || (prevc=='U')) && ((c=='y') || (c=='Y')) ) ++index;
     c = prevc;  prevc = this.value.charAt(index-1);
     if ( ((prevc=='q') || (prevc=='Q')) && ((c=='u') || (c=='U')) ) ++index;
     if ( ((prevc=='g') || (prevc=='G')) && ((c=='i') || (c=='I')) ) ++index;
  }
  return index;
}
// end vietString Obj


///////////////////////////////

// code usually is in the first part -> searching left to right
// not very elegant, but faster
Array.prototype.indexOf = function indexOf(code) {
  var ind = 0;
  while ((code != this[ind]) && (ind < this.length)) ++ind;
  if (ind == this.length) ind = -1;
  return ind;
}


// character map Obj
function VietCharMap() { return this; }

// Properties:
VietCharMap.prototype.vietchars = new Array(24);
VietCharMap.prototype.vietsac = new Array(24);
VietCharMap.prototype.viethuyen = new Array(24);
VietCharMap.prototype.vietnang = new Array(24);
VietCharMap.prototype.viethoi = new Array(24);
VietCharMap.prototype.vietnga = new Array(24);
VietCharMap.prototype.vietd = new Array(4);
// methods:
VietCharMap.prototype.canHaveAccent = canHaveAccent;
VietCharMap.prototype.getTelexAEOWD = getTelexAEOWD;
VietCharMap.prototype.getTelexDau = getTelexDau;


function canHaveAccent(code) {
  var ind = this.vietchars.indexOf(code);
  if (ind < 0) ind = this.vietsac.indexOf(code);
  if (ind < 0) ind = this.viethuyen.indexOf(code); 
  if (ind < 0) ind = this.vietnang.indexOf(code);
  if (ind < 0) ind = this.viethoi.indexOf(code);
  if (ind < 0) ind = this.vietnga.indexOf(code);
  return ind;
}

function getTelexDau(code, type) {
  var result = new Array(0,0,0);
  var ind = this.vietchars.indexOf(code);
  var accented = (ind >=0)? 0: 1;
  if (accented) ind = this.canHaveAccent(code);
  if (ind < 0) return result;
  var charset = this.vietchars;
  switch (type) {
    case 1: charset = this.vietsac; break;
    case 2: charset = this.viethuyen; break;
    case 3: charset = this.vietnang; break;
    case 4: charset = this.viethoi; break;
    case 5: charset = this.vietnga; break;
    case 6: charset = this.vietchars; break;
  }
  if( !charset[ind] ) return result;
  if(accented & (charset.indexOf(code)>=0)) charset = this.vietchars;
  else result[2] = 1;
  result[1] = charset[ind]; 
  result[0] = 1;
  return result;
}

function getTelexAEOWD(code, type) {
  var result = new Array(0,0,0); 
  var newind = -1;
  var ind = this.vietchars.indexOf(code);
  switch (type) {
    case 1: 
      if ((ind==0) || (ind==12)) newind=ind+1;
      else if ((ind==1) || (ind==13)) newind= ind-1;
      break;
    case 2:
      if ((ind==3) || (ind==15)) newind=ind+1;
      else if ((ind==4) || (ind==16)) newind=ind-1;
      break;
    case 3: 
      if ((ind==6) || (ind==18)) newind=ind+1;
      else if ((ind==7) || (ind==19)) newind=ind-1;
      break;
    case 4:
      if ((ind==0) || (ind==12) || (ind==6) || (ind==18)) newind=ind+2;
      else if ((ind==2) || (ind==14) || (ind==8) || (ind==20)) newind=ind-2;
      else if ((ind==9) || (ind==21)) newind=ind+1;
      else if ((ind==10) || (ind==22)) newind=ind-1;
      break;
    case 5: 
      ind = this.vietd.indexOf(code);
      if ((ind==0) || (ind==2)) newind=ind+1;
      else if ((ind==1) || (ind==3)) newind=ind-1;
      break;
  }
  if(newind >= 0) {
    result[0] = 1; 
    if(type == 5) result[1] = this.vietd[newind]; 
    else result[1] = this.vietchars[newind]; 
    if(newind > ind) result[2] = 1;
  }
  return result;
}
// end VietCharMap prototype

///////////////////////////////


// Viet-Unicode map
function VietUniCodeMap() {
  var map = new VietCharMap();

  // vietchas: [a, a^, a(, e, e^, i, o, o^, o+, u, u+, y, A, A^,...U+,Y]
  map.vietchars = new Array(  
     97, 226, 259, 101, 234, 105, 111, 244, 417, 117, 432, 121, 
     65, 194, 258, 69, 202, 73, 79, 212, 416, 85, 431, 89);
  map.vietsac = new Array(
     225, 7845, 7855, 233, 7871, 237, 243, 7889, 7899, 250, 7913, 253, 
     193, 7844, 7854, 201, 7870, 205, 211, 7888, 7898, 218, 7912, 221);
  map.viethuyen = new Array(
     224, 7847, 7857, 232, 7873, 236, 242, 7891, 7901, 249, 7915, 7923,
     192, 7846, 7856, 200, 7872, 204, 210, 7890, 7900, 217, 7914, 7922);
  map.vietnang = new Array(
     7841, 7853, 7863, 7865, 7879, 7883, 7885, 7897, 7907, 7909, 7921, 7925,
     7840, 7852, 7862, 7864, 7878, 7882, 7884, 7896, 7906, 7908, 7920, 7924);
  map.viethoi = new Array(
     7843, 7849, 7859, 7867, 7875, 7881, 7887, 7893, 7903, 7911, 7917, 7927,
     7842, 7848, 7858, 7866, 7874, 7880, 7886, 7892, 7902, 7910, 7916, 7926);
  map.vietnga = new Array(
     227, 7851, 7861, 7869, 7877, 297, 245, 7895, 7905, 361, 7919, 7929,
     195, 7850, 7860, 7868, 7876, 296, 213, 7894, 7904, 360, 7918, 7928);
  map.vietd = new Array(100, 273, 68, 272);

  return map;
}


//Viet-VNI-Win
function VietVniMap() {

var map = new VietCharMap();

map.vietchars = new Array
(97, 57953, 60001, 101, 57957, 105, 111, 57967, 244, 117, 246, 121,
 65, 49729, 51777, 69, 49733, 73, 79, 49743, 212, 85, 214, 89);

map.vietsac = new Array
(63841, 57697, 59745, 63845, 57701, 237, 63855, 57711, 63988, 63861, 63990, 63865,
 55617, 49473, 51521, 55621, 49477, 205, 55631, 49487, 55764, 55637, 55766, 55641);

map.viethuyen = new Array
(63585, 57441, 59489, 63589, 57445, 236, 63599, 57455, 63732, 63605, 63734, 63609,
 55361, 49217, 51265, 55365, 49221, 204, 55375, 49231, 55508, 55381, 55510, 55385);

map.vietnang = new Array
(61281, 58465, 60257, 61285, 58469, 242, 61295, 58479, 61428, 61301, 61430, 238,
 53057, 50241, 52033, 53061, 50245, 210, 53071, 50255, 53204, 53077, 53206, 206);

map.viethoi = new Array
(64353, 58721, 64097, 64357, 58725, 230, 64367, 58735, 64500, 64373, 64502, 64377,
 56129, 50497, 55873, 56133, 50501, 198, 56143, 50511, 56276, 56149, 56278, 56153);

map.vietnga = new Array
(62817, 58209, 64609, 62821, 58213, 243, 62831, 58223, 62964, 62837, 62966, 62841,
 54593, 49985, 56385, 54597, 49989, 211, 54607, 49999, 54740, 54613, 54742, 54617);

map.vietd = new Array(100, 241, 68, 209);
map.vni = 1;

return map;
}

// TCVN3-ABC
function VietTCVNMap() {
var map = new VietCharMap();
map.vietchars = new Array
(97, 169, 168, 101, 170, 105, 111, 171, 172, 117, 173, 121, 
65, 162, 161, 69, 163, 73, 79, 164, 165, 85, 166, 89);

map.vietsac = new Array
(184, 202, 190, 208, 213, 221, 227, 232, 237, 243, 248, 253,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

map.viethuyen = new Array
(181, 199, 187, 204, 210, 215, 223, 229, 234, 239, 245, 250,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

map.vietnang = new Array
(185, 203, 198, 209, 214, 222, 228, 233, 238, 244, 249, 254,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

map.viethoi = new Array
(182, 200, 188, 206, 211, 216, 225, 230, 235, 241, 246, 251,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

map.vietnga = new Array
(183, 201, 189, 207, 212, 220, 226, 231, 236, 242, 247, 252,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

map.vietd = new Array(100, 174, 68, 167);
return map;
}


function VietVISCIIMap() {
var map = new VietCharMap();

map.vietchars = new Array
(97, 226, 229, 101, 234, 105, 111, 244, 189, 117, 223, 121,
65, 194, 197, 69, 202, 73, 79, 212, 180, 85, 191, 89);

map.vietsac = new Array
(225, 164, 237, 233, 170, 237, 243, 175, 190, 250, 209, 253,
193, 8222, 129, 201, 352, 205, 211, 143, 8226, 218, 186, 221);

map.viethuyen = new Array
(224, 165, 162, 232, 171, 236, 242, 176, 182, 249, 215, 207,
192, 8230, 8218, 200, 8249, 204, 210, 144, 8211, 217, 187, 376);

map.vietnang = new Array
(213, 167, 163, 169, 174, 184, 247, 181, 254, 248, 241, 220,
8364, 8225, 402, 8240, 381, 732, 353, 8220, 8221, 382, 185, 220);

map.viethoi = new Array
(228, 166, 198, 235, 172, 239, 246, 177, 183, 252, 216, 214,
196, 8224, 198, 203, 338, 8250, 8482, 8216, 8212, 339, 188, 214);

map.vietnga = new Array
(227, 231, 199, 168, 173, 238, 245, 178, 222, 251, 230, 219,
195, 231, 199, 710, 141, 206, 245, 8217, 179, 157, 255, 219);

map.vietd = new Array(100, 240, 68, 208);
return map;
}


function VietVPSMap() {
var map = new VietCharMap();

map.vietchars = new Array
  (97, 226, 230, 101, 234, 105, 111, 244, 214, 117, 220, 121,
  65, 194, 710, 69, 202, 73, 79, 212, 247, 85, 208, 89);
map.vietsac = new Array
  (225, 195, 161, 233, 8240, 237, 243, 211, 167, 250, 217, 353,
  193, 402, 141, 201, 144, 180, 185, 8211, 157, 218, 173, 221);
map.viethuyen = new Array
  (224, 192, 162, 232, 352, 236, 242, 210, 169, 249, 216, 255,
  0, 8222, 0, 215, 8220, 181, 188, 8212, 0, 168, 175, 178);
map.vietnang = new Array
  (229, 198, 165, 203, 338, 206, 8224, 182, 174, 248, 191, 339,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 339);
map.viethoi = new Array
  (228, 196, 163, 200, 8249, 204, 213, 176, 170, 251, 186, 8250,
  129, 8230, 163, 222, 8221, 183, 189, 732, 376, 209, 177, 8250);
map.vietnga = new Array
  (227, 197, 164, 235, 205, 239, 245, 8225, 171, 219, 187, 207,
  8218, 197, 164, 254, 8226, 184, 245, 8482, 166, 172, 0, 207);
map.vietd = new Array(100, 199, 68, 241);
return map;
}

// end vietuni.js
