Ajax学习--输入前提示

Ajax学习--输入前提示

 



// -------------------- mContentLoader.js
/**/ /*
 *        Ajax的辅助对象,执行所有与Ajax处理相关的工作
 *
 
*/

var  net  =   new  Object();

net.READY_STATE_UNINITIALIZED
=   0 ;
net.READY_STATE_LOADING      
=   1 ;
net.READY_STATE_LOADED       
=   2 ;
net.READY_STATE_INTERACTIVE  
=   3 ;
net.READY_STATE_COMPLETE     
=   4 ;

/**/ /*
 *     构造函数
 *     component指定这个辅助所提供的对象,并假定component有一个ajaxUpdate()方法处理响应,一个handleError()方法处理错误
 *     url指定这个辅助从服务器端获取数据时调用的url
 *     mothd指定HTTP请求方法,有效值有GET和POST
 *     requestParams以key=value格式的字符串组形势指定一组传递给请求的请求参数
 
*/

net.ContentLoader 
=   function ( component, url, method, requestParams )  {
   
this.component     = component;
   
this.url           = url;
   
this.requestParams = requestParams;
   
this.method        = method;
}


net.ContentLoader.prototype 
=   {

   getTransport: 
function() {
      
var transport;
      
if ( window.XMLHttpRequest )
         transport 
= new XMLHttpRequest();
      
else if ( window.ActiveXObject ) {
         
try {
            transport 
= new ActiveXObject('Msxml2.XMLHTTP');
         }

         
catch(err) {
            transport 
= new ActiveXObject('Microsoft.XMLHTTP');
         }

      }

      
return transport;
   }
,

   sendRequest: 
function() {

      
//if ( window.netscape && window.netscape.security.PrivilegeManager.enablePrivilege)
      //   netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');

      
var requestParams = []
      
for ( var i = 0 ; i < arguments.length ;  i++ )
         requestParams.push(arguments[i]);

      
var oThis = this;
      
var request = this.getTransport();
      request.open( 
this.method, this.url, true );
      request.setRequestHeader( 'Content
-Type', 'application/x-www-form-urlencoded');
      request.onreadystatechange 
= function() { oThis.handleAjaxResponse(request) };
      request.send( 
this.queryString(requestParams) );
  }
,

  queryString: 
function(args) {

     
var requestParams = [];
     
for ( var i = 0 ; i < this.requestParams.length ; i++ )
        requestParams.push(
this.requestParams[i]);
     
for ( var j = 0 ; j < args.length ; j++ )
        requestParams.push(args[j]);

     
var queryString = "";
     
if ( requestParams && requestParams.length > 0 ) {
        
for ( var i = 0 ; i < requestParams.length ; i++ )
           queryString 
+= requestParams[i] + '&';
        queryString 
= queryString.substring(0, queryString.length-1);
     }

     
return queryString;
  }
,

  handleAjaxResponse: 
function(request) {
     
if ( request.readyState == net.READY_STATE_COMPLETE ) {
        
if ( this.isSuccess(request) )
           
this.component.ajaxUpdate(request);
        
else
           
this.component.handleError(request);
     }

  }
,

  isSuccess: 
function(request) {
    
return  request.status == 0
        
|| (request.status >= 200 && request.status < 300);
  }


}
;


// -------------------- mTextSuggest.js
/**/ /*
 *    输入前提示组件,可重用在大部分需要这些功能的应用中,为此考虑一下特征
 *    1.不修改HTML标记,只简单修改head部分来注入组件行为
 *    2.组件能够使用不同的URL,并为所有配置选项提供合理的默认值
 *      3.组件不引入任何全局变量,防止全局变量混乱
 *    4.组件使用开源框架,以减少编码工作量,并提高方案的质量和健壮性
 
*/

 
 
 
/**/ /*
 ************************************创建TextSuggest***********************************************
 
*/

TextSuggest 
=  Class.create();

TextSuggest.prototype 
=   {

/**//*
 *    构造函数
 *    andId:要附加提示行为的文本字段的ID
 *    url:处理请求的服务器URL
 *    options:options对象为组件的每一个配置参数提供一个属性
 
*/

   initialize: 
function(anId, url, options) {
      
this.id          = anId;
      
this.textInput   = $(this.id);
      
      
//检测浏览器类型
      var browser = navigator.userAgent.toLowerCase();
      
this.isIE        = browser.indexOf("msie"!= -1;
      
this.isOpera     = browser.indexOf("opera")!= -1;
      
      
this.suggestions = [];
      
this.setOptions(options);
      
this.initAjax(url);

      
this.injectSuggestBehavior();
   }
,


/**//*
 *    为options每个属性都指定一个默认值
 *    然后,使用在构造时传入的options对象调用Prototype库的extend()方法,来覆盖默认值
 *    最后结果是一个合并的options对象,包含默认值和指定的覆盖值
 
*/

   setOptions: 
function(options) {
      
this.options = {
         suggestDivClassName: 'suggestDiv',   
//指定用来保存提示的生成div元素的css类名
         suggestionClassName: 'suggestion',   //指定用来保存每一条提示的生成span元素的css类名
         matchClassName     : 'match',        //指定用来保存匹配用户输入内容的提示部分的span元素的css类名
         matchTextWidth     :  true,          //用来指示为提示生成的div是否需要将自身大小设置为与它附加到的文本字段的宽度匹配
         selectionColor     : '#b1c09c',      //为选中的提示的背景颜色指定一个十六进制值
         matchAnywhere      : false,          //指定匹配是只查找字符串的开始位置,还是查找任意位置
         ignoreCase         : false,          //指定匹配是否是区别大小写的 
         count              : 10              //显示最大提示数量         
      }
.extend(options || {});
   }
,


/**//*
 ************************************支持Ajax***********************************************
 
*/


//-----------------------文本提示--发送Ajax请求
/**//*
 *    发送请求限制
 *    如果没有正在处理的请求,就调用callRicoAjaxEngine()来发送请求,反之不发送
 *    通过设置一个内部布尔属性this.pendingRequest实现,当Ajax请求发送时值为true,当返回的请求被处理后值为false
 *    用来基于服务器的处理速度限制事件的发送速度
 
*/

   sendRequestForSuggestions: 
function() {

     
if ( this.handlingRequest ) {
        
this.pendingRequest = true;
        
return;
     }


     
this.handlingRequest = true;
     
this.callRicoAjaxEngine();
   }
,

/**//*
 *    使用Rico提供的一个出色的Ajax处理函数
 *    ajaxEngine这个API支持为请求注册逻辑名,和注册知道如何处理Ajax响应的对象
 *    ajaxEngine.registerRequest()为一个(可能很长或很怪的)URL注册逻辑名,在发送请求时可以使用这个逻辑名
 *    ajaxEngine.registerAjaxObject()用来注册一个Ajax处理对象
 
*/

   initAjax: 
function(url) {
      ajaxEngine.registerRequest( 
this.id + '_request', url );
      ajaxEngine.registerAjaxObject( 
this.id + '_updater', this );
   }
,
   
/**//*
 *    发送请求
 *    先将对象的内部状态和options对象的特定属性存入数组callParms
 *    再将外部请求参数存入数组callParms
 *    通过ajaxEngine.sendRequest.apply( ajaxEngine, callParms )发送
 *  apply()方法见最后注释
 
*/

   callRicoAjaxEngine: 
function() {
      
var callParms = [];
      callParms.push( 
this.id + '_request');
      callParms.push( 'id
='             + this.id);
      callParms.push( 'count
='          + this.options.count);
      callParms.push( 'query
='          + this.lastRequestString);
      callParms.push( 'match_anywhere
=+ this.options.matchAnywhere);
      callParms.push( 'ignore_case
='    + this.options.ignoreCase);

      
var additionalParms = this.options.requestParameters || [];
      
forvar i=0 ; i < additionalParms.length ; i++ )
         callParms.push(additionalParms[i]);

      ajaxEngine.sendRequest.apply( ajaxEngine, callParms );
   }
,

//-----------------------文本提示--处理Ajax请求

/**//*
 *    处理请求
 *    224 先通过createSuggestions()方法将响应解析成提示在内存中的表现形式,保存在suggestions属性中
 *    226 如果没找到提示,将探出框隐藏,并清空隐藏字段中的内部值
 *    230 如果找到提示,创建下拉框的UI元素,使用提示组装,并显示给用户
 *      236 最后将this.handlingRequest重新设置为false,表示响应处理完成
 
*/

   ajaxUpdate: 
function( ajaxResponse ) {

      
this.createSuggestions( ajaxResponse );

      
if ( this.suggestions.length == 0 ) {
         
this.hideSuggestions();
         $( 
this.id + "_hidden" ).value = "";
      }

      
else {
         
this.updateSuggestionsDiv();
         
this.showSuggestions();
         
this.updateSelection(0);
      }


      
this.handlingRequest = false;

      
if ( this.pendingRequest ) {
         
this.pendingRequest    = false;
         
this.lastRequestString = this.textInput.value;
         
this.sendRequestForSuggestions();
      }

   }
,

   createSuggestions: 
function(ajaxResponse) {
      
this.suggestions = [];
      
var entries = ajaxResponse.getElementsByTagName('entry');
      
for ( var i = 0 ; i < entries.length ; i++ ) {
         
var strText  = this.getElementContent(entries[i].getElementsByTagName('text')[0]);
         
var strValue = this.getElementContent(entries[i].getElementsByTagName('value')[0]);
         
this.suggestions.push( { text: strText, value: strValue } );
      }

   }
,

/**//*
 ************************************事件处理***********************************************
 
*/

 
//行为注入
   injectSuggestBehavior: function() {

      
if ( this.isIE )
         
this.textInput.autocomplete = "off";   //关闭IE自动完成

      
var keyEventHandler = new TextSuggestKeyHandler(this);   //创建控制器
      
      
//Insertion.After由Prototype库提供,添加一个不可见文本字段来防止回车键提交表单
      new Insertion.After( this.textInput,
                           '
<input type="text" id="'+this.id+'_preventtsubmit'+'" style="display:none"/>' );
      
new Insertion.After( this.textInput,
                           '
<input type="hidden" name="'+this.id+'_hidden'+'" id="'+this.id+'_hidden'+'"/>' );

      
this.createSuggestionsDiv();     //创建UI
   }
,

//TextSuggest的选择处理方法
   moveSelectionUp: function() {
      
//selectedIndex: select对象中当前被选option的下标
      if ( this.selectedIndex > 0 ) {
         
this.updateSelection(this.selectedIndex - 1);
      }

   }
,

   moveSelectionDown: 
function() {
      
if ( this.selectedIndex < (this.suggestions.length - 1)  ) {
         
this.updateSelection(this.selectedIndex + 1);
      }

   }
,

   updateSelection: 
function(n) {
      
var span = $( this.id + "_" + this.selectedIndex );
      
if ( span ){
         span.style.backgroundColor 
= "";  //清除之前的选择
      }

      
this.selectedIndex = n;
      
var span = $( this.id + "_" + this.selectedIndex );
      
if ( span ){
         span.style.backgroundColor 
= this.options.selectionColor;
      }

   }
,

//文本输入处理函数
   handleTextInput: function() {
     
var previousRequest    = this.lastRequestString;    //上次请求的值
     this.lastRequestString = this.textInput.value;      //现在请求的值
     if ( this.lastRequestString == "" )
        
this.hideSuggestions();
     
else if ( this.lastRequestString != previousRequest ) {
        
this.sendRequestForSuggestions();                //数据的Ajax请求
     }

   }
,

   setInputFromSelection: 
function() {
     
var hiddenInput = $( this.id + "_hidden" );
     
var suggestion  = this.suggestions[ this.selectedIndex ];

     
this.textInput.value = suggestion.text;             //更新可见的值
     hiddenInput.value    = suggestion.value;            //更新隐藏的值  
     this.hideSuggestions();
   }
,



/**//*
 ************************************提示的弹出框界面***********************************************
 
*/

 
 
//创建DIV
   createSuggestionsDiv: function() {
      
this.suggestionsDiv = document.createElement("div");                   //创建DIV
      this.suggestionsDiv.className = this.options.suggestDivClassName;      //设置样式

      
var divStyle = this.suggestionsDiv.style;            //添加行为样式
      divStyle.position = 'absolute';
      divStyle.zIndex   
= 101;
      divStyle.display  
= "none";

      
this.textInput.parentNode.appendChild(this.suggestionsDiv);           //插入到文档中
   }
,

//定位弹出框
   positionSuggestionsDiv: function() {
      
//通过Rico提供的toDocumentPosition()方法来计算文本字段的绝对位置
      var textPos = RicoUtil.toDocumentPosition(this.textInput);
      
var divStyle = this.suggestionsDiv.style;
      divStyle.top  
= (textPos.y + this.textInput.offsetHeight) + "px";
      divStyle.left 
= textPos.x + "px";

      
if ( this.options.matchTextWidth )
         divStyle.width 
= (this.textInput.offsetWidth- this.padding()) + "px";
   }
,

//计算左边和右边的填充值
   padding: function() {
     
try{
      
var styleFunc = RicoUtil.getElementsComputedStyle;
      
var lPad    = styleFunc( this.suggestionsDiv, "paddingLeft",      "padding-left" );
      
var rPad    = styleFunc( this.suggestionsDiv, "paddingRight",     "padding-right" );
      
var lBorder = styleFunc( this.suggestionsDiv, "borderLeftWidth",  "border-left-width" );
      
var rBorder = styleFunc( this.suggestionsDiv, "borderRightWidth""border-right-width" );

      lPad    
= isNaN(lPad)    ? 0 : lPad;
      rPad    
= isNaN(rPad)    ? 0 : rPad;
      lBorder 
= isNaN(lBorder) ? 0 : lBorder;
      rBorder 
= isNaN(rBorder) ? 0 : rBorder;

      
return parseInt(lPad) + parseInt(rPad) + parseInt(lBorder) + parseInt(rBorder);
     }
catch (e){
      
return 0;
     }

   }
,

//创建弹出框的内容
   updateSuggestionsDiv: function() {
      
this.suggestionsDiv.innerHTML = "";                    //除去以前的内容
      var suggestLines = this.createSuggestionSpans();      //创建新内容
      for ( var i = 0 ; i < suggestLines.length ; i++ )
         
this.suggestionsDiv.appendChild(suggestLines[i]);
   }
,

//创建提示列表的条目
   createSuggestionSpans: function() {
      
var regExpFlags = "";
      
if ( this.options.ignoreCase )
         regExpFlags 
= 'i';
      
var startRegExp = "^";
      
if ( this.options.matchAnywhere )
         startRegExp 
= '';

      
var regExp  = new RegExp( startRegExp + this.lastRequestString, regExpFlags );

      
var suggestionSpans = [];
      
for ( var i = 0 ; i < this.suggestions.length ; i++ )
         suggestionSpans.push( 
this.createSuggestionSpan( i, regExp ) )

      
return suggestionSpans;
   }
,

//创建列表的条目span
   createSuggestionSpan: function( n, regExp ) {
      
var suggestion = this.suggestions[n];

      
var suggestionSpan = document.createElement("span");
      suggestionSpan.className 
= this.options.suggestionClassName;
      suggestionSpan.style.width   
= '100%';
      suggestionSpan.style.display 
= 'block';
      suggestionSpan.id            
= this.id + "_" + n;
      suggestionSpan.onmouseover   
= this.mouseoverHandler.bindAsEventListener(this);
      suggestionSpan.onclick       
= this.itemClickHandler.bindAsEventListener(this);

      
var textValues = this.splitTextValues( suggestion.text,
                                             
this.lastRequestString.length,
                                             regExp );

      
var textMatchSpan = document.createElement("span");
      textMatchSpan.id            
= this.id + "_match_" + n;
      textMatchSpan.className     
= this.options.matchClassName;
      textMatchSpan.onmouseover   
= this.mouseoverHandler.bindAsEventListener(this);
      textMatchSpan.onclick       
= this.itemClickHandler.bindAsEventListener(this);

      textMatchSpan.appendChild( document.createTextNode(textValues.mid) );

      suggestionSpan.appendChild( document.createTextNode( textValues.start ) );
      suggestionSpan.appendChild( textMatchSpan );
      suggestionSpan.appendChild( document.createTextNode( textValues.end ) );

      
return suggestionSpan;
   }
,

   splitTextValues: 
function( text, len, regExp ) {
      
var startPos  = text.search(regExp);
      
var matchText = text.substring( startPos, startPos + len );
      
var startText = startPos == 0 ? "" : text.substring(0, startPos);
      
var endText   = text.substring( startPos + len );
      
return { start: startText, mid: matchText, end: endText };
   }
,

//列表条目的鼠标事件处理函数
   mouseoverHandler: function(e) {
      
var src = e.srcElement ? e.srcElement : e.target;
      
var index = parseInt(src.id.substring(src.id.lastIndexOf('_')+1));
      
this.updateSelection(index);
   }
,

   itemClickHandler: 
function(e) {
      
this.mouseoverHandler(e);
      
this.hideSuggestions();
      
this.textInput.focus();
   }
,

//显示和隐藏弹出框
   showSuggestions: function() {
      
var divStyle = this.suggestionsDiv.style;
      
if ( divStyle.display == '' )
         
return;
      
this.positionSuggestionsDiv();
      divStyle.display 
= '';
   }
,

   hideSuggestions: 
function() {
      
this.suggestionsDiv.style.display = 'none';
   }
,

   getElementContent: 
function(element) {
      
return element.firstChild.data;
   }

}



// ---------------控制器对象,用来担任事件的代理


TextSuggestKeyHandler 
=  Class.create();

TextSuggestKeyHandler.prototype 
=   {

/**//*
 *   构造方法
 *   控制器保存提示组件的引用和HTML标单的输入字段,通过this.addKeyHandling()为输入字段添加处理函数
 
*/

   initialize: 
function( textSuggest ) {
      
this.textSuggest = textSuggest;
      
this.input       = this.textSuggest.textInput;
      
this.addKeyHandling();
   }
,

//bindAsEventListener()是prototype库提供的一个闭包机制,该机制允许处理函数调用控制器的方法
   addKeyHandling: function() {
      
this.input.onkeyup    = this.keyupHandler.bindAsEventListener(this);
      
this.input.onkeydown  = this.keydownHandler.bindAsEventListener(this);
      
this.input.onblur     = this.onblurHandler.bindAsEventListener(this);
      
if ( this.isOpera )
         
this.input.onkeypress = this.keyupHandler.bindAsEventListener(this);
   }
,

//处理按下按键
   keydownHandler: function(e) {
      
var upArrow   = 38;     //上箭头
      var downArrow = 40;     //下箭头

      
if ( e.keyCode == upArrow ) {
         
this.textSuggest.moveSelectionUp();
         setTimeout( 
this.moveCaretToEnd.bind(this), 1 );
      }

      
else if ( e.keyCode == downArrow ){
         
this.textSuggest.moveSelectionDown();
      }

   }
,

//处理放开按键
   keyupHandler: function(e) {
      
if ( this.input.length == 0 && !this.isOpera )
         
this.textSuggest.hideSuggestions();

     
if ( !this.handledSpecialKeys(e) )
        
this.textSuggest.handleTextInput();
   }
,

//几个特殊 按键
   handledSpecialKeys: function(e) {
      
var enterKey  = 13;   //上箭头
      var upArrow   = 38;   //下箭头
      var downArrow = 40;   //回车键

      
if ( e.keyCode == upArrow || e.keyCode == downArrow ) {
         
return true;
      }

      
else if ( e.keyCode == enterKey ) {
         
this.textSuggest.setInputFromSelection();
         
return true;
      }


      
return false;
   }
,

//修改上箭头在文本字段中使光标后退的默认行为
   moveCaretToEnd: function() {
      
var pos = this.input.value.length;
      
if (this.input.setSelectionRange) {
         
this.input.setSelectionRange(pos,pos);
      }

      
else if(this.input.createTextRange){
         
var m = this.input.createTextRange();
         m.moveStart('character',pos);
         m.collapse();
         m.select();
      }

   }
,

   onblurHandler: 
function(e) {
      
if ( this.textSuggest.suggestionsDiv.style.display == '' )
         
this.textSuggest.setInputFromSelection();
      
this.textSuggest.hideSuggestions();
   }


}
;

你可能感兴趣的:(Ajax学习--输入前提示)