/*
  GNU通用公共許可證(GPL)
  這是一個自由軟件，任何人或機構都可以在遵守GPL守則下使用該軟件。
  原 作 者 ：亦會編程（萬碼論壇註冊 http://www.suoai.com/88988/）
  修 改 者 ：阿勤（倉頡之友。馬來西亞 http://www.chinesecj.com）せFXAM
  版  本　 ：0.61
  瀏覽器   ：支持IEせNetscape 及 Mozilla 最新版本
  推出日期 ：2003-7-30
  修改日期 ：2003-8-23
*/

// Gecko = Netscape 7.x & Mozilla 1.4+

LastNo=0;
SPACECHAR=" "; // 空白字元。
CandChinesePart=new Array(); //侯選字，中文字部份
CandCompPart=new Array();    //侯選字, 後碼部份，如『我o』裡的o
AsciiStr="a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z"; //定義使用的編碼。
AsciiStr=AsciiStr.split(',');
CodeList=CodeList.split(',');

CtrlDown=false;
CancelKey=false;

//==KeyCode 33..47
//Symbol1 = "！ぺ＃＄％＆□（）も＋，－。／";
//==KeyCode 58..64
//Symbol2 = "：；＜＝＞？＠";
//==KeyCode 91..96
//Symbol3 = "む、｝ふ＿□";
//==KeyCode 123..126
//Symbol4 = "｛｜め∼";

Punct2 =  new Array('；','＝','，','－','。','／','‘');
Punct3 =  new Array('〔','、','〕','’');

//==按ＳＨＩＦＴ輸出的符號
SPunct1 = new Array('）','！','＠','＃','＄','％','︿','＆','＊','（');
SPunct2 = new Array('：','＋','＜','＿','＞','？','∼');
SPunct3 = new Array('｛','｜','｝','”');

//==全形數字和字母
FullShape_No=new Array("０","１","２","３","４","５","６","７","８","９");
FullShape_BigAZ=new Array("Ａ","Ｂ","Ｃ","Ｄ","Ｅ","Ｆ","Ｇ","Ｈ","Ｉ","Ｊ","Ｋ","Ｌ","Ｍ","Ｎ","Ｏ","Ｐ","Ｑ","Ｒ","Ｓ","Ｔ", "Ｕ","Ｖ","Ｗ","Ｘ","Ｙ","Ｚ");
FullShape_SmallAZ=new Array('ａ','ｂ','ｃ','ｄ','ｅ','ｆ','ｇ','ｈ','ｉ','ｊ','ｋ','ｌ','ｍ','ｎ','ｏ','ｐ','ｑ','ｒ','ｓ','ｔ','ｕ','ｖ','ｗ','ｘ','ｙ','ｚ');

// 二分搜索法
function FindIn(s) {
var find=-1,low=0,mid=0,high=CodeList.length;
var sEng="";
  while(low<high){
    mid=(low+high)/2;
    mid=Math.floor(mid);
    sEng=CodeList[mid];
    if(sEng.indexOf(s,0)==0){find=mid;break;}
    sEng=CodeList[mid-1];
    if(sEng.indexOf(s,0)==0){find=mid;break;}
    if(sEng<s){low=mid+1;}else{high=mid-1;}
  }
  while(find>0){
    sEng=CodeList[find-1];
    if(sEng.indexOf(s,0)==0){find--;}else{break;}
  }
  return(find);
}

function GetStr(No, s){
  var sTmp="",sChi="";
  for(i=0;i<=9;i++){
    if(No+i>=CodeList.length-1){break;}
    sTmp=CodeList[No+i];
    if(sTmp.indexOf(s)>=0){
      sChi=CodeList[No+i];
      CandCompPart[i]=sChi.substring(s.length,sChi.indexOf(SPACECHAR));
      CandChinesePart[i]=sChi.substr(sChi.lastIndexOf(SPACECHAR)+1);
      if(i<=8){IME.Cand.value+=(i+1)+"."+CandChinesePart[i]+CandCompPart[i]+'\n';}
      else{IME.Cand.value+=(0)+"."+CandChinesePart[i]+CandCompPart[i]+'\n';}
      LastNo=No+i;
    }else{LastNo=-1;}
  }
}

function Grep(s){
  var No=-1;
  for(i=0;i<=9;i++){CandChinesePart[i]="";}
  if(s!=""){
    No=FindIn(s);
    if(No>=0){GetStr(No, s);}
  }

  //==自動上字
  if( IME.AutoUp.checked==true && CandChinesePart[0]!="" && CandChinesePart[1]=="" && CandCompPart[0]=="" ) {
    SendCand(0);
  }
}

// 送候選字到輸入區
function SendCand(n){
  if ( n>=0 && n<=9 ) {
    SendStr(CandChinesePart[n]);
    IME.Comp.value="";
    IME.Cand.value="";
  }
}

// 設選區：暫時無用
function setSelectionRange(input, selectionStart, selectionEnd) {
  if (input.setSelectionRange) {
    input.focus();
    input.setSelectionRange(selectionStart, selectionEnd);
  } else if (input.createTextRange) {
    var range = input.createTextRange();
    range.collapse(true);
    range.moveEnd('character', selectionEnd);
    range.moveStart('character', selectionStart);
    range.select();
  }
}
// 設光標位置：暫時無用
function setCaretToEnd (input) {
  setSelectionRange(input, input.value.length, input.value.length);
}
function setCaretToBegin (input) {
  setSelectionRange(input, 0, 0);
}
function setCaretToPos (input, pos) {
  setSelectionRange(input, pos, pos);
}

// 送字串到輸入區
function SendStr(s) {
  if (s=="") { return }

  switch (browser) {
  case 1: // IE
    var r=document.selection.createRange();
    r.text=s;
    r.select(); // 不知何故，此碼可取消選區。
    break;
  case 2: // Gecko
    /*
       -simulate keypress
       *simulate scroll
       -change outputString
       -use createRange, setStart, setEnd
    */

    var obj = IME.InputArea;
    var selectionStart = obj.selectionStart;
    var selectionEnd = obj.selectionEnd;
    var oldScrollTop = obj.scrollTop;  //因Gecko不會滾到該滾的地方。
    var oldScrollHeight = obj.scrollHeight;
    var oldLen = obj.value.length;

    obj.value = obj.value.substring(0, selectionStart) + s + obj.value.substring(selectionEnd); // 這時Gecko會將scrollTop改成0
    obj.selectionStart = obj.selectionEnd = selectionStart + s.length;
    if (obj.value.length == oldLen) { // 如果用戶在後面輸入，就直接滾到後面。
      obj.scrollTop = obj.scrollHeight;
    } else if (obj.scrollHeight > oldScrollHeight) { // 如果TextArea增加了高度，就滾下一點點
      obj.scrollTop = oldScrollTop + obj.scrollHeight - oldScrollHeight;
    } else { // 其它情形就滾回之前的地方
      obj.scrollTop = oldScrollTop;
    }
    
    break;
  default: // 其它，就在後面加字算了
    IME.InputArea.value += s;
  }
}

// 將半形字母換成全形字母
function ToFullShapeLetter(aStr) {
  var s="";

  for (i=0;i<aStr.length;i++) {
    var c = aStr.charCodeAt(i);
    if (c>=65 && c<=90) {
      s += FullShape_BigAZ[c-65];
    } else if (c>=97 && c<=122) {
      s += FullShape_SmallAZ[c-97];
    } else {
      s += aStr.charAt(i);
    }
  }

  return s;
}

function ImeKeyDown(e) {
  var s="";
  var key = e.which ? e.which : e.keyCode;

  CtrlDown=false;

  switch (key) {
  //==Backspace
  case 8:
    if (IME.Comp.value!="") {
      s=IME.Comp.value;
      IME.Comp.value = s.substr(0, s.length-1);
      IME.Cand.value = "";
      Grep(IME.Comp.value);
      //TODO: how to cancel KeyDown in Opera 7.11? Hack it?
      CancelKey = true;
      return (false);
    }
    return (true);

  //==Tab
  case 9:
    SendStr('　');
    CancelKey = true;
    return (false); 

  //==Esc
  case 27:
    IME.Comp.value="";
    IME.Cand.value="";
    CancelKey = true;
    return (false); //Esc會把全部文字刪除，故禁止 Esc鍵 起任何作用。

  //==PageUp
  case 33:
  case 57383: // Opera 7.11
    s=IME.Comp.value;
    if (s!="") {
      IME.Cand.value="";
      if((LastNo-19)>FindIn(s)){
        LastNo=LastNo-19;GetStr(LastNo, s);
      } else {
        GetStr(FindIn(s), s);
      }
      CancelKey = true;
      return(false);
    }
    return(true);

  //==PageDown
  case 34:
  case 57384: // Opera 7.11
    s=IME.Comp.value;
    if ( s!="" && LastNo!=-1 ) {
      IME.Cand.value="";
      GetStr(LastNo+1, s);
      CancelKey = true;
      return(false);
    }
    return(true);

  //==Space
  case 32:
    if(IME.Comp.value!="") {
      //TODO: sound if nothing in Cand
      SendCand(0);
      CancelKey = true;
      return(false);
    }
    return(true);

  //==Enter
  case 13:
    if (IME.Comp.value!="") {
      SendStr( IME.FullShape.checked ? 
        ToFullShapeLetter(IME.Comp.value) : 
        IME.Comp.value);
      IME.Comp.value="";
      IME.Cand.value="";
      CancelKey = true;
      return(false);
    }
    return(true);

  //==F12
  case 123:
  case 57356: // Opera 7.11
    IME.FullShape.checked = !IME.FullShape.checked;
    CancelKey = true;
    return (false);

  //==Ctrl
  case 17:
  case 57402: // Opera 7.11
    CtrlDown=true;
    break;
  }

  if (e.ctrlKey) { return (true) };

  //==數字和符號
  if (key>=48 && key<=57) {
    if (e.shiftKey) { // 0-9????
      if (IME.FullShape.checked || !IME.EnglishMode.checked) {
        SendStr(SPunct1[key-48]);
        CancelKey = true;
        return (false);
      }
    } else { // ??
      if (IME.Comp.value=="") {
        if (IME.FullShape.checked || !IME.EnglishMode.checked) {
          SendStr(FullShape_No[key-48]);
          CancelKey = true;
          return (false);
        }
      } else {
        if (!IME.EnglishMode.checked) {
          SendCand( key==48 ? 9 : (key-49) );
          CancelKey = true;
          return (false);
        }
      }
    }
    return (true);
  }
  //==其它符號
  if (IME.FullShape.checked || !IME.EnglishMode.checked) {
    if (key>=186 && key<=192) {
      SendStr( e.shiftKey ? SPunct2[key-186] : Punct2[key-186] );
      CancelKey = true;
      return (false);
    }
    if (key>=219 && key<=222) {
      SendStr( e.shiftKey ? SPunct3[key-219] : Punct3[key-219] );
      CancelKey = true;
      return (false);
    }
  }    

  if (browser==2) {
    if (IME.FullShape.checked || !IME.EnglishMode.checked) {
      switch (key) {
      case 59: // "Gecko 之分號和冒號" 的 keyCode 是 59
        SendStr( e.shiftKey ? SPunct2[0] : Punct2[0] );
        CancelKey = true;
        return (false);
      case 61:
        SendStr( e.shiftKey ? SPunct2[1] : Punct2[1] );
        CancelKey = true;
        return (false);
      case 109:
        SendStr( e.shiftKey ? SPunct2[3] : Punct2[3] );
        CancelKey = true;
        return (false);
      }
    }
  }

  return(true);
}

function ImeKeyPress(e) {
  var key = e.which ? e.which : e.keyCode;

  // Gecko 雖不能於 OnKeyDown 取消鍵，但它卻是在 OnKeyPress 之後纔執行鍵的動作，故於 OnKeyPress 取消鍵亦無所謂。
  // 但 Opera 在 OnKeyPress 之前已執行鍵的動作，故仍未能取消 Backspace 等鍵。  
  // 為什麼不連IE也在此取消多一次？即在OnKeyDown取消，在OnKeyPress再取消。因會出現一個問題：快速輸入文字時，第一個字會被取消。原因未知。
  if (browser==2 || browser==3) {
    if (CancelKey) {
      CancelKey = false;
      return (false);
    }
  }

  if (e.ctrlKey) { return (true); }

  // 為何不將A-Z的處理放在OnKeyDown ? 因無從知道Caps Lock的狀況。
  //==A-Z
  if ( key>=65 && key<=90 ) {
    if (IME.FullShape.checked) {
      SendStr(FullShape_BigAZ[key-65]);
      return (false);
    }
    return (true);
  }

  //==a-z
  if (key>=97 && key<=122) {
    if (IME.EnglishMode.checked) {
      if (IME.FullShape.checked) {
        SendStr( FullShape_SmallAZ[key-97] );
        return (false);
      }
      return (true);
    } else {
      s=IME.Comp.value;
      if (s.length<MAX) {
        IME.Comp.value+=AsciiStr[key-97];
        IME.Cand.value="";
        Grep(IME.Comp.value);
      }
      return (false);
    }
  }

  // Gecko 的某些鍵會產生兩個KeyCode，如?和/，須取消其中一個，否則將一鍵兩字
  if (browser==2) {
    switch (key) {
    case 47: case 63:
      if (!IME.EnglishMode.checked || IME.FullShape.checked) {
        return (false);
      }
      break;
    }
  }

  return (true);
}

function ImeKeyUp(e) {
  var key = e.which ? e.which : e.keyCode;

  //==Ctrl
  if (key==17 || key==57402) {
    if (CtrlDown==true) {
      IME.EnglishMode.checked = !IME.EnglishMode.checked;
    }
  }

  return(true);
}

function BodyOnLoad() {
  browser = 
    (navigator.appName.indexOf('Microsoft') != -1) ? 1 :
    (navigator.appName.indexOf('Netscape')  != -1) ? 2 :
    (navigator.appName.indexOf('Opera')     != -1) ? 3 :
    4;

  // Gecko 的JavaScript須以DOM方式表示物件，getElementById是『方便法』。IE亦通曉此法。Opera似乎亦曉得。
  IME = {
    InputArea: document.getElementById("InputArea"),
    Comp:      document.getElementById("Comp"),
    Cand:      document.getElementById("Cand"),

    EnglishMode: document.getElementById("EnglishMode"),
    FullShape:   document.getElementById("FullShape"),
    AutoUp:      document.getElementById("AutoUp")
  }

  IME.InputArea.focus();
}

//BETA
function LoadImeTable() {
  //CodeList.clear;
  //document.write("<SCRIPT LANGUAGE='JavaScript1.2' SRC='" + news.js + ' TYPE='text/javascript'><\/SCRIPT>");
}


