

//-------------------- 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 || [];
for( var 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();
}

};


posted on 2007-09-24 09:08
趙曦 閱讀(621)
評論(1) 編輯 收藏