javascript自动填充插件【续2-完结】

这次给出自动填充插件的全部源代码和测试地址。

测试地址:http://jsbin.com/alaya4/2 能打开这个链接的就不用看下面的代码了。因为这个网站不支持中文,也不支持中文的@,所以更版本号是2

效果图

html和css

<!DOCTYPE html>
<html>
<head>
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<meta charset=utf-8 />
<title>ac test</title>
<style type="text/css">
    body{
        font-size: 12px;
        line-height: 20px;
        font-family: Helvetica,Arial,sans-serif;
        color:#555;
    }
    #wrapper{
        display: inline;
        position: relative;
        border: 1px solid #DDDDDD;
        float: left;
        overflow: hidden;
        width: 516px;
    }
    #inputarea,#shadow{
        font-size: 12px;
        line-height: 20px;
        font-family: Helvetica,Arial,sans-serif;
        width: 512px;
        height: 100px;
        border: 0px none;
        margin: 0pt;
        padding: 3px;
    }
    #shadow{
        position: absolute;
        top: 0pt;
        left: 0pt;
        z-index: -999;
        overflow-y: scroll;
    }
    #selecter{
        background-color: white;
        border: 1px solid black;
        color: #333333;
        display: none;
        font: 12px/1.75 Tahoma,Arial;
        padding: 2px;
        position: absolute;
        z-index: 999;
    }
    #selecter ul{
        margin:0;
        padding:0;
    }
    #selecter ul li{
        cursor: pointer;
        list-style: none outside none;
        margin: 0;
        padding: 0 15px 0 5px;
        white-space: nowrap;
    }
    #selecter ul li.on{
        background-color: #EEEEEE;
    }
</style>
</head>
<body>
  <div id="wrapper">
      <div id="shadow"></div>
      <textarea id="inputarea" ></textarea>
  </div>
  <div id="selecter"></div>
</body>
</html>

javascript文件

/**
 * Author: kamal http://yukun.im/news/221
 * Date: 2011-3-4
 * Time: 14:25:37
 * auto complete
 */
var strTMP = '{"r":24,"i":{"12345":"kamal", "12389":"luffy", "12387":"zoro", "12384":"nami", "12383":"usopp", "12382":"sanji", "12381":"chopper", "12376":"robin","12371":"franky", "12369":"brook", "12367":"vivi", "12366":"Shanks", "12348":"Beckman", "12347":"Yasopp", "12346":"Roo", "12447":"Rockstar", "12448":"Buggy", "12449":"Smoker", "12547":"Ace"}}';

function AC(oTxtbox, /*input area jQuery object*/
    oSelecterLayer, /*div jQuery object*/
    oShadow /*div jQuery object*/
    ){
  this.nameLength = 16;
  this.url = "data.js";
  this.txtbox = oTxtbox;//textarea
  this.selecterLayer = oSelecterLayer;//selecter panel div
  this.shadow = oShadow;//shadow div
  this.selecterPos = {top:0,left:0};//selecter panel coordinates
  this.cursorX = 0; //cursor position in line
  this.defaultList = null;//object id:name
  this.curScrollTop = 0;//textarea scrolltop
  this.curName = "";//string after @
  this.selecterStr = "";//tmp var, selecter panel
  this.selected = null;//tmp var, selected name
  this.loading = false;//is ajax loading
  this.enable = true;

  this.init();
}

AC.prototype.init = function(){
  var oThis = this;
  this.txtbox[0].onkeyup = function(oEvent){
    oEvent = oEvent || window.event;
    oThis.handleKeyUp(oEvent);
  };
  this.txtbox[0].onkeydown = function(oEvent){
    oEvent = oEvent || window.event;
    if(oThis.selecterLayer.is(":visible") &&
        ( oEvent.keyCode == 38 ||   //up
        oEvent.keyCode == 40 ||   //down
        oEvent.keyCode == 13 ||   //enter
        oEvent.keyCode == 9)){     //tab
      oThis.stopDefault(oEvent); return;
    }
  };
};
AC.prototype.handleKeyUp = function(oEvent){
  if(!this.enable){
    return false;
  }
  var KEY = {
      UP: 38,
      DOWN: 40,
      DEL: 46,
      TAB: 9,
      RETURN: 13,
      ESC: 27,
      COMMA: 188,
      PAGEUP: 33,
      PAGEDOWN: 34,
      BACKSPACE: 8
    };
  var obj = this.selecterLayer;
  var li = obj.find("li");
  switch(oEvent.keyCode){
    case KEY.UP:
      if(obj.is(":visible")){
        this.updatePos(this.selected.prev()[0] || li[li.length - 1]);
        this.stopDefault(oEvent);
      }
      break;
    case KEY.DOWN:
      if(obj.is(":visible")){
        this.updatePos(this.selected.next()[0] || li[0]);
        this.stopDefault(oEvent);
      }
      break;
    case KEY.RETURN:
    case KEY.TAB:
      if(obj.is(":visible")){
        this.insert();
        this.stopDefault(oEvent);
      }
      break;
    default:
      this.search();
      break;
  }

};
AC.prototype.search = function(){
    if(this.txtbox.val().replace(/@/g, "@").indexOf('@') != -1){//if has @
      //fetch name

      this.fetchNameList();
      //get cursor position
      this.setCursorX();
      //get user input name
      var tmpName = this.txtbox.val().substr(0,this.cursorX).match(/@[^@\n\r]{0,30}$/g);
      this.curName = tmpName?tmpName[0].slice(1):"";
      if(this.curName !== ""){//alert('about to show suggest + currName='+this.curName);
        this.showSelecterLayer();
      }else{
        this.hide();
      }
    }else{
      this.hide();
    }
};

AC.prototype.showSelecterLayer = function(){
  this.fillShadow();
  this.setSelecterPosition();
  this.setSelecterStr();
  if(this.selecterStr !== ""){
    this.selecterLayer.css({top:this.selecterPos.top+"px",left:this.selecterPos.left+"px"})
    .html(this.selecterStr)
    .show();

    this.selected = this.selecterLayer.find("li:first").addClass("on");
    this.bindHover();
  }else{
    this.hide();
  }
};

AC.prototype.updatePos = function(elem){
  this.selected = $(elem);
  this.selecterLayer.find("li").removeClass("on");
  this.selected.addClass("on");
};
AC.prototype.insert = function(){
  var str = this.selected.html().replace(/\<\/?b\>|\(.*\)/ig,"");
  var txttmp = this.txtbox.val().substr(0,this.cursorX);
  var txt1 = txttmp.substr(0,txttmp.lastIndexOf('@')+1);
  var txt2 = this.txtbox.val().substr(this.cursorX);// alert('to insert -- '+txt1+'+' + str + " +" +txt2);
  this.txtbox.val(txt1 + str + " " +txt2 ).focus();
  this.setCursorX(txt1.length + str.length + 1);
  this.hide();
};
AC.prototype.setSelecterPosition = function(){
  //var absposition = ugAbsPos(this.shadow.find("span")[0]);
  var pos = this.shadow.find("b").position();
  this.selecterPos.top = pos.top + 25;
  this.selecterPos.left = pos.left;
  this.curScrollTop = this.txtbox[0].scrollTop;
  this.selecterPos.top = this.selecterPos.top - this.curScrollTop;
};
AC.prototype.fillShadow = function(){//alert('-'+this.txtbox.val()+'-');
  var tmpTxt = this.txtbox.val().substr(0,this.cursorX).replace(/@/g, "@").replace(/ /g,"&nbsp;").replace(/\r\n|\n/g, "<br />");
  var txt1 = tmpTxt.substr(0,tmpTxt.lastIndexOf('@')+1);
    this.shadow.html("<span>"+txt1 + "</span><b>" + this.curName + "</b>");
};
AC.prototype.setSelecterStr = function(){
  var tmpStr = "";
  var data = this.defaultList;
  var regStr = this.escapeReg(this.curName);
  var reg = eval("/^"+ regStr +"/i");
  if(this.defaultList){
    var suggest_count = 0;
    for(var p in data){// alert(p + "\n"+data[p]);
      if(suggest_count >= 10){break;}
      var tmpname = data[p];
      var matchStr = tmpname.match(reg);
      if(matchStr != null){
        suggest_count++;
        tmpStr += '<li>'+tmpname.replace(reg,'<b>'+matchStr+'</b>')+'('+p+')</li>';
      }
    }//end of for
    if(tmpStr != ""){
      this.selecterStr = "<ul>"+tmpStr+"</ul>";
    }else{
      this.selecterStr = "";
    }
  }
};

AC.prototype.hide = function(){
  this.selecterLayer.hide();
};
AC.prototype.bindHover = function(){
  var oThis = this;
  this.selecterLayer.find("li").mouseover(function(){
    $(this).parent().find("li").removeClass("on");
    oThis.selected = $(this).addClass("on");
  });
  this.selecterLayer[0].onclick = function(){
    oThis.insert();
  };
};

AC.prototype.fetchNameList = function(){
  var oThis = this;
  if(oThis.defaultList == null && !this.loading){
    oThis.loading = true;
    oThis.defaultList = eval('('+strTMP+')').i;

    // $.ajax({
      // url:this.url,
      // type:'get',
      // dataType:'json',
      // success:function(data){
        // if(data.r && data.r>0){
          // oThis.defaultList = data.i;
        // }
      // },
      // finish:function(){
        // oThis.loading = false;
      // },
      // error:function(data){
        // alert(data);
      // }
    // });
    oThis.selected = null;
  }
};
AC.prototype.setCursorX = function(pos){
  var obj = this.txtbox[0];
  if(document.selection){
    var rng = document.selection.createRange();
    if(pos === undefined){
      obj.focus();
      if(rng == null){
        this.cursorX = 0;
      }

      var re = obj.createTextRange(),
        rc = re.duplicate();
      re.moveToBookmark(rng.getBookmark());
      rc.setEndPoint('EndToStart',re);
      this.cursorX = rc.text.length;
    }else if(typeof pos === "number"){
      obj.focus();
      var index = this.cursorX;
      var range = obj.createTextRange();
      range.collapse(true);
      range.moveEnd('character', pos);
      range.moveStart('character', pos);
      range.select();
    }
  }else{
    if(pos === undefined){
      this.cursorX = obj.selectionStart;
    }else if(typeof pos === "number"){
      obj.scrollTop = this.curScrollTop;
      obj.focus();
      obj.selectionEnd = pos;
      obj.selectionStart = pos;
    }
  }
};
AC.prototype.escapeReg = function(a){
  for(var b = [], c = 0; c < a.length; c ++ )
    {
       var e = a.charAt(c);
       switch(e)
       {
          case "." :
             b.push("\\x2E");
             break;
          case "$" :
             b.push("\\x24");
             break;
          case "^" :
             b.push("\\x5E");
             break;
          case "{" :
             b.push("\\x7B");
             break;
          case "[" :
             b.push("\\x5B");
             break;
          case "(" :
             b.push("\\x28");
             break;
          case "|" :
             b.push("\\x28");
             break;
          case ")" :
             b.push("\\x29");
             break;
          case "*" :
             b.push("\\x2A");
             break;
          case "+" :
             b.push("\\x2B");
             break;
          case "?" :
             b.push("\\x3F");
             break;
          case "\\" :
             b.push("\\x5C");
             break;
          case "/" :
              b.push("\\x2F");
              break;
          default :
           b.push(e);
          }
       }
    return b.join("");
};

AC.prototype.stopDefault = function(e){
     if ( e && e.preventDefault )
        e.preventDefault();
    else
        window.event.returnValue = false;

    return false;
};
//-------------------
new AC($('#inputarea'), /*input area jQuery object*/
  $('#selecter'), /*div jQuery object*/
  $('#shadow') /*div jQuery object*/);