//--------------------mContentLoader.js/**//**Ajax的辅助对象,执行所有与Ajax处理相关的工作**/varnet=newObject();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(){vartransport;if(window.XMLHttpRequest)transport=newXMLHttpRequest();elseif(window.ActiveXObject){try{transport=newActiveXObject('Msxml2.XMLHTTP');}catch(err){transport=newActiveXObject('Microsoft.XMLHTTP');}}returntransport;},sendRequest:function(){//if(window.netscape&&window.netscape.security.PrivilegeManager.enablePrivilege)//netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');varrequestParams=[]for(vari=0;i<arguments.length;i++)requestParams.push(arguments[i]);varoThis=this;varrequest=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){varrequestParams=[];for(vari=0;i<this.requestParams.length;i++)requestParams.push(this.requestParams[i]);for(varj=0;j<args.length;j++)requestParams.push(args[j]);varqueryString="";if(requestParams&&requestParams.length>0){for(vari=0;i<requestParams.length;i++)queryString+=requestParams[i]+'&';queryString=queryString.substring(0,queryString.length-1);}returnqueryString;},handleAjaxResponse:function(request){if(request.readyState==net.READY_STATE_COMPLETE){if(this.isSuccess(request))this.component.ajaxUpdate(request);elsethis.component.handleError(request);}},isSuccess:function(request){returnrequest.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);//检测浏览器类型varbrowser=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(){varcallParms=[];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);varadditionalParms=this.options.requestParameters||[];for(vari=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=[];varentries=ajaxResponse.getElementsByTagName('entry');for(vari=0;i<entries.length;i++){varstrText=this.getElementContent(entries[i].getElementsByTagName('text')[0]);varstrValue=this.getElementContent(entries[i].getElementsByTagName('value')[0]);this.suggestions.push({text:strText,value:strValue});}},/**//*************************************事件处理************************************************///行为注入injectSuggestBehavior:function(){if(this.isIE)this.textInput.autocomplete="off";//关闭IE自动完成varkeyEventHandler=newTextSuggestKeyHandler(this);//创建控制器//Insertion.After由Prototype库提供,添加一个不可见文本字段来防止回车键提交表单newInsertion.After(this.textInput,'<inputtype="text"id="'+this.id+'_preventtsubmit'+'"style="display:none"/>');newInsertion.After(this.textInput,'<inputtype="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){varspan=$(this.id+"_"+this.selectedIndex);if(span){span.style.backgroundColor="";//清除之前的选择}this.selectedIndex=n;varspan=$(this.id+"_"+this.selectedIndex);if(span){span.style.backgroundColor=this.options.selectionColor;}},//文本输入处理函数handleTextInput:function(){varpreviousRequest=this.lastRequestString;//上次请求的值this.lastRequestString=this.textInput.value;//现在请求的值if(this.lastRequestString=="")this.hideSuggestions();elseif(this.lastRequestString!=previousRequest){this.sendRequestForSuggestions();//数据的Ajax请求}},setInputFromSelection:function(){varhiddenInput=$(this.id+"_hidden");varsuggestion=this.suggestions[this.selectedIndex];this.textInput.value=suggestion.text;//更新可见的值hiddenInput.value=suggestion.value;//更新隐藏的值this.hideSuggestions();},/**//*************************************提示的弹出框界面************************************************///创建DIVcreateSuggestionsDiv:function(){this.suggestionsDiv=document.createElement("div");//创建DIVthis.suggestionsDiv.className=this.options.suggestDivClassName;//设置样式vardivStyle=this.suggestionsDiv.style;//添加行为样式divStyle.position='absolute';divStyle.zIndex=101;divStyle.display="none";this.textInput.parentNode.appendChild(this.suggestionsDiv);//插入到文档中},//定位弹出框positionSuggestionsDiv:function(){//通过Rico提供的toDocumentPosition()方法来计算文本字段的绝对位置vartextPos=RicoUtil.toDocumentPosition(this.textInput);vardivStyle=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{varstyleFunc=RicoUtil.getElementsComputedStyle;varlPad=styleFunc(this.suggestionsDiv,"paddingLeft","padding-left");varrPad=styleFunc(this.suggestionsDiv,"paddingRight","padding-right");varlBorder=styleFunc(this.suggestionsDiv,"borderLeftWidth","border-left-width");varrBorder=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;returnparseInt(lPad)+parseInt(rPad)+parseInt(lBorder)+parseInt(rBorder);}catch(e){return0;}},//创建弹出框的内容updateSuggestionsDiv:function(){this.suggestionsDiv.innerHTML="";//除去以前的内容varsuggestLines=this.createSuggestionSpans();//创建新内容for(vari=0;i<suggestLines.length;i++)this.suggestionsDiv.appendChild(suggestLines[i]);},//创建提示列表的条目createSuggestionSpans:function(){varregExpFlags="";if(this.options.ignoreCase)regExpFlags='i';varstartRegExp="^";if(this.options.matchAnywhere)startRegExp='';varregExp=newRegExp(startRegExp+this.lastRequestString,regExpFlags);varsuggestionSpans=[];for(vari=0;i<this.suggestions.length;i++)suggestionSpans.push(this.createSuggestionSpan(i,regExp))returnsuggestionSpans;},//创建列表的条目spancreateSuggestionSpan:function(n,regExp){varsuggestion=this.suggestions[n];varsuggestionSpan=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);vartextValues=this.splitTextValues(suggestion.text,this.lastRequestString.length,regExp);vartextMatchSpan=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));returnsuggestionSpan;},splitTextValues:function(text,len,regExp){varstartPos=text.search(regExp);varmatchText=text.substring(startPos,startPos+len);varstartText=startPos==0?"":text.substring(0,startPos);varendText=text.substring(startPos+len);return{start:startText,mid:matchText,end:endText};},//列表条目的鼠标事件处理函数mouseoverHandler:function(e){varsrc=e.srcElement?e.srcElement:e.target;varindex=parseInt(src.id.substring(src.id.lastIndexOf('_')+1));this.updateSelection(index);},itemClickHandler:function(e){this.mouseoverHandler(e);this.hideSuggestions();this.textInput.focus();},//显示和隐藏弹出框showSuggestions:function(){vardivStyle=this.suggestionsDiv.style;if(divStyle.display=='')return;this.positionSuggestionsDiv();divStyle.display='';},hideSuggestions:function(){this.suggestionsDiv.style.display='none';},getElementContent:function(element){returnelement.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){varupArrow=38;//上箭头vardownArrow=40;//下箭头if(e.keyCode==upArrow){this.textSuggest.moveSelectionUp();setTimeout(this.moveCaretToEnd.bind(this),1);}elseif(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){varenterKey=13;//上箭头varupArrow=38;//下箭头vardownArrow=40;//回车键if(e.keyCode==upArrow||e.keyCode==downArrow){returntrue;}elseif(e.keyCode==enterKey){this.textSuggest.setInputFromSelection();returntrue;}returnfalse;},//修改上箭头在文本字段中使光标后退的默认行为moveCaretToEnd:function(){varpos=this.input.value.length;if(this.input.setSelectionRange){this.input.setSelectionRange(pos,pos);}elseif(this.input.createTextRange){varm=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();}};