?
/*
?*?定義一個全局對象,?屬性?Version?在發布的時候會替換為當前版本號
? */
var ?Prototype? = ?{
??Version:?' 1.3 . 1 ',
?? // ?一個空方法,其后的代碼常會用到,先前的版本該方法被定義于?Ajax?類中。
??emptyFunction:? function ()?{}
}
/*
?*?創建一種類型,注意其屬性?create?是一個方法,返回一個構造函數。
?*?一般使用如下?
?*?????var?X?=?Class.create();??返回一個類型,類似于?java?的一個Class實例。
?*?要使用?X?類型,需繼續用?new?X()來獲取一個實例,如同?java?的?Class.newInstance()方法。
?*
?*?返回的構造函數會執行名為?initialize?的方法,?initialize?是?Ruby?對象的構造器方法名字。
?*?此時initialize方法還沒有定義,其后的代碼中創建新類型時會建立相應的同名方法。
?*
?*?如果一定要從java上去理解。你可以理解為用Class.create()創建一個繼承java.lang.Class類的類。
?*?當然java不允許這樣做,因為Class類是final的
?*
? */
var ?Class? = ?{
??create:? function ()?{
???? return ? function ()?{
?????? this .initialize.apply( this ,?arguments);
????}
??}
}
/*
?*?創建一個對象,從變量名來思考,本意也許是定義一個抽象類,以后創建新對象都?extend?它。
?*?但從其后代碼的應用來看,?Abstract?更多是為了保持命名空間清晰的考慮。
?*?也就是說,我們可以給?Abstract?這個對象實例添加新的對象定義。
?*
?*?從java去理解,就是動態給一個對象創建內部類。
? */
var ?Abstract? = ? new ?Object();
Object.extend? = ? function (destination,?source)?{
?? for ?(property? in ?source)?{
????destination[property]? = ?source[property];
??}
?? return ?destination;
}
/*
?*?獲取參數對象的所有屬性和方法,有點象多重繼承。但是這種繼承是動態獲得的。
?*?如:
?*?????var?a?=?new?ObjectA(),?b?=?new?ObjectB();
?*?????var?c?=?a.extend(b);
?*?此時?c?對象同時擁有?a?和?b?對象的屬性和方法。但是與多重繼承不同的是,c?instanceof?ObjectB?將返回false。
?*
?*?舊版本的該方法定義如下:
?*?Object.prototype.extend?=?function(object)?{
?*?????for?(property?in?object)?{
?*?????????this[property]?=?object[property];
?*?????}
?*?????return?this;
?*?}
?*
?*?新的形式新定義了一個靜態方法?Object.extend,這樣做的目的大概是為了使代碼更為清晰
? */
Object.prototype.extend? = ? function (object)?{
?? return ?Object.extend.apply( this ,?[ this ,?object]);
}
/*
?*?這個方法很有趣,它封裝一個javascript函數對象,返回一個新函數對象,新函數對象的主體和原對象相同,
?*?但是bind()方法參數將被用作當前對象的對象。
?*?也就是說新函數中的?this?引用被改變為參數提供的對象。
?*?比如:
?*?????<input?type="text"?id="aaa"?value="aaa">
?*?????<input?type="text"?id="bbb"?value="bbb">
?*?????



..
?*?????<script>
?*?????????var?aaa?=?document.getElementById("aaa");
?*?????????var?bbb?=?document.getElementById("bbb");
?*?????????aaa.showValue?=?function()?{alert(this.value);}
?*?????????aaa.showValue2?=?aaa.showValue.bind(bbb);
?*?????</script>
?*?那么,調用aaa.showValue?將返回"aaa",?但調用aaa.showValue2?將返回"bbb"。
?*
?*?apply?是ie5.5后才出現的新方法(Netscape好像很早就支持了)。
?*?該方法更多的資料參考MSDN?http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthApply.asp
?*?閱讀其后的代碼就會發現,bind?被應用的很廣泛,該方法和?Object.prototype.extend?一樣是?Prototype?的核心。
?*?還有一個?call?方法,應用起來和?apply?類似。可以一起研究下。
? */
Function.prototype.bind? = ? function (object)?{
?? var ?__method? = ? this ;
?? return ? function ()?{
????__method.apply(object,?arguments);
??}
}
/*
?*?和bind一樣,不過這個方法一般用做html控件對象的事件處理。所以要傳遞event對象
?*?注意這時候,用到了?Function.call。它與?Function.apply?的不同好像僅僅是對參數形式的定義。
?*?如同?java?兩個過載的方法。
? */
Function.prototype.bindAsEventListener? = ? function (object)?{
?? var ?__method? = ? this ;
?? return ? function (event)?{
????__method.call(object,?event? || ?window.event);
??}
}
/*
?*?將整數形式RGB顏色值轉換為HEX形式
? */
Number.prototype.toColorPart? = ? function ()?{
?? var ?digits? = ? this .toString( 16 );
?? if ?( this ? < ? 16 )? return ?' 0 '? + ?digits;
?? return ?digits;
}
/*
?*?典型?Ruby?風格的函數,將參數中的方法逐個調用,返回第一個成功執行的方法的返回值
? */
var ?Try? = ?{
??these:? function ()?{
???? var ?returnValue;
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
?????? var ?lambda? = ?arguments[i];
?????? try ?{
????????returnValue? = ?lambda();
???????? break ;
??????}? catch ?(e)?{}
????}
???? return ?returnValue;
??}
}
/* ======================================================= */
/*
?*?一個設計精巧的定時執行器
?*?首先由?Class.create()?創建一個?PeriodicalExecuter?類型,
?*?然后用對象直接量的語法形式設置原型。
?*
?*?需要特別說明的是?rgisterCallback?方法,它調用上面定義的函數原型方法bind,?并傳遞自己為參數。
?*?之所以這樣做,是因為?setTimeout?默認總以?window?對象為當前對象,也就是說,如果?registerCallback?方法定義如下的話:
?*?????registerCallback:?function()?{
?*?????????setTimeout(this.onTimerEvent,?this.frequency?*?1000);
?*?????}
?*?那么,this.onTimeoutEvent?方法執行失敗,因為它無法訪問?this.currentlyExecuting?屬性。
?*?而使用了bind以后,該方法才能正確的找到this,也就是PeriodicalExecuter的當前實例。
? */
var ?PeriodicalExecuter? = ?Class.create();
PeriodicalExecuter.prototype? = ?{
??initialize:? function (callback,?frequency)?{
???? this .callback? = ?callback;
???? this .frequency? = ?frequency;
???? this .currentlyExecuting? = ? false ;
???? this .registerCallback();
??},
??registerCallback:? function ()?{
????setInterval( this .onTimerEvent.bind( this ),? this .frequency? * ? 1000 );
??},
??onTimerEvent:? function ()?{
???? if ?( ! this .currentlyExecuting)?{
?????? try ?{
???????? this .currentlyExecuting? = ? true ;
???????? this .callback();
??????}? finally ?{
???????? this .currentlyExecuting? = ? false ;
??????}
????}
??}
}
/* ======================================================= */
/*
?*?這個函數就?Ruby?了。我覺得它的作用主要有兩個
?*?1.??大概是?document.getElementById(id)?的最簡化調用。
?*?比如:$("aaa")?將返回?aaa?對象
?*?2.??得到對象數組
?*?比如:?$("aaa","bbb")?返回一個包括id為"aaa"和"bbb"兩個input控件對象的數組。
? */
function ?$()?{
?? var ?elements? = ? new ?Array();
?? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
???? var ?element? = ?arguments[i];
???? if ?( typeof ?element? == ?'string')
??????element? = ?document.getElementById(element);
???? if ?(arguments.length? == ? 1 )
?????? return ?element;
????elements.push(element);
??}
?? return ?elements;
}
/*
?*?為兼容舊版本的瀏覽器增加?Array?的?push?方法。
? */
if ?( ! Array.prototype.push)?{
??Array.prototype.push? = ? function ()?{
?????? var ?startLength? = ? this .length;
?????? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )
?????? this [startLength? + ?i]? = ?arguments[i];
????? return ? this .length;
??}
}
/*
?*?為兼容舊版本的瀏覽器增加?Function?的?apply?方法。
? */
if ?( ! Function.prototype.apply)?{
?? // ?Based?on?code?from?http://www.youngpup.net/
??Function.prototype.apply? = ? function (object,?parameters)?{
???? var ?parameterStrings? = ? new ?Array();
???? if ?( ! object)?????object? = ?window;
???? if ?( ! parameters)?parameters? = ? new ?Array();
???
???? for ?( var ?i? = ? 0 ;?i? < ?parameters.length;?i ++ )
??????parameterStrings[i]? = ?'parameters['? + ?i? + ?']';
???
????object.__apply__? = ? this ;
???? var ?result? = ?eval('object.__apply__('? +
??????parameterStrings.join(',?')? + ?')');
????object.__apply__? = ? null ;
???
???? return ?result;
??}
}
/*
?*?擴展?javascript?內置的?String?對象
? */
String.prototype.extend({
/*
?*去掉字符串中的<html>標簽
? */
??stripTags:? function ()?{
???? return ? this .replace( /< \ /? [ ^> ] +>/ gi,?'');
??},
/*
?*這個方法很常見,通常的實現都是用正則表達式替換特殊字符為html規范定義的命名實體或者十進制編碼,比如:
?*?string.replace(/&/g,?"&").replace(/</g,?"<").replace(/>/g,?">");
?*?而這里的實現借用瀏覽器自身的內部替換,確實巧妙。
? */
??escapeHTML:? function ()?{
???? var ?div? = ?document.createElement('div');
???? var ?text? = ?document.createTextNode( this );
????div.appendChild(text);
???? return ?div.innerHTML;
??},
?
/*
?*同上
? */
??unescapeHTML:? function ()?{
???? var ?div? = ?document.createElement('div');
????div.innerHTML? = ? this .stripTags();
???? return ?div.childNodes[ 0 ].nodeValue;
??}
});
/*
?*?定義?Ajax?對象,?靜態方法?getTransport?方法返回一個?XMLHttp?對象
? */
var ?Ajax? = ?{
??getTransport:? function ()?{
???? return ?Try.these(
?????? function ()?{ return ? new ?ActiveXObject('Msxml2.XMLHTTP')},
?????? function ()?{ return ? new ?ActiveXObject('Microsoft.XMLHTTP')},
?????? function ()?{ return ? new ?XMLHttpRequest()}
????)? || ? false ;
??}
}
/*
?*?我以為此時的Ajax對象起到命名空間的作用。
?*?Ajax.Base?聲明為一個基礎對象類型
?*?注意?Ajax.Base?并沒有使用?Class.create()?的方式來創建,我想是因為作者并不希望?Ajax.Base?被庫使用者實例化。
?*?作者在其他對象類型的聲明中,將會繼承于它。
?*?就好像?java?中的私有抽象類
? */
Ajax.Base? = ? function ()?{};
Ajax.Base.prototype? = ?{
/*
?*extend?(見上)?的用法真是讓人耳目一新
?*?options?首先設置默認屬性,然后再?extend?參數對象,那么參數對象中也有同名的屬性,那么就覆蓋默認屬性值。
?*?想想如果我寫這樣的實現,應該類似如下:
?????setOptions:?function(options)?{
??????this.options.methed?=?options.methed??options.methed?:?'post';
??????

.
?????}
?????我想很多時候,java?限制了?js?的創意。
? */
??setOptions:? function (options)?{
???? this .options? = ?{
??????method:???????'post',
??????asynchronous:? true ,
??????parameters:???''
????}.extend(options? || ?{});
??},
?
/*
?*如果 xmlhttp?調用返回正確的HTTP狀態值,函數返回ture, 反之false。
?*?xmlhttp 的?readyState?屬性不足以準確判斷?xmlhttp 遠程調用成功,該方法是readyState判斷的一個前提條件
? */
??responseIsSuccess:? function ()?{
???? return ? this .transport.status? == ?undefined
???????? || ? this .transport.status? == ? 0
???????? || ?( this .transport.status? >= ? 200 ? && ? this .transport.status? < ? 300 );
??},
?
/*
?*如果 xmlhttp?調用返回錯誤的HTTP狀態值,函數返回ture, 反之false。
? */
??responseIsFailure:? function ()?{
???? return ? ! this .responseIsSuccess();
??}
}
/*
?*?Ajax.Request?封裝?XmlHttp
? */
Ajax.Request? = ?Class.create();
/*
?*?定義四種事件(狀態),?參考http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp
? */
Ajax.Request.Events? =
??['Uninitialized',?'Loading',?'Loaded',?'Interactive',?'Complete'];
/*
?* 相比先前的版本,對于 xmlhttp?的調用和返回值處理分離得更為清晰
? */
Ajax.Request.prototype? = ?( new ?Ajax.Base()).extend({
??initialize:? function (url,?options)?{
???? this .transport? = ?Ajax.getTransport();
???? this .setOptions(options);
???? this .request(url);
??},
/*
?*新增加?request?方法封裝?xmlhttp 的調用過程。
? */
??request:? function (url)?{
???? var ?parameters? = ? this .options.parameters? || ?'';
???? if ?(parameters.length? > ? 0 )?parameters? += ?' & _ = ';
???? try ?{
?????? if ?( this .options.method? == ?'get')
????????url? += ?' ? '? + ?parameters;
?????? this .transport.open( this .options.method,?url,
???????? this .options.asynchronous);
?????? if ?( this .options.asynchronous)?{
???????? this .transport.onreadystatechange? = ? this .onStateChange.bind( this );
????????setTimeout(( function ()?{ this .respondToReadyState( 1 )}).bind( this ),? 10 );
??????}
?????? this .setRequestHeaders();
?????? var ?body? = ? this .options.postBody? ? ? this .options.postBody?:?parameters;
?????? this .transport.send( this .options.method? == ?'post'? ? ?body?:? null );
????}? catch ?(e)?{
????}
??},
/*
?*新增加的?setRequestHeaders?方法允許添加自定義的http?header
? */
??setRequestHeaders:? function ()?{
???? var ?requestHeaders? =
??????['X - Requested - With',?'XMLHttpRequest',
???????'X - Prototype - Version',?Prototype.Version];
???? if ?( this .options.method? == ?'post')?{
??????requestHeaders.push('Content - type',
????????'application / x - www - form - urlencoded');
/* ?Force?"Connection:?close"?for?Mozilla?browsers?to?work?around
?*?a?bug?where?XMLHttpReqeuest?sends?an?incorrect?Content-length
?*?header.?See?Mozilla?Bugzilla?#246651.
? */
?????? if ?( this .transport.overrideMimeType)
????????requestHeaders.push('Connection',?'close');
????}
/*
?*?其后的?apply?方法的調用有些奇技淫巧的意味
?*?從上下文中我們可以分析出?this.options.requestHeaders?是調用者自定義的http?header數組。
?*?requestHeaders?也是一個數組,將一個數組中的元素逐個添加到另一個元素中,直接調用
?*?requestHeaders.push(this.options.requestHeaders)
?*?是不行的,因為該調用導致?this.options.requestHeaders?整個數組作為一個元素添加到?requestHeaders中。
?*?javascript的Array對象還提供一個concat?的方法表面上滿足要求,但是concat實際上是創建一個新數組,將兩個數組的元素添加到新數組中。
?*?所以,下面的代碼也可以替換為
?*?requestHeaders?=?requestHeaders.concat(this.options.requestHeaders);
?*?很顯然,作者不喜歡這樣的代碼方式
?*?而?apply?方法的語法?apply([thisObj[,argArray]])?本身就要求第二個參數是一個數組或者arguments對象。
?*?所以巧妙的實現了?concat?函數的作用。
?*?令人拍案叫絕啊!
? */
???? if ?( this .options.requestHeaders)
??????requestHeaders.push.apply(requestHeaders,? this .options.requestHeaders);
???? for ?( var ?i? = ? 0 ;?i? < ?requestHeaders.length;?i? += ? 2 )
?????? this .transport.setRequestHeader(requestHeaders[i],?requestHeaders[i + 1 ]);
??},
?
??onStateChange:? function ()?{
???? var ?readyState? = ? this .transport.readyState;
/*
?*?如果不是?Loading?狀態,就調用回調函數
? */
???? if ?(readyState? != ? 1 )
?????? this .respondToReadyState( this .transport.readyState);
??},
/*
?*回調函數定義在?this.options?屬性中,比如:
??????var?option?=?{
?????????onLoaded?:?function(req)?{
};
?????????

??????}
??????new?Ajax.Request(url,?option);
? */
??respondToReadyState:? function (readyState)?{
???? var ?event? = ?Ajax.Request.Events[readyState];
/*
?*?新增的回調函數處理,調用者還可以在options中定義?on200,?onSuccess?這樣的回調函數
?*?在?readyState?為完成狀態的時候調用
? */
???? if ?(event? == ?'Complete')
??????( this .options['on'? + ? this .transport.status]
??????? || ? this .options['on'? + ?( this .responseIsSuccess()? ? ?'Success'?:?'Failure')]
??????? || ?Prototype.emptyFunction)( this .transport);
????( this .options['on'? + ?event]? || ?Prototype.emptyFunction)( this .transport);
/* ?Avoid?memory?leak?in?MSIE:?clean?up?the?oncomplete?event?handler? */
???? if ?(event? == ?'Complete')
?????? this .transport.onreadystatechange? = ?Prototype.emptyFunction;
??}
});
/*
?*?Ajax.Updater?用于綁定一個html元素與?XmlHttp調用的返回值。類似與?buffalo?的?bind。
?*?如果?options?中有?insertion(見后)?對象的話,?insertion?能提供更多的插入控制。
? */
Ajax.Updater? = ?Class.create();
Ajax.Updater.ScriptFragment? = ?'( ? : < script. *?> )((\n | .) *? )( ? : < \ / script > )';
Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
??initialize:? function (container,?url,?options)?{
/*
?*?containers?就是被綁定的?html?對象,xmlhttp的返回值被賦給該對象的?innerHTML?屬性。
?*?相比新版本,containers?根據container參數定義?success?和?failure?引用,如果它們被定義的話,根據xmlhttp調用是否成功來選擇
?*?更新對象,假想調用可能如下:
?*?var?c?=?{success:?$("successDiv"),?failure:?$("failureDiv")};
?*?new?Ajax.Updater(c,?url,?options);
?*?那么調用成功則?successDiv?顯示成功信息或者數據,反之?failureDiv?顯示錯誤信息
? */
???? this .containers? = ?{
??????success:?container.success? ? ?$(container.success)?:?$(container),
??????failure:?container.failure? ? ?$(container.failure)?:
????????(container.success? ? ? null ?:?$(container))
????}
???? this .transport? = ?Ajax.getTransport();
???? this .setOptions(options);
???? var ?onComplete? = ? this .options.onComplete? || ?Prototype.emptyFunction;
???? this .options.onComplete? = ?( function ()?{
?????? this .updateContent();
??????onComplete( this .transport);
????}).bind( this );
???? this .request(url);
??},
??updateContent:? function ()?{
???? var ?receiver? = ? this .responseIsSuccess()? ?
?????? this .containers.success?:? this .containers.failure;
???? var ?match???? = ? new ?RegExp(Ajax.Updater.ScriptFragment,?'img');
???? var ?response? = ? this .transport.responseText.replace(match,?'');
???? var ?scripts?? = ? this .transport.responseText.match(match);
???? if ?(receiver)?{
?????? if ?( this .options.insertion)?{
???????? new ? this .options.insertion(receiver,?response);
??????}? else ?{
????????receiver.innerHTML? = ?response;
??????}
????}
???? if ?( this .responseIsSuccess())?{
?????? if ?( this .onComplete)
????????setTimeout(( function ()?{ this .onComplete(
?????????? this .transport)}).bind( this ),? 10 );
????}
/*
?*?如果調用者在傳入的options參數中定義?evalScripts=true,同時xmlhttp返回值的html中包含<script>標簽的話,執行該腳本
? */
???? if ?( this .options.evalScripts? && ?scripts)?{
/*
?*?注意前二十行左右還有一個?match?的聲明
?*?var?match????=?new?RegExp(Ajax.Updater.ScriptFragment,?'img');
?*?和此處的區別就是,正則表達式匹配標記多一個?"g"。
?*?多個g,?所以?scripts?是一個數組,數組中每個元素是一段?<script>
</script>?文本。
?*?沒有g,?scripts[i].match(match)[1]?匹配的就是?<script>標記中的?script?代碼。
?*?關于正則表達式,請參考javascript的相關資料。
? */
??????match? = ? new ?RegExp(Ajax.Updater.ScriptFragment,?'im');
??????setTimeout(( function ()?{
???????? for ?( var ?i? = ? 0 ;?i? < ?scripts.length;?i ++ )
??????????eval(scripts[i].match(match)[ 1 ]);
??????}).bind( this ),? 10 );
????}
??}
});
/*
?*?定期更新器
? */
Ajax.PeriodicalUpdater? = ?Class.create();
Ajax.PeriodicalUpdater.prototype? = ?( new ?Ajax.Base()).extend({
??initialize:? function (container,?url,?options)?{
???? this .setOptions(options);
???? this .onComplete? = ? this .options.onComplete;
???? this .frequency? = ?( this .options.frequency? || ? 2 );
??? // ?decay?可能是一個時間調整因素
???? this .decay? = ? 1 ;
???? this .updater? = ?{};
???? this .container? = ?container;
???? this .url? = ?url;
???? this .start();
??},
??start:? function ()?{
???? this .options.onComplete? = ? this .updateComplete.bind( this );
???? this .onTimerEvent();
??},
??stop:? function ()?{
???? this .updater.onComplete? = ?undefined;
????clearTimeout( this .timer);
????( this .onComplete? || ?Ajax.emptyFunction).apply( this ,?arguments);
??},
??updateComplete:? function (request)?{
???? if ?( this .options.decay)?{
?????? this .decay? = ?(request.responseText? == ? this .lastText? ?
???????? this .decay? * ? this .options.decay?:? 1 );
?????? this .lastText? = ?request.responseText;
????}
???? this .timer? = ?setTimeout( this .onTimerEvent.bind( this ),
?????? this .decay? * ? this .frequency? * ? 1000 );
??},
??onTimerEvent:? function ()?{
???? this .updater? = ? new ?Ajax.Updater( this .container,? this .url,? this .options);
??}
});
/*
?*?根據?class?attribute?的名字得到對象數組,支持?multiple?class
?*
? */
document.getElementsByClassName? = ? function (className)?{
?? var ?children? = ?document.getElementsByTagName(' * ')? || ?document.all;
?? var ?elements? = ? new ?Array();
?
?? for ?( var ?i? = ? 0 ;?i? < ?children.length;?i ++ )?{
???? var ?child? = ?children[i];
???? var ?classNames? = ?child.className.split('?');
???? for ?( var ?j? = ? 0 ;?j? < ?classNames.length;?j ++ )?{
?????? if ?(classNames[j]? == ?className)?{
????????elements.push(child);
???????? break ;
??????}
????}
??}
?
?? return ?elements;
}
/* ======================================================= */
/*
?*?Element?就象一個?java?的工具類,主要用來?隱藏/顯示/銷除?對象,以及獲取對象的簡單屬性。
?*
? */
if ?( ! window.Element)?{
?? var ?Element? = ? new ?Object();
}
Object.extend(Element,?{
/*
?*切換?顯示/隱藏
? */
??toggle:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
?????? var ?element? = ?$(arguments[i]);
??????element.style.display? =
????????(element.style.display? == ?'none'? ? ?''?:?'none');
????}
??},
??hide:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
?????? var ?element? = ?$(arguments[i]);
??????element.style.display? = ?'none';
????}
??},
??show:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
?????? var ?element? = ?$(arguments[i]);
??????element.style.display? = ?'';
????}
??},
/*
?*從父節點中移除
? */
??remove:? function (element)?{
????element? = ?$(element);
????element.parentNode.removeChild(element);
??},
???
??getHeight:? function (element)?{
????element? = ?$(element);
???? return ?element.offsetHeight;
??},
/*
?*是否擁有?class?屬性值
? */
??hasClassName:? function (element,?className)?{
????element? = ?$(element);
???? if ?( ! element)
?????? return ;
???? var ?a? = ?element.className.split('?');
???? for ?( var ?i? = ? 0 ;?i? < ?a.length;?i ++ )?{
?????? if ?(a[i]? == ?className)
???????? return ? true ;
????}
???? return ? false ;
??},
/*
?*為對象添加?class?屬性值
? */
??addClassName:? function (element,?className)?{
????element? = ?$(element);
????Element.removeClassName(element,?className);
????element.className? += ?'?'? + ?className;
??},
/*
?*為對象移除?class?屬性值
? */
??removeClassName:? function (element,?className)?{
????element? = ?$(element);
???? if ?( ! element)
?????? return ;
???? var ?newClassName? = ?'';
???? var ?a? = ?element.className.split('?');
???? for ?( var ?i? = ? 0 ;?i? < ?a.length;?i ++ )?{
?????? if ?(a[i]? != ?className)?{
???????? if ?(i? > ? 0 )
??????????newClassName? += ?'?';
????????newClassName? += ?a[i];
??????}
????}
????element.className? = ?newClassName;
??},
?
?? // ?removes?whitespace-only?text?node?children
??cleanWhitespace:? function (element)?{
???? var ?element? = ?$(element);
???? for ?( var ?i? = ? 0 ;?i? < ?element.childNodes.length;?i ++ )?{
?????? var ?node? = ?element.childNodes[i];
?????? if ?(node.nodeType? == ? 3 ? && ? !/ \S / .test(node.nodeValue))
????????Element.remove(node);
????}
??}
});
/*
?*?為?Element.toggle?做了一個符號連接,大概是兼容性的考慮
? */
var ?Toggle? = ? new ?Object();
Toggle.display? = ?Element.toggle;
/* ======================================================= */
/*
?*?動態插入內容的實現,MS的Jscript實現中對象有一個?insertAdjacentHTML?方法
?*?(http:?//msdn.microsoft.com/workshop/author/dhtml/reference/methods/insertadjacenthtml.asp)
?*?這里算是一個對象形式的封裝。
? */
Abstract.Insertion? = ? function (adjacency)?{
?? this .adjacency? = ?adjacency;
}
Abstract.Insertion.prototype? = ?{
??initialize:? function (element,?content)?{
???? this .element? = ?$(element);
???? this .content? = ?content;
???
???? if ?( this .adjacency? && ? this .element.insertAdjacentHTML)?{
?????? this .element.insertAdjacentHTML( this .adjacency,? this .content);
????}? else ?{
/*
?*?gecko?不支持?insertAdjacentHTML?方法,但可以用如下代碼代替
? */
?????? this .range? = ? this .element.ownerDocument.createRange();
/*
?*?如果定義了?initializeRange?方法,則實行,這里相當與定義了一個抽象的?initializeRange?方法
? */
?????? if ?( this .initializeRange)? this .initializeRange();
?????? this .fragment? = ? this .range.createContextualFragment( this .content);
/*
?*?insertContent?也是一個抽象方法,子類必須實現
? */
?????? this .insertContent();
????}
??}
}
/*
?*?prototype?加深了我的體會,就是寫js?如何去遵循 Don’t?Repeat?Yourself?(DRY)?原則
?*?上文中?Abstract.Insertion?算是一個抽象類,定義了名為 initializeRange?的一個抽象方法
?*?var?Insertion?=?new?Object() 建立一個命名空間
?*?Insertion.Before|Top|Bottom|After?就象是四個java中的四個靜態內部類,而它們分別繼承于Abstract.Insertion,并實現了initializeRange方法。
? */
var ?Insertion? = ? new ?Object();
/*
?*將內容插入到指定節點的前面,?與指定節點同級
*/
Insertion.Before? = ?Class.create();
Insertion.Before.prototype? = ?( new ?Abstract.Insertion('beforeBegin')).extend({
??initializeRange:? function ()?{
???? this .range.setStartBefore( this .element);
??},
?
??insertContent:? function ()?{
???? this .element.parentNode.insertBefore( this .fragment,? this .element);
??}
});
/*
?*將內容插入到指定節點的第一個子節點前,于是內容變為該節點的第一個子節點
*/
Insertion.Top? = ?Class.create();
Insertion.Top.prototype? = ?( new ?Abstract.Insertion('afterBegin')).extend({
??initializeRange:? function ()?{
???? this .range.selectNodeContents( this .element);
???? this .range.collapse( true );
??},
?
??insertContent:? function ()?{?
???? this .element.insertBefore( this .fragment,? this .element.firstChild);
??}
});
/*
?*將內容插入到指定節點的最后,于是內容變為該節點的最后一個子節點
*/
Insertion.Bottom? = ?Class.create();
Insertion.Bottom.prototype? = ?( new ?Abstract.Insertion('beforeEnd')).extend({
??initializeRange:? function ()?{
???? this .range.selectNodeContents( this .element);
???? this .range.collapse( this .element);
??},
?
??insertContent:? function ()?{
???? this .element.appendChild( this .fragment);
??}
});
/*
?*將內容插入到指定節點的后面,?與指定節點同級
*/
Insertion.After? = ?Class.create();
Insertion.After.prototype? = ?( new ?Abstract.Insertion('afterEnd')).extend({
??initializeRange:? function ()?{
???? this .range.setStartAfter( this .element);
??},
?
??insertContent:? function ()?{
???? this .element.parentNode.insertBefore( this .fragment,
?????? this .element.nextSibling);
??}
});
/*
?*?針對?頁面元素對象(一般都是表單控件)的工具類,提供一些簡單靜態方法
?*?這些方法顯然常用在表單處理中
?*?注意?Field?這種聲明方式類似于?java?聲明一個靜態的?singleton?工具類
?*?等同于?:
?*???var?Field?=?new?Object();
?*???Field.extend({
});
?*
?*?后文中的?Form,?Event,?Position?對象聲明方式如出一轍
? */
var ?Field? = ?{
/*
?*清除參數引用的對象的值
? */
??clear:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )
??????$(arguments[i]).value? = ?'';
??},
/*
?*使參數引用對象獲取焦點
? */
??focus:? function (element)?{
????$(element).focus();
??},
?
/*
?*判斷參數引用對象是否有非空值,如為空值,返回false,?反之true
? */
??present:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )
?????? if ?($(arguments[i]).value? == ?'')? return ? false ;
???? return ? true ;
??},
?
/*
?*使選中參數引用對象
? */
??select:? function (element)?{
????$(element).select();
??},
???
/*
?*使參數引用對象處于可編輯狀態
? */
??activate:? function (element)?{
????$(element).focus();
????$(element).select();
??}
}
/* ======================================================= */
/*
?*?表單工具類
? */
var ?Form? = ?{
/*
?*將表單元素序列化后的值(其實就是?name=value?形式的名值配對)組合成?QueryString?的形式
? */
??serialize:? function (form)?{
???? var ?elements? = ?Form.getElements($(form));
???? var ?queryComponents? = ? new ?Array();
???
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )?{
?????? var ?queryComponent? = ?Form.Element.serialize(elements[i]);
?????? if ?(queryComponent)
????????queryComponents.push(queryComponent);
????}
???
???? return ?queryComponents.join(' & ');
??},
?
/*
?*得到表單的所有元素對象
? */
??getElements:? function (form)?{
???? var ?form? = ?$(form);
???? var ?elements? = ? new ?Array();
???? for ?(tagName? in ?Form.Element.Serializers)?{
?????? var ?tagElements? = ?form.getElementsByTagName(tagName);
?????? for ?( var ?j? = ? 0 ;?j? < ?tagElements.length;?j ++ )
????????elements.push(tagElements[j]);
????}
???? return ?elements;
??},
?
/*
?*根據?type?和?name?過濾得到表單中符合的?<input>?對象
? */
??getInputs:? function (form,?typeName,?name)?{
???? var ?form? = ?$(form);
???? var ?inputs? = ?form.getElementsByTagName('input');
???
???? if ?( ! typeName? && ? ! name)
?????? return ?inputs;
?????
???? var ?matchingInputs? = ? new ?Array();
???? for ?( var ?i? = ? 0 ;?i? < ?inputs.length;?i ++ )?{
?????? var ?input? = ?inputs[i];
?????? if ?((typeName? && ?input.type? != ?typeName)? ||
??????????(name? && ?input.name? != ?name))
???????? continue ;
??????matchingInputs.push(input);
????}
???? return ?matchingInputs;
??},
/*
?*將指定表單的元素置于不可用狀態
? */ ?
??disable:? function (form)?{
???? var ?elements? = ?Form.getElements(form);
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )?{
?????? var ?element? = ?elements[i];
??????element.blur();
??????element.disabled? = ?' true ';
????}
??},
/*
?*將指定表單的元素置于可用狀態
? */
??enable:? function (form)?{
???? var ?elements? = ?Form.getElements(form);
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )?{
?????? var ?element? = ?elements[i];
??????element.disabled? = ?'';
????}
??},
/*
?*使表單的第一個非?hidden?類型而且處于可用狀態的元素獲得焦點
? */
??focusFirstElement:? function (form)?{
???? var ?form? = ?$(form);
???? var ?elements? = ?Form.getElements(form);
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )?{
?????? var ?element? = ?elements[i];
?????? if ?(element.type? != ?'hidden'? && ? ! element.disabled)?{
????????Field.activate(element);
???????? break ;
??????}
????}
??},
?? /*
?*?重置表單
? */
??reset:? function (form)?{
????$(form).reset();
??}
}
/*
?*?表單元素工具類
? */
Form.Element? = ?{
/*
?*返回表單元素的值先序列化,?其實就是?name=value?形式的名值配對
? */
??serialize:? function (element)?{
???? var ?element? = ?$(element);
???? var ?method? = ?element.tagName.toLowerCase();
???? var ?parameter? = ?Form.Element.Serializers[method](element);
???
???? if ?(parameter)
?????? return ?encodeURIComponent(parameter[ 0 ])? + ?' = '? +
????????encodeURIComponent(parameter[ 1 ]);???????????????????
??},
?
/*
?*?返回表單元素的值
? */
??getValue:? function (element)?{
???? var ?element? = ?$(element);
???? var ?method? = ?element.tagName.toLowerCase();
???? var ?parameter? = ?Form.Element.Serializers[method](element);
???
???? if ?(parameter)
?????? return ?parameter[ 1 ];
??}
}
/*
?*?prototype?的所謂序列化其實就是將表單的名字和值組合成一個數組
? */
Form.Element.Serializers? = ?{
??input:? function (element)?{
???? switch ?(element.type.toLowerCase())?{
?????? case ?'submit':
?????? case ?'hidden':
?????? case ?'password':
?????? case ?'text':
???????? return ?Form.Element.Serializers.textarea(element);
?????? case ?'checkbox':?
?????? case ?'radio':
???????? return ?Form.Element.Serializers.inputSelector(element);
????}
???? return ? false ;
??},
/*
?*單/多選框?由此方法處理序列化
? */
??inputSelector:? function (element)?{
???? if ?(element.checked)
?????? return ?[element.name,?element.value];
??},
/*
?*textarea?由此方法處理序列化
? */
??textarea:? function (element)?{
???? return ?[element.name,?element.value];
??},
/*
?*select?下拉列表由此方法處理序列化
? */
??select:? function (element)?{
???? var ?value? = ?'';
???? if ?(element.type? == ?'select - one')?{
?????? var ?index? = ?element.selectedIndex;
?????? if ?(index? >= ? 0 )
????????value? = ?element.options[index].value? || ?element.options[index].text;
????}? else ?{
/*
?*?支持?select-mulitple?的下拉列表,返回的數組的第二個元素,是一個值數組
? */
??????value? = ? new ?Array();
?????? for ?( var ?i? = ? 0 ;?i? < ?element.length;?i ++ )?{
???????? var ?opt? = ?element.options[i];
???????? if ?(opt.selected)
??????????value.push(opt.value? || ?opt.text);
??????}
????}
???? return ?[element.name,?value];
??}
}
/* ======================================================= */
/*
?*?Form.Element.getValue?會經常用到,所以做了一個快捷引用
?*?取得某個表單控件的值,可以簡化調用為?$F("username"),真是方便啊
? */
var ?$F? = ?Form.Element.getValue;
/* ======================================================= */
/*
?*?Abstract.TimedObserver?也沒有用?Class.create()?來創建,和Ajax.Base?意圖應該一樣
?*?Abstract.TimedObserver?顧名思義,是套用Observer設計模式來跟蹤指定表單元素,
?*?當表單元素的值發生變化的時候,就執行回調函數
?*
?*?我想 Observer?與注冊onchange事件相似,不同點在于?onchange?事件是在元素失去焦點的時候才激發。
?*?同樣的與?onpropertychange?事件也相似,不過它只關注表單元素的值的變化,而且提供timeout的控制。
?*
?*?除此之外,Observer?的好處大概就在與更面向對象,另外可以動態的更換回調函數,這就比注冊事件要靈活一些。
?*?Observer?應該可以勝任動態數據校驗,或者多個關聯下拉選項列表的連動等等
?*
? */
Abstract.TimedObserver? = ? function ()?{}
/*
?*?這個設計和?PeriodicalExecuter?一樣,bind?方法是實現的核心
? */
Abstract.TimedObserver.prototype? = ?{
??initialize:? function (element,?frequency,?callback)?{
???? this .frequency? = ?frequency;
???? this .element??? = ?$(element);
???? this .callback?? = ?callback;
???
???? this .lastValue? = ? this .getValue();
???? this .registerCallback();
??},
?
??registerCallback:? function ()?{
????setInterval( this .onTimerEvent.bind( this ),? this .frequency? * ? 1000 );
??},
?
??onTimerEvent:? function ()?{
???? var ?value? = ? this .getValue();
???? if ?( this .lastValue? != ?value)?{
?????? this .callback( this .element,?value);
?????? this .lastValue? = ?value;
????}
??}
}
/*
?*?Form.Element.Observer?監視指定表單域的值是否變化
? */
Form.Element.Observer? = ?Class.create();
Form.Element.Observer.prototype? = ?( new ?Abstract.TimedObserver()).extend({
??getValue:? function ()?{
???? return ?Form.Element.getValue( this .element);
??}
});
/*
?*?Form.Element.Observer?監視指定表單所有控件的值是否有變化
? */
Form.Observer? = ?Class.create();
Form.Observer.prototype? = ?( new ?Abstract.TimedObserver()).extend({
??getValue:? function ()?{
???? return ?Form.serialize( this .element);
??}
});
/* ======================================================= */
/*
?*?EventObserver?相比上面的?TimedObserver,是更具主動性的一種監測
?*?它直接為表單控件(根據?type?的不同)?注冊相應的事件處理,?只要發現某個控件值發生改變,就執行回調函數
? */
Abstract.EventObserver? = ? function ()?{}
Abstract.EventObserver.prototype? = ?{
??initialize:? function (element,?callback)?{
???? this .element?? = ?$(element);
???? this .callback? = ?callback;
???
???? this .lastValue? = ? this .getValue();
???? if ?( this .element.tagName.toLowerCase()? == ?'form')
?????? this .registerFormCallbacks();
???? else
?????? this .registerCallback( this .element);
??},
?
??onElementEvent:? function ()?{
???? var ?value? = ? this .getValue();
???? if ?( this .lastValue? != ?value)?{
?????? this .callback( this .element,?value);
?????? this .lastValue? = ?value;
????}
??},
?
??registerFormCallbacks:? function ()?{
???? var ?elements? = ?Form.getElements( this .element);
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )
?????? this .registerCallback(elements[i]);
??},
?
??registerCallback:? function (element)?{
???? if ?(element.type)?{
?????? switch ?(element.type.toLowerCase())?{
/*
?*?checkbox?和?radio?類型的控件注冊?onclick?事件處理
? */
???????? case ?'checkbox':?
???????? case ?'radio':
??????????element.target? = ? this ;
??????????element.prev_onclick? = ?element.onclick? || ?Prototype.emptyFunction;
/*
?*?相信這里有改進的空間,應該使用其后的?Event對象提供的注冊管理功能來統一注冊
? */
??????????element.onclick? = ? function ()?{
???????????? this .prev_onclick();
???????????? this .target.onElementEvent();
??????????}
?????????? break ;
/*
?*?其他類型的控件注冊?onchange?事件處理
? */
???????? case ?'password':
???????? case ?'text':
???????? case ?'textarea':
???????? case ?'select - one':
???????? case ?'select - multiple':
??????????element.target? = ? this ;
??????????element.prev_onchange? = ?element.onchange? || ?Prototype.emptyFunction;
??????????element.onchange? = ? function ()?{
???????????? this .prev_onchange();
???????????? this .target.onElementEvent();
??????????}
?????????? break ;
??????}
????}???
??}
}
/*
?*?監視指定表單控件
? */
Form.Element.EventObserver? = ?Class.create();
Form.Element.EventObserver.prototype? = ?( new ?Abstract.EventObserver()).extend({
??getValue:? function ()?{
???? return ?Form.Element.getValue( this .element);
??}
});
/*
?*?監視指定表單所有控件
? */
Form.EventObserver? = ?Class.create();
Form.EventObserver.prototype? = ?( new ?Abstract.EventObserver()).extend({
??getValue:? function ()?{
???? return ?Form.serialize( this .element);
??}
});
/*
?*?封裝事件處理的靜態工具對象
? */
if ?( ! window.Event)?{
?? var ?Event? = ? new ?Object();
}
Object.extend(Event,?{
??KEY_BACKSPACE:? 8 ,
??KEY_TAB:??????? 9 ,
??KEY_RETURN:??? 13 ,
??KEY_ESC:?????? 27 ,
??KEY_LEFT:????? 37 ,
??KEY_UP:??????? 38 ,
??KEY_RIGHT:???? 39 ,
??KEY_DOWN:????? 40 ,
??KEY_DELETE:??? 46 ,
??element:? function (event)?{
???? return ?event.target? || ?event.srcElement;
??},
??isLeftClick:? function (event)?{
???? return ?(((event.which)? && ?(event.which? == ? 1 ))? ||
????????????((event.button)? && ?(event.button? == ? 1 )));
??},
/*
?*click事件時鼠標以頁面為基準的x坐標值,?考慮到了滾動條導致的位移差
? */
??pointerX:? function (event)?{
???? return ?event.pageX? || ?(event.clientX? +
??????(document.documentElement.scrollLeft? || ?document.body.scrollLeft));
??},
/*
?*click事件時鼠標以頁面為基準的y坐標值,?考慮到了滾動條導致的位移差
? */
??pointerY:? function (event)?{
???? return ?event.pageY? || ?(event.clientY? +
??????(document.documentElement.scrollTop? || ?document.body.scrollTop));
??},
/*
?*停止冒泡(參見?http://www.quirksmode.org/js/events_order.html)?和阻止瀏覽器執行與事件相關的默認動作
?*?比如
?*?<a?>google</a>
?*?那么點擊該連接,頁面并不會執行轉向
? */
??stop:? function (event)?{
???? if ?(event.preventDefault)?{
??????event.preventDefault();
??????event.stopPropagation();
????}? else ?{
??????event.returnValue? = ? false ;
????}
??},
?? // ?find?the?first?node?with?the?given?tagName,?starting?from?the
?? // ?node?the?event?was?triggered?on;?traverses?the?DOM?upwards
/*
?*找到事件元素的父級元素中,最接近事件元素且等同于指定標簽名的父元素。
?*?如果到達頂級元素(HTML),那么就返回頂級元素
? */
??findElement:? function (event,?tagName)?{
???? var ?element? = ?Event.element(event);
???? while ?(element.parentNode? && ?( ! element.tagName? ||
????????(element.tagName.toUpperCase()? != ?tagName.toUpperCase())))
??????element? = ?element.parentNode;
???? return ?element;
??},
/*
?*其后的代碼封裝了事件的注冊和反注冊,避免ie的內存泄露的bug
?*?參見??http://javascript.weblogsinc.com/entry/1234000267034921/
? */
??observers:? false ,
?
/*
?*this.observers?的數據格式是一個二維數組,二維的數組分別四個元素分別是
?*?[注冊事件對象,事件名,事件處理函數,事件處理模式布爾值]
? */
??_observeAndCache:? function (element,?name,?observer,?useCapture)?{
???? if ?( ! this .observers)? this .observers? = ?[];
???? if ?(element.addEventListener)?{
?????? this .observers.push([element,?name,?observer,?useCapture]);
??????element.addEventListener(name,?observer,?useCapture);
????}? else ? if ?(element.attachEvent)?{
?????? this .observers.push([element,?name,?observer,?useCapture]);
??????element.attachEvent('on'? + ?name,?observer);
????}
??},
?
??unloadCache:? function ()?{
???? if ?( ! Event.observers)? return ;
???? for ?( var ?i? = ? 0 ;?i? < ?Event.observers.length;?i ++ )?{
/*
?*?這里與?Ajax.Request?對象設置?request?header?的代碼異曲同工
? */
??????Event.stopObserving.apply( this ,?Event.observers[i]);
??????Event.observers[i][ 0 ]? = ? null ;
????}
????Event.observers? = ? false ;
??},
/*
?*注冊對象的事件處理,并記錄到cache中
? */
??observe:? function (element,?name,?observer,?useCapture)?{
???? var ?element? = ?$(element);
????useCapture? = ?useCapture? || ? false ;
???
???? if ?(name? == ?'keypress'? &&
????????((navigator.appVersion.indexOf('AppleWebKit')? > ? 0 )
???????? || ?element.attachEvent))
??????name? = ?'keydown';
???
???? this ._observeAndCache(element,?name,?observer,?useCapture);
??},
/*
?*取消對象已注冊的事件處理
? */
??stopObserving:? function (element,?name,?observer,?useCapture)?{
???? var ?element? = ?$(element);
????useCapture? = ?useCapture? || ? false ;
???
???? if ?(name? == ?'keypress'? &&
????????((navigator.appVersion.indexOf('AppleWebKit')? > ? 0 )
???????? || ?element.detachEvent))
??????name? = ?'keydown';
???
???? if ?(element.removeEventListener)?{
??????element.removeEventListener(name,?observer,?useCapture);
????}? else ? if ?(element.detachEvent)?{
??????element.detachEvent('on'? + ?name,?observer);
????}
??}
});
/* ?prevent?memory?leaks?in?IE? */
/*
?*頁面onload?的時候取消所有事件注冊,避免ie內存泄漏的bug
*/
Event.observe(window,?'unload',?Event.unloadCache,? false );
/*
?*?Position?對象也是常用的工具類,提供了獲取元素在頁面上位置的函數,Drag&Drop的效果一定常會用到
?*?具體的應用參考?script.aculo.us?基于prototype?的實現,尤其是dragdrop.js。
? */
var ?Position? = ?{
?? // ?set?to?true?if?needed,?warning:?firefox?performance?problems
?? // ?NOT?neeeded?for?page?scrolling,?only?if?draggable?contained?in
?? // ?scrollable?elements
??includeScrollOffsets:? false ,
?? // ?must?be?called?before?calling?withinIncludingScrolloffset,?every?time?the
?? // ?page?is?scrolled
??prepare:? function ()?{
???? this .deltaX? = ??window.pageXOffset
???????????????? || ?document.documentElement.scrollLeft
???????????????? || ?document.body.scrollLeft
???????????????? || ? 0 ;
???? this .deltaY? = ??window.pageYOffset
???????????????? || ?document.documentElement.scrollTop
???????????????? || ?document.body.scrollTop
???????????????? || ? 0 ;
??},
/*
?*當對象所處的頁面有滾動條是,計算位移
? */
??realOffset:? function (element)?{
???? var ?valueT? = ? 0 ,?valueL? = ? 0 ;
???? do ?{
??????valueT? += ?element.scrollTop?? || ? 0 ;
??????valueL? += ?element.scrollLeft? || ? 0 ;
??????element? = ?element.parentNode;
????}? while ?(element);
???? return ?[valueL,?valueT];
??},
/*
?*計算出對象在頁面上的位置
? */
??cumulativeOffset:? function (element)?{
???? var ?valueT? = ? 0 ,?valueL? = ? 0 ;
???? do ?{
??????valueT? += ?element.offsetTop?? || ? 0 ;
??????valueL? += ?element.offsetLeft? || ? 0 ;
??????element? = ?element.offsetParent;
????}? while ?(element);
???? return ?[valueL,?valueT];
??},
?? // ?caches?x/y?coordinate?pair?to?use?with?overlap
/*
?*判斷一個坐標是否在指定元素的空間范圍中
?*?比如你想判斷鼠標點擊點的坐標是否在某個層或窗口
? */
??within:? function (element,?x,?y)?{
???? if ?( this .includeScrollOffsets)
?????? return ? this .withinIncludingScrolloffsets(element,?x,?y);
???? this .xcomp? = ?x;
???? this .ycomp? = ?y;
???? this .offset? = ? this .cumulativeOffset(element);
???? return ?(y? >= ? this .offset[ 1 ]? &&
????????????y? < ?? this .offset[ 1 ]? + ?element.offsetHeight? &&
????????????x? >= ? this .offset[ 0 ]? &&
????????????x? < ?? this .offset[ 0 ]? + ?element.offsetWidth);
??},
??withinIncludingScrolloffsets:? function (element,?x,?y)?{
???? var ?offsetcache? = ? this .realOffset(element);
???? this .xcomp? = ?x? + ?offsetcache[ 0 ]? - ? this .deltaX;
???? this .ycomp? = ?y? + ?offsetcache[ 1 ]? - ? this .deltaY;
???? this .offset? = ? this .cumulativeOffset(element);
???? return ?( this .ycomp? >= ? this .offset[ 1 ]? &&
???????????? this .ycomp? < ?? this .offset[ 1 ]? + ?element.offsetHeight? &&
???????????? this .xcomp? >= ? this .offset[ 0 ]? &&
???????????? this .xcomp? < ?? this .offset[ 0 ]? + ?element.offsetWidth);
??},
?? // ?within?must?be?called?directly?before
/*
?*調用該方法時,確保首先調用了within方法
?*?如果x,y坐標位于element的空間范圍中,那么返回一個小于1的標示位置的值,比如0.5標示該坐標位于element空間的中線上
? */
??overlap:? function (mode,?element)?{?
???? if ?( ! mode)? return ? 0 ;?
???? if ?(mode? == ?'vertical')
?????? return ?(( this .offset[ 1 ]? + ?element.offsetHeight)? - ? this .ycomp)? /
????????element.offsetHeight;
???? if ?(mode? == ?'horizontal')
?????? return ?(( this .offset[ 0 ]? + ?element.offsetWidth)? - ? this .xcomp)? /
????????element.offsetWidth;
??},
/*
?*復制源對象的空間數據到目的對象。
?*?常用的地方:拖綴一個層到新地方時,常常動態構造和該層同樣大小的虛層。
? */
??clone:? function (source,?target)?{
????source? = ?$(source);
????target? = ?$(target);
????target.style.position? = ?'absolute';
???? var ?offsets? = ? this .cumulativeOffset(source);
????target.style.top???? = ?offsets[ 1 ]? + ?'px';
????target.style.left??? = ?offsets[ 0 ]? + ?'px';
????target.style.width?? = ?source.offsetWidth? + ?'px';
????target.style.height? = ?source.offsetHeight? + ?'px';
??}
}
?*?定義一個全局對象,?屬性?Version?在發布的時候會替換為當前版本號
? */
var ?Prototype? = ?{
??Version:?' 1.3 . 1 ',
?? // ?一個空方法,其后的代碼常會用到,先前的版本該方法被定義于?Ajax?類中。
??emptyFunction:? function ()?{}
}
/*
?*?創建一種類型,注意其屬性?create?是一個方法,返回一個構造函數。
?*?一般使用如下?
?*?????var?X?=?Class.create();??返回一個類型,類似于?java?的一個Class實例。
?*?要使用?X?類型,需繼續用?new?X()來獲取一個實例,如同?java?的?Class.newInstance()方法。
?*
?*?返回的構造函數會執行名為?initialize?的方法,?initialize?是?Ruby?對象的構造器方法名字。
?*?此時initialize方法還沒有定義,其后的代碼中創建新類型時會建立相應的同名方法。
?*
?*?如果一定要從java上去理解。你可以理解為用Class.create()創建一個繼承java.lang.Class類的類。
?*?當然java不允許這樣做,因為Class類是final的
?*
? */
var ?Class? = ?{
??create:? function ()?{
???? return ? function ()?{
?????? this .initialize.apply( this ,?arguments);
????}
??}
}
/*
?*?創建一個對象,從變量名來思考,本意也許是定義一個抽象類,以后創建新對象都?extend?它。
?*?但從其后代碼的應用來看,?Abstract?更多是為了保持命名空間清晰的考慮。
?*?也就是說,我們可以給?Abstract?這個對象實例添加新的對象定義。
?*
?*?從java去理解,就是動態給一個對象創建內部類。
? */
var ?Abstract? = ? new ?Object();
Object.extend? = ? function (destination,?source)?{
?? for ?(property? in ?source)?{
????destination[property]? = ?source[property];
??}
?? return ?destination;
}
/*
?*?獲取參數對象的所有屬性和方法,有點象多重繼承。但是這種繼承是動態獲得的。
?*?如:
?*?????var?a?=?new?ObjectA(),?b?=?new?ObjectB();
?*?????var?c?=?a.extend(b);
?*?此時?c?對象同時擁有?a?和?b?對象的屬性和方法。但是與多重繼承不同的是,c?instanceof?ObjectB?將返回false。
?*
?*?舊版本的該方法定義如下:
?*?Object.prototype.extend?=?function(object)?{
?*?????for?(property?in?object)?{
?*?????????this[property]?=?object[property];
?*?????}
?*?????return?this;
?*?}
?*
?*?新的形式新定義了一個靜態方法?Object.extend,這樣做的目的大概是為了使代碼更為清晰
? */
Object.prototype.extend? = ? function (object)?{
?? return ?Object.extend.apply( this ,?[ this ,?object]);
}
/*
?*?這個方法很有趣,它封裝一個javascript函數對象,返回一個新函數對象,新函數對象的主體和原對象相同,
?*?但是bind()方法參數將被用作當前對象的對象。
?*?也就是說新函數中的?this?引用被改變為參數提供的對象。
?*?比如:
?*?????<input?type="text"?id="aaa"?value="aaa">
?*?????<input?type="text"?id="bbb"?value="bbb">
?*?????





?*?????<script>
?*?????????var?aaa?=?document.getElementById("aaa");
?*?????????var?bbb?=?document.getElementById("bbb");
?*?????????aaa.showValue?=?function()?{alert(this.value);}
?*?????????aaa.showValue2?=?aaa.showValue.bind(bbb);
?*?????</script>
?*?那么,調用aaa.showValue?將返回"aaa",?但調用aaa.showValue2?將返回"bbb"。
?*
?*?apply?是ie5.5后才出現的新方法(Netscape好像很早就支持了)。
?*?該方法更多的資料參考MSDN?http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthApply.asp
?*?閱讀其后的代碼就會發現,bind?被應用的很廣泛,該方法和?Object.prototype.extend?一樣是?Prototype?的核心。
?*?還有一個?call?方法,應用起來和?apply?類似。可以一起研究下。
? */
Function.prototype.bind? = ? function (object)?{
?? var ?__method? = ? this ;
?? return ? function ()?{
????__method.apply(object,?arguments);
??}
}
/*
?*?和bind一樣,不過這個方法一般用做html控件對象的事件處理。所以要傳遞event對象
?*?注意這時候,用到了?Function.call。它與?Function.apply?的不同好像僅僅是對參數形式的定義。
?*?如同?java?兩個過載的方法。
? */
Function.prototype.bindAsEventListener? = ? function (object)?{
?? var ?__method? = ? this ;
?? return ? function (event)?{
????__method.call(object,?event? || ?window.event);
??}
}
/*
?*?將整數形式RGB顏色值轉換為HEX形式
? */
Number.prototype.toColorPart? = ? function ()?{
?? var ?digits? = ? this .toString( 16 );
?? if ?( this ? < ? 16 )? return ?' 0 '? + ?digits;
?? return ?digits;
}
/*
?*?典型?Ruby?風格的函數,將參數中的方法逐個調用,返回第一個成功執行的方法的返回值
? */
var ?Try? = ?{
??these:? function ()?{
???? var ?returnValue;
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
?????? var ?lambda? = ?arguments[i];
?????? try ?{
????????returnValue? = ?lambda();
???????? break ;
??????}? catch ?(e)?{}
????}
???? return ?returnValue;
??}
}
/* ======================================================= */
/*
?*?一個設計精巧的定時執行器
?*?首先由?Class.create()?創建一個?PeriodicalExecuter?類型,
?*?然后用對象直接量的語法形式設置原型。
?*
?*?需要特別說明的是?rgisterCallback?方法,它調用上面定義的函數原型方法bind,?并傳遞自己為參數。
?*?之所以這樣做,是因為?setTimeout?默認總以?window?對象為當前對象,也就是說,如果?registerCallback?方法定義如下的話:
?*?????registerCallback:?function()?{
?*?????????setTimeout(this.onTimerEvent,?this.frequency?*?1000);
?*?????}
?*?那么,this.onTimeoutEvent?方法執行失敗,因為它無法訪問?this.currentlyExecuting?屬性。
?*?而使用了bind以后,該方法才能正確的找到this,也就是PeriodicalExecuter的當前實例。
? */
var ?PeriodicalExecuter? = ?Class.create();
PeriodicalExecuter.prototype? = ?{
??initialize:? function (callback,?frequency)?{
???? this .callback? = ?callback;
???? this .frequency? = ?frequency;
???? this .currentlyExecuting? = ? false ;
???? this .registerCallback();
??},
??registerCallback:? function ()?{
????setInterval( this .onTimerEvent.bind( this ),? this .frequency? * ? 1000 );
??},
??onTimerEvent:? function ()?{
???? if ?( ! this .currentlyExecuting)?{
?????? try ?{
???????? this .currentlyExecuting? = ? true ;
???????? this .callback();
??????}? finally ?{
???????? this .currentlyExecuting? = ? false ;
??????}
????}
??}
}
/* ======================================================= */
/*
?*?這個函數就?Ruby?了。我覺得它的作用主要有兩個
?*?1.??大概是?document.getElementById(id)?的最簡化調用。
?*?比如:$("aaa")?將返回?aaa?對象
?*?2.??得到對象數組
?*?比如:?$("aaa","bbb")?返回一個包括id為"aaa"和"bbb"兩個input控件對象的數組。
? */
function ?$()?{
?? var ?elements? = ? new ?Array();
?? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
???? var ?element? = ?arguments[i];
???? if ?( typeof ?element? == ?'string')
??????element? = ?document.getElementById(element);
???? if ?(arguments.length? == ? 1 )
?????? return ?element;
????elements.push(element);
??}
?? return ?elements;
}
/*
?*?為兼容舊版本的瀏覽器增加?Array?的?push?方法。
? */
if ?( ! Array.prototype.push)?{
??Array.prototype.push? = ? function ()?{
?????? var ?startLength? = ? this .length;
?????? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )
?????? this [startLength? + ?i]? = ?arguments[i];
????? return ? this .length;
??}
}
/*
?*?為兼容舊版本的瀏覽器增加?Function?的?apply?方法。
? */
if ?( ! Function.prototype.apply)?{
?? // ?Based?on?code?from?http://www.youngpup.net/
??Function.prototype.apply? = ? function (object,?parameters)?{
???? var ?parameterStrings? = ? new ?Array();
???? if ?( ! object)?????object? = ?window;
???? if ?( ! parameters)?parameters? = ? new ?Array();
???
???? for ?( var ?i? = ? 0 ;?i? < ?parameters.length;?i ++ )
??????parameterStrings[i]? = ?'parameters['? + ?i? + ?']';
???
????object.__apply__? = ? this ;
???? var ?result? = ?eval('object.__apply__('? +
??????parameterStrings.join(',?')? + ?')');
????object.__apply__? = ? null ;
???
???? return ?result;
??}
}
/*
?*?擴展?javascript?內置的?String?對象
? */
String.prototype.extend({
/*
?*去掉字符串中的<html>標簽
? */
??stripTags:? function ()?{
???? return ? this .replace( /< \ /? [ ^> ] +>/ gi,?'');
??},
/*
?*這個方法很常見,通常的實現都是用正則表達式替換特殊字符為html規范定義的命名實體或者十進制編碼,比如:
?*?string.replace(/&/g,?"&").replace(/</g,?"<").replace(/>/g,?">");
?*?而這里的實現借用瀏覽器自身的內部替換,確實巧妙。
? */
??escapeHTML:? function ()?{
???? var ?div? = ?document.createElement('div');
???? var ?text? = ?document.createTextNode( this );
????div.appendChild(text);
???? return ?div.innerHTML;
??},
?
/*
?*同上
? */
??unescapeHTML:? function ()?{
???? var ?div? = ?document.createElement('div');
????div.innerHTML? = ? this .stripTags();
???? return ?div.childNodes[ 0 ].nodeValue;
??}
});
/*
?*?定義?Ajax?對象,?靜態方法?getTransport?方法返回一個?XMLHttp?對象
? */
var ?Ajax? = ?{
??getTransport:? function ()?{
???? return ?Try.these(
?????? function ()?{ return ? new ?ActiveXObject('Msxml2.XMLHTTP')},
?????? function ()?{ return ? new ?ActiveXObject('Microsoft.XMLHTTP')},
?????? function ()?{ return ? new ?XMLHttpRequest()}
????)? || ? false ;
??}
}
/*
?*?我以為此時的Ajax對象起到命名空間的作用。
?*?Ajax.Base?聲明為一個基礎對象類型
?*?注意?Ajax.Base?并沒有使用?Class.create()?的方式來創建,我想是因為作者并不希望?Ajax.Base?被庫使用者實例化。
?*?作者在其他對象類型的聲明中,將會繼承于它。
?*?就好像?java?中的私有抽象類
? */
Ajax.Base? = ? function ()?{};
Ajax.Base.prototype? = ?{
/*
?*extend?(見上)?的用法真是讓人耳目一新
?*?options?首先設置默認屬性,然后再?extend?參數對象,那么參數對象中也有同名的屬性,那么就覆蓋默認屬性值。
?*?想想如果我寫這樣的實現,應該類似如下:
?????setOptions:?function(options)?{
??????this.options.methed?=?options.methed??options.methed?:?'post';
??????



?????}
?????我想很多時候,java?限制了?js?的創意。
? */
??setOptions:? function (options)?{
???? this .options? = ?{
??????method:???????'post',
??????asynchronous:? true ,
??????parameters:???''
????}.extend(options? || ?{});
??},
?
/*
?*如果 xmlhttp?調用返回正確的HTTP狀態值,函數返回ture, 反之false。
?*?xmlhttp 的?readyState?屬性不足以準確判斷?xmlhttp 遠程調用成功,該方法是readyState判斷的一個前提條件
? */
??responseIsSuccess:? function ()?{
???? return ? this .transport.status? == ?undefined
???????? || ? this .transport.status? == ? 0
???????? || ?( this .transport.status? >= ? 200 ? && ? this .transport.status? < ? 300 );
??},
?
/*
?*如果 xmlhttp?調用返回錯誤的HTTP狀態值,函數返回ture, 反之false。
? */
??responseIsFailure:? function ()?{
???? return ? ! this .responseIsSuccess();
??}
}
/*
?*?Ajax.Request?封裝?XmlHttp
? */
Ajax.Request? = ?Class.create();
/*
?*?定義四種事件(狀態),?參考http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp
? */
Ajax.Request.Events? =
??['Uninitialized',?'Loading',?'Loaded',?'Interactive',?'Complete'];
/*
?* 相比先前的版本,對于 xmlhttp?的調用和返回值處理分離得更為清晰
? */
Ajax.Request.prototype? = ?( new ?Ajax.Base()).extend({
??initialize:? function (url,?options)?{
???? this .transport? = ?Ajax.getTransport();
???? this .setOptions(options);
???? this .request(url);
??},
/*
?*新增加?request?方法封裝?xmlhttp 的調用過程。
? */
??request:? function (url)?{
???? var ?parameters? = ? this .options.parameters? || ?'';
???? if ?(parameters.length? > ? 0 )?parameters? += ?' & _ = ';
???? try ?{
?????? if ?( this .options.method? == ?'get')
????????url? += ?' ? '? + ?parameters;
?????? this .transport.open( this .options.method,?url,
???????? this .options.asynchronous);
?????? if ?( this .options.asynchronous)?{
???????? this .transport.onreadystatechange? = ? this .onStateChange.bind( this );
????????setTimeout(( function ()?{ this .respondToReadyState( 1 )}).bind( this ),? 10 );
??????}
?????? this .setRequestHeaders();
?????? var ?body? = ? this .options.postBody? ? ? this .options.postBody?:?parameters;
?????? this .transport.send( this .options.method? == ?'post'? ? ?body?:? null );
????}? catch ?(e)?{
????}
??},
/*
?*新增加的?setRequestHeaders?方法允許添加自定義的http?header
? */
??setRequestHeaders:? function ()?{
???? var ?requestHeaders? =
??????['X - Requested - With',?'XMLHttpRequest',
???????'X - Prototype - Version',?Prototype.Version];
???? if ?( this .options.method? == ?'post')?{
??????requestHeaders.push('Content - type',
????????'application / x - www - form - urlencoded');
/* ?Force?"Connection:?close"?for?Mozilla?browsers?to?work?around
?*?a?bug?where?XMLHttpReqeuest?sends?an?incorrect?Content-length
?*?header.?See?Mozilla?Bugzilla?#246651.
? */
?????? if ?( this .transport.overrideMimeType)
????????requestHeaders.push('Connection',?'close');
????}
/*
?*?其后的?apply?方法的調用有些奇技淫巧的意味
?*?從上下文中我們可以分析出?this.options.requestHeaders?是調用者自定義的http?header數組。
?*?requestHeaders?也是一個數組,將一個數組中的元素逐個添加到另一個元素中,直接調用
?*?requestHeaders.push(this.options.requestHeaders)
?*?是不行的,因為該調用導致?this.options.requestHeaders?整個數組作為一個元素添加到?requestHeaders中。
?*?javascript的Array對象還提供一個concat?的方法表面上滿足要求,但是concat實際上是創建一個新數組,將兩個數組的元素添加到新數組中。
?*?所以,下面的代碼也可以替換為
?*?requestHeaders?=?requestHeaders.concat(this.options.requestHeaders);
?*?很顯然,作者不喜歡這樣的代碼方式
?*?而?apply?方法的語法?apply([thisObj[,argArray]])?本身就要求第二個參數是一個數組或者arguments對象。
?*?所以巧妙的實現了?concat?函數的作用。
?*?令人拍案叫絕啊!
? */
???? if ?( this .options.requestHeaders)
??????requestHeaders.push.apply(requestHeaders,? this .options.requestHeaders);
???? for ?( var ?i? = ? 0 ;?i? < ?requestHeaders.length;?i? += ? 2 )
?????? this .transport.setRequestHeader(requestHeaders[i],?requestHeaders[i + 1 ]);
??},
?
??onStateChange:? function ()?{
???? var ?readyState? = ? this .transport.readyState;
/*
?*?如果不是?Loading?狀態,就調用回調函數
? */
???? if ?(readyState? != ? 1 )
?????? this .respondToReadyState( this .transport.readyState);
??},
/*
?*回調函數定義在?this.options?屬性中,比如:
??????var?option?=?{
?????????onLoaded?:?function(req)?{

?????????


??????}
??????new?Ajax.Request(url,?option);
? */
??respondToReadyState:? function (readyState)?{
???? var ?event? = ?Ajax.Request.Events[readyState];
/*
?*?新增的回調函數處理,調用者還可以在options中定義?on200,?onSuccess?這樣的回調函數
?*?在?readyState?為完成狀態的時候調用
? */
???? if ?(event? == ?'Complete')
??????( this .options['on'? + ? this .transport.status]
??????? || ? this .options['on'? + ?( this .responseIsSuccess()? ? ?'Success'?:?'Failure')]
??????? || ?Prototype.emptyFunction)( this .transport);
????( this .options['on'? + ?event]? || ?Prototype.emptyFunction)( this .transport);
/* ?Avoid?memory?leak?in?MSIE:?clean?up?the?oncomplete?event?handler? */
???? if ?(event? == ?'Complete')
?????? this .transport.onreadystatechange? = ?Prototype.emptyFunction;
??}
});
/*
?*?Ajax.Updater?用于綁定一個html元素與?XmlHttp調用的返回值。類似與?buffalo?的?bind。
?*?如果?options?中有?insertion(見后)?對象的話,?insertion?能提供更多的插入控制。
? */
Ajax.Updater? = ?Class.create();
Ajax.Updater.ScriptFragment? = ?'( ? : < script. *?> )((\n | .) *? )( ? : < \ / script > )';
Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
??initialize:? function (container,?url,?options)?{
/*
?*?containers?就是被綁定的?html?對象,xmlhttp的返回值被賦給該對象的?innerHTML?屬性。
?*?相比新版本,containers?根據container參數定義?success?和?failure?引用,如果它們被定義的話,根據xmlhttp調用是否成功來選擇
?*?更新對象,假想調用可能如下:
?*?var?c?=?{success:?$("successDiv"),?failure:?$("failureDiv")};
?*?new?Ajax.Updater(c,?url,?options);
?*?那么調用成功則?successDiv?顯示成功信息或者數據,反之?failureDiv?顯示錯誤信息
? */
???? this .containers? = ?{
??????success:?container.success? ? ?$(container.success)?:?$(container),
??????failure:?container.failure? ? ?$(container.failure)?:
????????(container.success? ? ? null ?:?$(container))
????}
???? this .transport? = ?Ajax.getTransport();
???? this .setOptions(options);
???? var ?onComplete? = ? this .options.onComplete? || ?Prototype.emptyFunction;
???? this .options.onComplete? = ?( function ()?{
?????? this .updateContent();
??????onComplete( this .transport);
????}).bind( this );
???? this .request(url);
??},
??updateContent:? function ()?{
???? var ?receiver? = ? this .responseIsSuccess()? ?
?????? this .containers.success?:? this .containers.failure;
???? var ?match???? = ? new ?RegExp(Ajax.Updater.ScriptFragment,?'img');
???? var ?response? = ? this .transport.responseText.replace(match,?'');
???? var ?scripts?? = ? this .transport.responseText.match(match);
???? if ?(receiver)?{
?????? if ?( this .options.insertion)?{
???????? new ? this .options.insertion(receiver,?response);
??????}? else ?{
????????receiver.innerHTML? = ?response;
??????}
????}
???? if ?( this .responseIsSuccess())?{
?????? if ?( this .onComplete)
????????setTimeout(( function ()?{ this .onComplete(
?????????? this .transport)}).bind( this ),? 10 );
????}
/*
?*?如果調用者在傳入的options參數中定義?evalScripts=true,同時xmlhttp返回值的html中包含<script>標簽的話,執行該腳本
? */
???? if ?( this .options.evalScripts? && ?scripts)?{
/*
?*?注意前二十行左右還有一個?match?的聲明
?*?var?match????=?new?RegExp(Ajax.Updater.ScriptFragment,?'img');
?*?和此處的區別就是,正則表達式匹配標記多一個?"g"。
?*?多個g,?所以?scripts?是一個數組,數組中每個元素是一段?<script>

?*?沒有g,?scripts[i].match(match)[1]?匹配的就是?<script>標記中的?script?代碼。
?*?關于正則表達式,請參考javascript的相關資料。
? */
??????match? = ? new ?RegExp(Ajax.Updater.ScriptFragment,?'im');
??????setTimeout(( function ()?{
???????? for ?( var ?i? = ? 0 ;?i? < ?scripts.length;?i ++ )
??????????eval(scripts[i].match(match)[ 1 ]);
??????}).bind( this ),? 10 );
????}
??}
});
/*
?*?定期更新器
? */
Ajax.PeriodicalUpdater? = ?Class.create();
Ajax.PeriodicalUpdater.prototype? = ?( new ?Ajax.Base()).extend({
??initialize:? function (container,?url,?options)?{
???? this .setOptions(options);
???? this .onComplete? = ? this .options.onComplete;
???? this .frequency? = ?( this .options.frequency? || ? 2 );
??? // ?decay?可能是一個時間調整因素
???? this .decay? = ? 1 ;
???? this .updater? = ?{};
???? this .container? = ?container;
???? this .url? = ?url;
???? this .start();
??},
??start:? function ()?{
???? this .options.onComplete? = ? this .updateComplete.bind( this );
???? this .onTimerEvent();
??},
??stop:? function ()?{
???? this .updater.onComplete? = ?undefined;
????clearTimeout( this .timer);
????( this .onComplete? || ?Ajax.emptyFunction).apply( this ,?arguments);
??},
??updateComplete:? function (request)?{
???? if ?( this .options.decay)?{
?????? this .decay? = ?(request.responseText? == ? this .lastText? ?
???????? this .decay? * ? this .options.decay?:? 1 );
?????? this .lastText? = ?request.responseText;
????}
???? this .timer? = ?setTimeout( this .onTimerEvent.bind( this ),
?????? this .decay? * ? this .frequency? * ? 1000 );
??},
??onTimerEvent:? function ()?{
???? this .updater? = ? new ?Ajax.Updater( this .container,? this .url,? this .options);
??}
});
/*
?*?根據?class?attribute?的名字得到對象數組,支持?multiple?class
?*
? */
document.getElementsByClassName? = ? function (className)?{
?? var ?children? = ?document.getElementsByTagName(' * ')? || ?document.all;
?? var ?elements? = ? new ?Array();
?
?? for ?( var ?i? = ? 0 ;?i? < ?children.length;?i ++ )?{
???? var ?child? = ?children[i];
???? var ?classNames? = ?child.className.split('?');
???? for ?( var ?j? = ? 0 ;?j? < ?classNames.length;?j ++ )?{
?????? if ?(classNames[j]? == ?className)?{
????????elements.push(child);
???????? break ;
??????}
????}
??}
?
?? return ?elements;
}
/* ======================================================= */
/*
?*?Element?就象一個?java?的工具類,主要用來?隱藏/顯示/銷除?對象,以及獲取對象的簡單屬性。
?*
? */
if ?( ! window.Element)?{
?? var ?Element? = ? new ?Object();
}
Object.extend(Element,?{
/*
?*切換?顯示/隱藏
? */
??toggle:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
?????? var ?element? = ?$(arguments[i]);
??????element.style.display? =
????????(element.style.display? == ?'none'? ? ?''?:?'none');
????}
??},
??hide:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
?????? var ?element? = ?$(arguments[i]);
??????element.style.display? = ?'none';
????}
??},
??show:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )?{
?????? var ?element? = ?$(arguments[i]);
??????element.style.display? = ?'';
????}
??},
/*
?*從父節點中移除
? */
??remove:? function (element)?{
????element? = ?$(element);
????element.parentNode.removeChild(element);
??},
???
??getHeight:? function (element)?{
????element? = ?$(element);
???? return ?element.offsetHeight;
??},
/*
?*是否擁有?class?屬性值
? */
??hasClassName:? function (element,?className)?{
????element? = ?$(element);
???? if ?( ! element)
?????? return ;
???? var ?a? = ?element.className.split('?');
???? for ?( var ?i? = ? 0 ;?i? < ?a.length;?i ++ )?{
?????? if ?(a[i]? == ?className)
???????? return ? true ;
????}
???? return ? false ;
??},
/*
?*為對象添加?class?屬性值
? */
??addClassName:? function (element,?className)?{
????element? = ?$(element);
????Element.removeClassName(element,?className);
????element.className? += ?'?'? + ?className;
??},
/*
?*為對象移除?class?屬性值
? */
??removeClassName:? function (element,?className)?{
????element? = ?$(element);
???? if ?( ! element)
?????? return ;
???? var ?newClassName? = ?'';
???? var ?a? = ?element.className.split('?');
???? for ?( var ?i? = ? 0 ;?i? < ?a.length;?i ++ )?{
?????? if ?(a[i]? != ?className)?{
???????? if ?(i? > ? 0 )
??????????newClassName? += ?'?';
????????newClassName? += ?a[i];
??????}
????}
????element.className? = ?newClassName;
??},
?
?? // ?removes?whitespace-only?text?node?children
??cleanWhitespace:? function (element)?{
???? var ?element? = ?$(element);
???? for ?( var ?i? = ? 0 ;?i? < ?element.childNodes.length;?i ++ )?{
?????? var ?node? = ?element.childNodes[i];
?????? if ?(node.nodeType? == ? 3 ? && ? !/ \S / .test(node.nodeValue))
????????Element.remove(node);
????}
??}
});
/*
?*?為?Element.toggle?做了一個符號連接,大概是兼容性的考慮
? */
var ?Toggle? = ? new ?Object();
Toggle.display? = ?Element.toggle;
/* ======================================================= */
/*
?*?動態插入內容的實現,MS的Jscript實現中對象有一個?insertAdjacentHTML?方法
?*?(http:?//msdn.microsoft.com/workshop/author/dhtml/reference/methods/insertadjacenthtml.asp)
?*?這里算是一個對象形式的封裝。
? */
Abstract.Insertion? = ? function (adjacency)?{
?? this .adjacency? = ?adjacency;
}
Abstract.Insertion.prototype? = ?{
??initialize:? function (element,?content)?{
???? this .element? = ?$(element);
???? this .content? = ?content;
???
???? if ?( this .adjacency? && ? this .element.insertAdjacentHTML)?{
?????? this .element.insertAdjacentHTML( this .adjacency,? this .content);
????}? else ?{
/*
?*?gecko?不支持?insertAdjacentHTML?方法,但可以用如下代碼代替
? */
?????? this .range? = ? this .element.ownerDocument.createRange();
/*
?*?如果定義了?initializeRange?方法,則實行,這里相當與定義了一個抽象的?initializeRange?方法
? */
?????? if ?( this .initializeRange)? this .initializeRange();
?????? this .fragment? = ? this .range.createContextualFragment( this .content);
/*
?*?insertContent?也是一個抽象方法,子類必須實現
? */
?????? this .insertContent();
????}
??}
}
/*
?*?prototype?加深了我的體會,就是寫js?如何去遵循 Don’t?Repeat?Yourself?(DRY)?原則
?*?上文中?Abstract.Insertion?算是一個抽象類,定義了名為 initializeRange?的一個抽象方法
?*?var?Insertion?=?new?Object() 建立一個命名空間
?*?Insertion.Before|Top|Bottom|After?就象是四個java中的四個靜態內部類,而它們分別繼承于Abstract.Insertion,并實現了initializeRange方法。
? */
var ?Insertion? = ? new ?Object();
/*
?*將內容插入到指定節點的前面,?與指定節點同級
*/
Insertion.Before? = ?Class.create();
Insertion.Before.prototype? = ?( new ?Abstract.Insertion('beforeBegin')).extend({
??initializeRange:? function ()?{
???? this .range.setStartBefore( this .element);
??},
?
??insertContent:? function ()?{
???? this .element.parentNode.insertBefore( this .fragment,? this .element);
??}
});
/*
?*將內容插入到指定節點的第一個子節點前,于是內容變為該節點的第一個子節點
*/
Insertion.Top? = ?Class.create();
Insertion.Top.prototype? = ?( new ?Abstract.Insertion('afterBegin')).extend({
??initializeRange:? function ()?{
???? this .range.selectNodeContents( this .element);
???? this .range.collapse( true );
??},
?
??insertContent:? function ()?{?
???? this .element.insertBefore( this .fragment,? this .element.firstChild);
??}
});
/*
?*將內容插入到指定節點的最后,于是內容變為該節點的最后一個子節點
*/
Insertion.Bottom? = ?Class.create();
Insertion.Bottom.prototype? = ?( new ?Abstract.Insertion('beforeEnd')).extend({
??initializeRange:? function ()?{
???? this .range.selectNodeContents( this .element);
???? this .range.collapse( this .element);
??},
?
??insertContent:? function ()?{
???? this .element.appendChild( this .fragment);
??}
});
/*
?*將內容插入到指定節點的后面,?與指定節點同級
*/
Insertion.After? = ?Class.create();
Insertion.After.prototype? = ?( new ?Abstract.Insertion('afterEnd')).extend({
??initializeRange:? function ()?{
???? this .range.setStartAfter( this .element);
??},
?
??insertContent:? function ()?{
???? this .element.parentNode.insertBefore( this .fragment,
?????? this .element.nextSibling);
??}
});
/*
?*?針對?頁面元素對象(一般都是表單控件)的工具類,提供一些簡單靜態方法
?*?這些方法顯然常用在表單處理中
?*?注意?Field?這種聲明方式類似于?java?聲明一個靜態的?singleton?工具類
?*?等同于?:
?*???var?Field?=?new?Object();
?*???Field.extend({

?*
?*?后文中的?Form,?Event,?Position?對象聲明方式如出一轍
? */
var ?Field? = ?{
/*
?*清除參數引用的對象的值
? */
??clear:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )
??????$(arguments[i]).value? = ?'';
??},
/*
?*使參數引用對象獲取焦點
? */
??focus:? function (element)?{
????$(element).focus();
??},
?
/*
?*判斷參數引用對象是否有非空值,如為空值,返回false,?反之true
? */
??present:? function ()?{
???? for ?( var ?i? = ? 0 ;?i? < ?arguments.length;?i ++ )
?????? if ?($(arguments[i]).value? == ?'')? return ? false ;
???? return ? true ;
??},
?
/*
?*使選中參數引用對象
? */
??select:? function (element)?{
????$(element).select();
??},
???
/*
?*使參數引用對象處于可編輯狀態
? */
??activate:? function (element)?{
????$(element).focus();
????$(element).select();
??}
}
/* ======================================================= */
/*
?*?表單工具類
? */
var ?Form? = ?{
/*
?*將表單元素序列化后的值(其實就是?name=value?形式的名值配對)組合成?QueryString?的形式
? */
??serialize:? function (form)?{
???? var ?elements? = ?Form.getElements($(form));
???? var ?queryComponents? = ? new ?Array();
???
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )?{
?????? var ?queryComponent? = ?Form.Element.serialize(elements[i]);
?????? if ?(queryComponent)
????????queryComponents.push(queryComponent);
????}
???
???? return ?queryComponents.join(' & ');
??},
?
/*
?*得到表單的所有元素對象
? */
??getElements:? function (form)?{
???? var ?form? = ?$(form);
???? var ?elements? = ? new ?Array();
???? for ?(tagName? in ?Form.Element.Serializers)?{
?????? var ?tagElements? = ?form.getElementsByTagName(tagName);
?????? for ?( var ?j? = ? 0 ;?j? < ?tagElements.length;?j ++ )
????????elements.push(tagElements[j]);
????}
???? return ?elements;
??},
?
/*
?*根據?type?和?name?過濾得到表單中符合的?<input>?對象
? */
??getInputs:? function (form,?typeName,?name)?{
???? var ?form? = ?$(form);
???? var ?inputs? = ?form.getElementsByTagName('input');
???
???? if ?( ! typeName? && ? ! name)
?????? return ?inputs;
?????
???? var ?matchingInputs? = ? new ?Array();
???? for ?( var ?i? = ? 0 ;?i? < ?inputs.length;?i ++ )?{
?????? var ?input? = ?inputs[i];
?????? if ?((typeName? && ?input.type? != ?typeName)? ||
??????????(name? && ?input.name? != ?name))
???????? continue ;
??????matchingInputs.push(input);
????}
???? return ?matchingInputs;
??},
/*
?*將指定表單的元素置于不可用狀態
? */ ?
??disable:? function (form)?{
???? var ?elements? = ?Form.getElements(form);
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )?{
?????? var ?element? = ?elements[i];
??????element.blur();
??????element.disabled? = ?' true ';
????}
??},
/*
?*將指定表單的元素置于可用狀態
? */
??enable:? function (form)?{
???? var ?elements? = ?Form.getElements(form);
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )?{
?????? var ?element? = ?elements[i];
??????element.disabled? = ?'';
????}
??},
/*
?*使表單的第一個非?hidden?類型而且處于可用狀態的元素獲得焦點
? */
??focusFirstElement:? function (form)?{
???? var ?form? = ?$(form);
???? var ?elements? = ?Form.getElements(form);
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )?{
?????? var ?element? = ?elements[i];
?????? if ?(element.type? != ?'hidden'? && ? ! element.disabled)?{
????????Field.activate(element);
???????? break ;
??????}
????}
??},
?? /*
?*?重置表單
? */
??reset:? function (form)?{
????$(form).reset();
??}
}
/*
?*?表單元素工具類
? */
Form.Element? = ?{
/*
?*返回表單元素的值先序列化,?其實就是?name=value?形式的名值配對
? */
??serialize:? function (element)?{
???? var ?element? = ?$(element);
???? var ?method? = ?element.tagName.toLowerCase();
???? var ?parameter? = ?Form.Element.Serializers[method](element);
???
???? if ?(parameter)
?????? return ?encodeURIComponent(parameter[ 0 ])? + ?' = '? +
????????encodeURIComponent(parameter[ 1 ]);???????????????????
??},
?
/*
?*?返回表單元素的值
? */
??getValue:? function (element)?{
???? var ?element? = ?$(element);
???? var ?method? = ?element.tagName.toLowerCase();
???? var ?parameter? = ?Form.Element.Serializers[method](element);
???
???? if ?(parameter)
?????? return ?parameter[ 1 ];
??}
}
/*
?*?prototype?的所謂序列化其實就是將表單的名字和值組合成一個數組
? */
Form.Element.Serializers? = ?{
??input:? function (element)?{
???? switch ?(element.type.toLowerCase())?{
?????? case ?'submit':
?????? case ?'hidden':
?????? case ?'password':
?????? case ?'text':
???????? return ?Form.Element.Serializers.textarea(element);
?????? case ?'checkbox':?
?????? case ?'radio':
???????? return ?Form.Element.Serializers.inputSelector(element);
????}
???? return ? false ;
??},
/*
?*單/多選框?由此方法處理序列化
? */
??inputSelector:? function (element)?{
???? if ?(element.checked)
?????? return ?[element.name,?element.value];
??},
/*
?*textarea?由此方法處理序列化
? */
??textarea:? function (element)?{
???? return ?[element.name,?element.value];
??},
/*
?*select?下拉列表由此方法處理序列化
? */
??select:? function (element)?{
???? var ?value? = ?'';
???? if ?(element.type? == ?'select - one')?{
?????? var ?index? = ?element.selectedIndex;
?????? if ?(index? >= ? 0 )
????????value? = ?element.options[index].value? || ?element.options[index].text;
????}? else ?{
/*
?*?支持?select-mulitple?的下拉列表,返回的數組的第二個元素,是一個值數組
? */
??????value? = ? new ?Array();
?????? for ?( var ?i? = ? 0 ;?i? < ?element.length;?i ++ )?{
???????? var ?opt? = ?element.options[i];
???????? if ?(opt.selected)
??????????value.push(opt.value? || ?opt.text);
??????}
????}
???? return ?[element.name,?value];
??}
}
/* ======================================================= */
/*
?*?Form.Element.getValue?會經常用到,所以做了一個快捷引用
?*?取得某個表單控件的值,可以簡化調用為?$F("username"),真是方便啊
? */
var ?$F? = ?Form.Element.getValue;
/* ======================================================= */
/*
?*?Abstract.TimedObserver?也沒有用?Class.create()?來創建,和Ajax.Base?意圖應該一樣
?*?Abstract.TimedObserver?顧名思義,是套用Observer設計模式來跟蹤指定表單元素,
?*?當表單元素的值發生變化的時候,就執行回調函數
?*
?*?我想 Observer?與注冊onchange事件相似,不同點在于?onchange?事件是在元素失去焦點的時候才激發。
?*?同樣的與?onpropertychange?事件也相似,不過它只關注表單元素的值的變化,而且提供timeout的控制。
?*
?*?除此之外,Observer?的好處大概就在與更面向對象,另外可以動態的更換回調函數,這就比注冊事件要靈活一些。
?*?Observer?應該可以勝任動態數據校驗,或者多個關聯下拉選項列表的連動等等
?*
? */
Abstract.TimedObserver? = ? function ()?{}
/*
?*?這個設計和?PeriodicalExecuter?一樣,bind?方法是實現的核心
? */
Abstract.TimedObserver.prototype? = ?{
??initialize:? function (element,?frequency,?callback)?{
???? this .frequency? = ?frequency;
???? this .element??? = ?$(element);
???? this .callback?? = ?callback;
???
???? this .lastValue? = ? this .getValue();
???? this .registerCallback();
??},
?
??registerCallback:? function ()?{
????setInterval( this .onTimerEvent.bind( this ),? this .frequency? * ? 1000 );
??},
?
??onTimerEvent:? function ()?{
???? var ?value? = ? this .getValue();
???? if ?( this .lastValue? != ?value)?{
?????? this .callback( this .element,?value);
?????? this .lastValue? = ?value;
????}
??}
}
/*
?*?Form.Element.Observer?監視指定表單域的值是否變化
? */
Form.Element.Observer? = ?Class.create();
Form.Element.Observer.prototype? = ?( new ?Abstract.TimedObserver()).extend({
??getValue:? function ()?{
???? return ?Form.Element.getValue( this .element);
??}
});
/*
?*?Form.Element.Observer?監視指定表單所有控件的值是否有變化
? */
Form.Observer? = ?Class.create();
Form.Observer.prototype? = ?( new ?Abstract.TimedObserver()).extend({
??getValue:? function ()?{
???? return ?Form.serialize( this .element);
??}
});
/* ======================================================= */
/*
?*?EventObserver?相比上面的?TimedObserver,是更具主動性的一種監測
?*?它直接為表單控件(根據?type?的不同)?注冊相應的事件處理,?只要發現某個控件值發生改變,就執行回調函數
? */
Abstract.EventObserver? = ? function ()?{}
Abstract.EventObserver.prototype? = ?{
??initialize:? function (element,?callback)?{
???? this .element?? = ?$(element);
???? this .callback? = ?callback;
???
???? this .lastValue? = ? this .getValue();
???? if ?( this .element.tagName.toLowerCase()? == ?'form')
?????? this .registerFormCallbacks();
???? else
?????? this .registerCallback( this .element);
??},
?
??onElementEvent:? function ()?{
???? var ?value? = ? this .getValue();
???? if ?( this .lastValue? != ?value)?{
?????? this .callback( this .element,?value);
?????? this .lastValue? = ?value;
????}
??},
?
??registerFormCallbacks:? function ()?{
???? var ?elements? = ?Form.getElements( this .element);
???? for ?( var ?i? = ? 0 ;?i? < ?elements.length;?i ++ )
?????? this .registerCallback(elements[i]);
??},
?
??registerCallback:? function (element)?{
???? if ?(element.type)?{
?????? switch ?(element.type.toLowerCase())?{
/*
?*?checkbox?和?radio?類型的控件注冊?onclick?事件處理
? */
???????? case ?'checkbox':?
???????? case ?'radio':
??????????element.target? = ? this ;
??????????element.prev_onclick? = ?element.onclick? || ?Prototype.emptyFunction;
/*
?*?相信這里有改進的空間,應該使用其后的?Event對象提供的注冊管理功能來統一注冊
? */
??????????element.onclick? = ? function ()?{
???????????? this .prev_onclick();
???????????? this .target.onElementEvent();
??????????}
?????????? break ;
/*
?*?其他類型的控件注冊?onchange?事件處理
? */
???????? case ?'password':
???????? case ?'text':
???????? case ?'textarea':
???????? case ?'select - one':
???????? case ?'select - multiple':
??????????element.target? = ? this ;
??????????element.prev_onchange? = ?element.onchange? || ?Prototype.emptyFunction;
??????????element.onchange? = ? function ()?{
???????????? this .prev_onchange();
???????????? this .target.onElementEvent();
??????????}
?????????? break ;
??????}
????}???
??}
}
/*
?*?監視指定表單控件
? */
Form.Element.EventObserver? = ?Class.create();
Form.Element.EventObserver.prototype? = ?( new ?Abstract.EventObserver()).extend({
??getValue:? function ()?{
???? return ?Form.Element.getValue( this .element);
??}
});
/*
?*?監視指定表單所有控件
? */
Form.EventObserver? = ?Class.create();
Form.EventObserver.prototype? = ?( new ?Abstract.EventObserver()).extend({
??getValue:? function ()?{
???? return ?Form.serialize( this .element);
??}
});
/*
?*?封裝事件處理的靜態工具對象
? */
if ?( ! window.Event)?{
?? var ?Event? = ? new ?Object();
}
Object.extend(Event,?{
??KEY_BACKSPACE:? 8 ,
??KEY_TAB:??????? 9 ,
??KEY_RETURN:??? 13 ,
??KEY_ESC:?????? 27 ,
??KEY_LEFT:????? 37 ,
??KEY_UP:??????? 38 ,
??KEY_RIGHT:???? 39 ,
??KEY_DOWN:????? 40 ,
??KEY_DELETE:??? 46 ,
??element:? function (event)?{
???? return ?event.target? || ?event.srcElement;
??},
??isLeftClick:? function (event)?{
???? return ?(((event.which)? && ?(event.which? == ? 1 ))? ||
????????????((event.button)? && ?(event.button? == ? 1 )));
??},
/*
?*click事件時鼠標以頁面為基準的x坐標值,?考慮到了滾動條導致的位移差
? */
??pointerX:? function (event)?{
???? return ?event.pageX? || ?(event.clientX? +
??????(document.documentElement.scrollLeft? || ?document.body.scrollLeft));
??},
/*
?*click事件時鼠標以頁面為基準的y坐標值,?考慮到了滾動條導致的位移差
? */
??pointerY:? function (event)?{
???? return ?event.pageY? || ?(event.clientY? +
??????(document.documentElement.scrollTop? || ?document.body.scrollTop));
??},
/*
?*停止冒泡(參見?http://www.quirksmode.org/js/events_order.html)?和阻止瀏覽器執行與事件相關的默認動作
?*?比如
?*?<a?>google</a>
?*?那么點擊該連接,頁面并不會執行轉向
? */
??stop:? function (event)?{
???? if ?(event.preventDefault)?{
??????event.preventDefault();
??????event.stopPropagation();
????}? else ?{
??????event.returnValue? = ? false ;
????}
??},
?? // ?find?the?first?node?with?the?given?tagName,?starting?from?the
?? // ?node?the?event?was?triggered?on;?traverses?the?DOM?upwards
/*
?*找到事件元素的父級元素中,最接近事件元素且等同于指定標簽名的父元素。
?*?如果到達頂級元素(HTML),那么就返回頂級元素
? */
??findElement:? function (event,?tagName)?{
???? var ?element? = ?Event.element(event);
???? while ?(element.parentNode? && ?( ! element.tagName? ||
????????(element.tagName.toUpperCase()? != ?tagName.toUpperCase())))
??????element? = ?element.parentNode;
???? return ?element;
??},
/*
?*其后的代碼封裝了事件的注冊和反注冊,避免ie的內存泄露的bug
?*?參見??http://javascript.weblogsinc.com/entry/1234000267034921/
? */
??observers:? false ,
?
/*
?*this.observers?的數據格式是一個二維數組,二維的數組分別四個元素分別是
?*?[注冊事件對象,事件名,事件處理函數,事件處理模式布爾值]
? */
??_observeAndCache:? function (element,?name,?observer,?useCapture)?{
???? if ?( ! this .observers)? this .observers? = ?[];
???? if ?(element.addEventListener)?{
?????? this .observers.push([element,?name,?observer,?useCapture]);
??????element.addEventListener(name,?observer,?useCapture);
????}? else ? if ?(element.attachEvent)?{
?????? this .observers.push([element,?name,?observer,?useCapture]);
??????element.attachEvent('on'? + ?name,?observer);
????}
??},
?
??unloadCache:? function ()?{
???? if ?( ! Event.observers)? return ;
???? for ?( var ?i? = ? 0 ;?i? < ?Event.observers.length;?i ++ )?{
/*
?*?這里與?Ajax.Request?對象設置?request?header?的代碼異曲同工
? */
??????Event.stopObserving.apply( this ,?Event.observers[i]);
??????Event.observers[i][ 0 ]? = ? null ;
????}
????Event.observers? = ? false ;
??},
/*
?*注冊對象的事件處理,并記錄到cache中
? */
??observe:? function (element,?name,?observer,?useCapture)?{
???? var ?element? = ?$(element);
????useCapture? = ?useCapture? || ? false ;
???
???? if ?(name? == ?'keypress'? &&
????????((navigator.appVersion.indexOf('AppleWebKit')? > ? 0 )
???????? || ?element.attachEvent))
??????name? = ?'keydown';
???
???? this ._observeAndCache(element,?name,?observer,?useCapture);
??},
/*
?*取消對象已注冊的事件處理
? */
??stopObserving:? function (element,?name,?observer,?useCapture)?{
???? var ?element? = ?$(element);
????useCapture? = ?useCapture? || ? false ;
???
???? if ?(name? == ?'keypress'? &&
????????((navigator.appVersion.indexOf('AppleWebKit')? > ? 0 )
???????? || ?element.detachEvent))
??????name? = ?'keydown';
???
???? if ?(element.removeEventListener)?{
??????element.removeEventListener(name,?observer,?useCapture);
????}? else ? if ?(element.detachEvent)?{
??????element.detachEvent('on'? + ?name,?observer);
????}
??}
});
/* ?prevent?memory?leaks?in?IE? */
/*
?*頁面onload?的時候取消所有事件注冊,避免ie內存泄漏的bug
*/
Event.observe(window,?'unload',?Event.unloadCache,? false );
/*
?*?Position?對象也是常用的工具類,提供了獲取元素在頁面上位置的函數,Drag&Drop的效果一定常會用到
?*?具體的應用參考?script.aculo.us?基于prototype?的實現,尤其是dragdrop.js。
? */
var ?Position? = ?{
?? // ?set?to?true?if?needed,?warning:?firefox?performance?problems
?? // ?NOT?neeeded?for?page?scrolling,?only?if?draggable?contained?in
?? // ?scrollable?elements
??includeScrollOffsets:? false ,
?? // ?must?be?called?before?calling?withinIncludingScrolloffset,?every?time?the
?? // ?page?is?scrolled
??prepare:? function ()?{
???? this .deltaX? = ??window.pageXOffset
???????????????? || ?document.documentElement.scrollLeft
???????????????? || ?document.body.scrollLeft
???????????????? || ? 0 ;
???? this .deltaY? = ??window.pageYOffset
???????????????? || ?document.documentElement.scrollTop
???????????????? || ?document.body.scrollTop
???????????????? || ? 0 ;
??},
/*
?*當對象所處的頁面有滾動條是,計算位移
? */
??realOffset:? function (element)?{
???? var ?valueT? = ? 0 ,?valueL? = ? 0 ;
???? do ?{
??????valueT? += ?element.scrollTop?? || ? 0 ;
??????valueL? += ?element.scrollLeft? || ? 0 ;
??????element? = ?element.parentNode;
????}? while ?(element);
???? return ?[valueL,?valueT];
??},
/*
?*計算出對象在頁面上的位置
? */
??cumulativeOffset:? function (element)?{
???? var ?valueT? = ? 0 ,?valueL? = ? 0 ;
???? do ?{
??????valueT? += ?element.offsetTop?? || ? 0 ;
??????valueL? += ?element.offsetLeft? || ? 0 ;
??????element? = ?element.offsetParent;
????}? while ?(element);
???? return ?[valueL,?valueT];
??},
?? // ?caches?x/y?coordinate?pair?to?use?with?overlap
/*
?*判斷一個坐標是否在指定元素的空間范圍中
?*?比如你想判斷鼠標點擊點的坐標是否在某個層或窗口
? */
??within:? function (element,?x,?y)?{
???? if ?( this .includeScrollOffsets)
?????? return ? this .withinIncludingScrolloffsets(element,?x,?y);
???? this .xcomp? = ?x;
???? this .ycomp? = ?y;
???? this .offset? = ? this .cumulativeOffset(element);
???? return ?(y? >= ? this .offset[ 1 ]? &&
????????????y? < ?? this .offset[ 1 ]? + ?element.offsetHeight? &&
????????????x? >= ? this .offset[ 0 ]? &&
????????????x? < ?? this .offset[ 0 ]? + ?element.offsetWidth);
??},
??withinIncludingScrolloffsets:? function (element,?x,?y)?{
???? var ?offsetcache? = ? this .realOffset(element);
???? this .xcomp? = ?x? + ?offsetcache[ 0 ]? - ? this .deltaX;
???? this .ycomp? = ?y? + ?offsetcache[ 1 ]? - ? this .deltaY;
???? this .offset? = ? this .cumulativeOffset(element);
???? return ?( this .ycomp? >= ? this .offset[ 1 ]? &&
???????????? this .ycomp? < ?? this .offset[ 1 ]? + ?element.offsetHeight? &&
???????????? this .xcomp? >= ? this .offset[ 0 ]? &&
???????????? this .xcomp? < ?? this .offset[ 0 ]? + ?element.offsetWidth);
??},
?? // ?within?must?be?called?directly?before
/*
?*調用該方法時,確保首先調用了within方法
?*?如果x,y坐標位于element的空間范圍中,那么返回一個小于1的標示位置的值,比如0.5標示該坐標位于element空間的中線上
? */
??overlap:? function (mode,?element)?{?
???? if ?( ! mode)? return ? 0 ;?
???? if ?(mode? == ?'vertical')
?????? return ?(( this .offset[ 1 ]? + ?element.offsetHeight)? - ? this .ycomp)? /
????????element.offsetHeight;
???? if ?(mode? == ?'horizontal')
?????? return ?(( this .offset[ 0 ]? + ?element.offsetWidth)? - ? this .xcomp)? /
????????element.offsetWidth;
??},
/*
?*復制源對象的空間數據到目的對象。
?*?常用的地方:拖綴一個層到新地方時,常常動態構造和該層同樣大小的虛層。
? */
??clone:? function (source,?target)?{
????source? = ?$(source);
????target? = ?$(target);
????target.style.position? = ?'absolute';
???? var ?offsets? = ? this .cumulativeOffset(source);
????target.style.top???? = ?offsets[ 1 ]? + ?'px';
????target.style.left??? = ?offsets[ 0 ]? + ?'px';
????target.style.width?? = ?source.offsetWidth? + ?'px';
????target.style.height? = ?source.offsetHeight? + ?'px';
??}
}