prototype框架最早是出于方便Ruby開發(fā)人員進(jìn)行
javascript開發(fā)所構(gòu)建的,從這個(gè)版本上更加體現(xiàn)的淋漓盡致。
比起1.3.1版本,1.4.0中的編程思想和技巧更加令人拍案叫絕,對(duì)于開拓編程思路很有幫助。
該版本主要加入了迭代器思想,也是Ruby中的一個(gè)核心概念,從而使用此框架進(jìn)行
javascript開發(fā)幾乎可以避免for循環(huán)的使用。
/*----------------------------------------------------------------------------------------------------*/
/*
定義prototype對(duì)象,告知版本信息,有利于程序的自動(dòng)檢測(cè)
ScriptFragment是正則表達(dá)式,用于捕獲字符串中的<script>標(biāo)記及其中的內(nèi)容
emptyFunction:空函數(shù)
K:返回參數(shù)自身的函數(shù),后面會(huì)有應(yīng)用
在這里使用了直接定義對(duì)象的語法:
var?obj={
property1:
value1,
property2:
value2,
....
}
后面會(huì)經(jīng)常用到
*/
var?Prototype?=?{
??Version:?'1.4.0',
??ScriptFragment:?'(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
??emptyFunction:?function()?{},
??K:?function(x)?{return?x}
}
/*
定義創(chuàng)建類的模式,使用此模式創(chuàng)建的類能夠?qū)崿F(xiàn)構(gòu)造函數(shù)
其中initialize是一個(gè)抽象方法,apply使得能對(duì)其保持參數(shù)。
如果直接調(diào)用this.initialize(arguments),則整個(gè)參數(shù)數(shù)組作為了一個(gè)參數(shù)。
*/
var?Class?=?{
??create:?function()?{
????return?function()?{
??????this.initialize.apply(this,?arguments);
????}
??}
}
//表示命名空間或者抽象類的東西,使代碼邏輯更加清楚
var?Abstract?=?new?Object();
/*
將source的所有屬性復(fù)制到destination
例如:
var?a={};
var?b={p:1};
Object.extent(a,b);
alert(a.p);
可以看到a具有了屬性p且值等于1。
如果屬性相同則覆蓋。
*/
Object.extend?=?function(destination,?source)?{
??for?(property?in?source)?{
????destination[property]?=?source[property];
??}
??return?destination;
}
/*
相比prototype-1.3.1這里少了下面的函數(shù):
Object.prototype.extend?=?function(object)?{
??return?Object.extend.apply(this,?[this,?object]);
}
所以原先基于1.3.1框架的js腳本升級(jí)到1.4.0時(shí)會(huì)產(chǎn)生兼容性問題。只要在1.4.0里加上上述函數(shù)即可。
去掉的原因大概因?yàn)闉槊總€(gè)object都增加extend方法顯的很浪費(fèi),畢竟95%的對(duì)象是不會(huì)用到的。
而且增加了extend方法也為反射枚舉帶來一定的麻煩,這從后面Hash對(duì)象的用法可以看到。
*/
/*
將對(duì)象轉(zhuǎn)換為字符串,這里能夠更詳細(xì)一些,只要對(duì)象自定義了inspect函數(shù)。而不是原來對(duì)象的toString總是[object]。
例如后面對(duì)數(shù)組定義了inspect函數(shù),使得
var?arr=[1,2,3];
-》arr.inspect()=="[1,2,3]";
*/
Object.inspect?=?function(object)?{
??try?{
????if?(object?==?undefined)?return?'undefined';
????if?(object?==?null)?return?'null';
????return?object.inspect???object.inspect()?:?object.toString();
??}?catch?(e)?{
????if?(e?instanceof?RangeError)?return?'...';
????throw?e;
??}
}
/*
一個(gè)很重要的方法,能夠?qū)⒑瘮?shù)綁定到某個(gè)對(duì)象運(yùn)行
和1.3.1版本相比,原來不能在綁定的時(shí)候就添加參數(shù),而現(xiàn)在可以。
例如:
var?obj1={p:"obj1"};
var?obj2={
p:"obj2",
method:function(arg){
alert(arg+this.p);
}
}
obj2.method("this?is?");//顯示“this?is?obj2”;
obj2.method.bind(obj1,"now?this?is?");//顯示“now?this?is?obj1”;
最后一句在1.3.1中必須寫為:
obj2.method.bind(obj1)("now?this?is?");//顯示“now?this?is?obj1”;
*/
Function.prototype.bind?=?function()?{
??var?__method?=?this,?args?=?$A(arguments),?object?=?args.shift();
??return?function()?{
????return?__method.apply(object,?args.concat($A(arguments)));
??}
}
/*
將函數(shù)作為對(duì)象的事件監(jiān)聽器,這樣可以產(chǎn)生獨(dú)立而且通用的事件處理程序,例如要對(duì)單擊事件進(jìn)行處理:
function?clickHandler(element){
//處理element的單擊事件
}
假設(shè)有節(jié)點(diǎn)node1,則:
node1.
onclick=function(){
clickHandler.bindAsEventListener(this)(event||window.event);
}
*/
Function.prototype.bindAsEventListener?=?function(object)?{
??var?__method?=?this;
??return?function(event)?{
????return?__method.call(object,?event?||?window.event);
??}
}
/*
所有的數(shù)字類型都是Number類的實(shí)例,下面就是給Number類定義一些方法
*/
Object.extend(Number.prototype,?{
/*
將數(shù)字轉(zhuǎn)換為顏色的形式
*/
??toColorPart:?function()?{
????var?digits?=?this.toString(16);
????if?(this?<?16)?return?'0'?+?digits;
????return?digits;
??},
//加1
??succ:?function()?{
????return?this?+?1;
??},
/*
執(zhí)行指定次數(shù)的循環(huán),例如獲取10個(gè)隨機(jī)數(shù)
var?ran=[]
var?c=10;
c.times(function(){
ran.push(Math.random());
});
$R是ObjectRange對(duì)象的快捷創(chuàng)建形式,后面會(huì)有介紹。
*/
??times:?function(iterator)?{
????$R(0,?this,?true).each(iterator);
????return?this;
??}
});
/*
Try對(duì)象,僅有一個(gè)方法these
*/
var?Try?=?{
/*
根據(jù)參數(shù)指定的函數(shù)進(jìn)行調(diào)用,返回第一個(gè)調(diào)用成功的值
在后面跨瀏覽器建立XMLHttpRequest對(duì)象時(shí)就用到了。
如果所有都不成功則返回undefined
*/
??these:?function()?{
????var?return
value;
????for?(var?i?=?0;?i?<?arguments.length;?i++)?{
??????var?lambda?=?arguments[i];
??????try?{
????????return
value?=?lambda();
????????break;
??????}?catch?(e)?{}
????}
????return?return
value;
??}
}
/*--------------------------------------------------------------------------*/
/*
定時(shí)器類,比起window.setInterval函數(shù),該類能夠使得回調(diào)函數(shù)不會(huì)被并發(fā)調(diào)用,見onTimerEvent的注釋。
*/
var?PeriodicalExecuter?=?Class.create();
PeriodicalExecuter.prototype?=?{
/*
構(gòu)造函數(shù),指定回調(diào)函數(shù)和執(zhí)行頻率,單位為秒
*/
??initialize:?function(callback,?frequency)?{
????this.callback?=?callback;
????this.frequency?=?frequency;
????this.currentlyExecuting?=?false;
????this.registerCallback();
??},
/*
開始執(zhí)行定時(shí)器,一般不要顯示調(diào)用,在構(gòu)造函數(shù)中被調(diào)用
注意這里寫為:
this.onTimerEvent.bind(this)
如果寫為:
this.onTimerEvent
則onTimerEvent中的函數(shù)的this指針將指向window對(duì)象,即setInterval的默認(rèn)對(duì)象。
*/
??registerCallback:?function()?{
????setInterval(this.onTimerEvent.bind(this),?this.frequency?*?1000);
??},
/*
相當(dāng)于回調(diào)函數(shù)的一個(gè)代理。
在傳統(tǒng)的setInterval函數(shù)中,時(shí)間一到,便強(qiáng)制執(zhí)行回調(diào)函數(shù),而這里加入了currentlyExecuting屬性判斷,
則如果callback函數(shù)的執(zhí)行時(shí)間超過了一個(gè)時(shí)間片,則阻止其被重復(fù)執(zhí)行。
*/
??onTimerEvent:?function()?{
????if?(!this.currentlyExecuting)?{
??????try?{
????????this.currentlyExecuting?=?true;
????????this.callback();
??????}?finally?{
????????this.currentlyExecuting?=?false;
??????}
????}
??}
}
/*--------------------------------------------------------------------------*/
/*
很方便的一個(gè)快速鏈接函數(shù),能夠獲得參數(shù)所指定的頁面節(jié)點(diǎn),如果有多個(gè)參數(shù)則返回?cái)?shù)組。
參數(shù)的形式既可以是節(jié)點(diǎn)的id值,也可以是節(jié)點(diǎn)的引用,即$($("someId"))和$("someId")是等價(jià)的;
*/
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;
}
/*
為字符串對(duì)象添加方法,和前面為Number添加方法的原理相同
*/
Object.extend(String.prototype,?{
/*
將Html轉(zhuǎn)換為純文本,例如:
var?s="<font?color='red'>hello</font>";
s.stripTags()將得到“hello”。
*/
??stripTags:?function()?{
????return?this.replace(/<\/?[^>]+>/gi,?'');
??},
/*
刪除文本中的腳本代碼(<script?xxx>...</script>)
*/
??stripScripts:?function()?{
????return?this.replace(new?RegExp(Prototype.ScriptFragment,?'img'),?'');
??},
//提取字符串中的腳本,返回所有腳本內(nèi)容組成的數(shù)組
??extractScripts:?function()?{
????var?matchAll?=?new?RegExp(Prototype.ScriptFragment,?'img');//先找到所有包括<script>的代碼標(biāo)記
????var?matchOne?=?new?RegExp(Prototype.ScriptFragment,?'im'); //再對(duì)每個(gè)腳本刪除<script>標(biāo)記
????return?(this.match(matchAll)?||?[]).map(function(scriptTag)?{
??????return?(scriptTag.match(matchOne)?||?['',?''])[1];
????});
??},
//先提取字符串中的腳本塊,再執(zhí)行這些腳本
??evalScripts:?function()?{
????return?this.extractScripts().map(eval);
??},
/*
利用瀏覽器本身的機(jī)制對(duì)Html字符串進(jìn)行編碼,例如將<轉(zhuǎn)換為<
*/
??escapeHTML:?function()?{
????var?div?=?document.createElement('div');
????var?text?=?document.createTextNode(this);
????div.appendChild(text);
????return?div.innerHTML;
??},
/*
對(duì)Html進(jìn)行解碼
*/
??unescapeHTML:?function()?{
????var?div?=?document.createElement('div');
????div.innerHTML?=?this.stripTags();
????return?div.childNodes[0]???div.childNodes[0].node
value?:?'';
??},
//獲取查詢字符串?dāng)?shù)組,例如通過document.location.toQueryParams()就可以得到由鍵和值組成的哈希表(用對(duì)象表示)。
??toQueryParams:?function()?{
????var?pairs?=?this.match(/^\??(.*)$/)[1].split('&');
????return?pairs.inject({},?function(params,?pairString)?{
??????var?pair?=?pairString.split('=');
??????params[pair[0]]?=?pair[1];
??????return?params;
????});
??},
//將字符串轉(zhuǎn)換為字符數(shù)組
??toArray:?function()?{
????return?this.split('');
??},
/*
將用"-"連接的字符串駱駝化。例如:
var?s="background-color";
alert(s.camelize());
將得到“backgroundColor”。
*/
??camelize:?function()?{
????var?oStringList?=?this.split('-');
????if?(oStringList.length?==?1)?return?oStringList[0];
????var?camelizedString?=?this.indexOf('-')?==?0
????????oStringList[0].charAt(0).toUpperCase()?+?oStringList[0].substring(1)
??????:?oStringList[0];
????for?(var?i?=?1,?len?=?oStringList.length;?i?<?len;?i++)?{
??????var?s?=?oStringList[i];
??????camelizedString?+=?s.charAt(0).toUpperCase()?+?s.substring(1);
????}
????return?camelizedString;
??},
/*
inspect是觀察的意思。這里大概就是將字符串轉(zhuǎn)換為可觀察的形式。這里將轉(zhuǎn)義字符寫成轉(zhuǎn)義前的字符串形式,
例如:
var?s="abc\ndef";
alert(s);
將得到兩行字符串,上一行是abc,下一行是def
而
alert(s.inspect());
將得到abc\ndef
即給字符串賦值時(shí)的形式,這和數(shù)組的inspect作用類似。
*/
??inspect:?function()?{
????return?"'"?+?this.replace('\\',?'\\\\').replace("'",?'\\\'')?+?"'";
??}
});
//做一個(gè)名稱鏈接
String.prototype.parseQuery?=?String.prototype.toQueryParams;
//定義了兩個(gè)異常對(duì)象,主要用于迭代控制
var?$break????=?new?Object();
var?$continue?=?new?Object();
/*
這是一個(gè)非常Ruby的機(jī)制,事實(shí)上可以將Enumerable看作一個(gè)枚舉接口,
而_each是必須實(shí)現(xiàn)的方法,只要實(shí)現(xiàn)了此方法的類,都可以調(diào)用接口類中的其他成員。
例如后面Array就實(shí)現(xiàn)了此接口,也是最典型的應(yīng)用
*/
var?Enumerable?=?{
/*
對(duì)可枚舉對(duì)象的每個(gè)成員調(diào)用iterator(迭代器)方法,
如果迭代器方法拋出$continue異常,則繼續(xù)執(zhí)行,如果拋出$break異常,則不再繼續(xù)迭代
其中調(diào)用了_each這個(gè)抽象方法,
_each是由具體的繼承于Enumerable的類實(shí)現(xiàn)的
index計(jì)數(shù)器的作用是用于告訴迭代器當(dāng)前執(zhí)行到第幾個(gè)元素,是迭代器可選實(shí)現(xiàn)的。
*/
??each:?function(iterator)?{
????var?index?=?0;
????try?{
??????this._each(function(
value)?{
????????try?{
??????????iterator(
value,?index++);
????????}?catch?(e)?{
??????????if?(e?!=?$continue)?throw?e;
????????}
??????});
????}?catch?(e)?{
??????if?(e?!=?$break)?throw?e;
????}
??},
/*
判斷枚舉對(duì)象中的所有元素是否都能使得迭代器返回true。如果沒有指定迭代器,則判斷所有元素是否都對(duì)應(yīng)于布爾類型的true
如果所有都滿足,則返回true;否則返回false;
注意這里就使用了$break異常,用于實(shí)現(xiàn)“邏輯與”操作的短路效果
另外值得注意的一個(gè)技巧是使用了!!將一個(gè)變量強(qiáng)制轉(zhuǎn)換為布爾類型,可以參考:
http://www.x2blog.cn/supNate/?tid=4669*/
all:?function(iterator)?{
var?result?=?true;
this.each(function(
value,?index)?{
result?=?result?&&?!!(iterator?||?Prototype.K)(
value,?index);
if?(!result)?throw?$break;
});
return?result;
},
/*
判斷枚舉對(duì)象中的所有元素是否有滿足指定迭代器的值(返回true),如果有則返回true,否則返回false
其原理和all方法類似
如果數(shù)組為空,仍然返回true,這一點(diǎn)有點(diǎn)匪夷所思。
*/
??any:?function(iterator)?{
????var?result?=?true;
????this.each(function(
value,?index)?{
??????if?(result?=?!!(iterator?||?Prototype.K)(
value,?index))
????????throw?$break;
????});
????return?result;
??},
/*
返回所有枚舉元素通過迭代器執(zhí)行的結(jié)果,作為數(shù)組返回
*/
??collect:?function(iterator)?{
????var?results?=?[];
????this.each(function(
value,?index)?{
??????results.push(iterator(
value,?index));
????});
????return?results;
??},
/*
返回第一個(gè)能夠使得迭代器返回true的枚舉元素的值,如果沒有true,則返回"undefined",即result未被賦值
這有可能是作者考慮的一個(gè)小失誤,畢竟返回"undefined"并不是一個(gè)好的風(fēng)格(僅是猜測(cè))
*/
??detect:?function?(iterator)?{
????var?result;
????this.each(function(
value,?index)?{
??????if?(iterator(
value,?index))?{
????????result?=?
value;
????????throw?$break;
??????}
????});
????return?result;
??},
/*
返回所有能夠使得迭代器返回true的枚舉元素,作為數(shù)組返回。
*/
??findAll:?function(iterator)?{
????var?results?=?[];
????this.each(function(
value,?index)?{
??????if?(iterator(
value,?index))
????????results.push(
value);
????});
????return?results;
??},
/*
grep是unix類操作系統(tǒng)下的一個(gè)經(jīng)典命令,而這里則是
javascript的一個(gè)類似實(shí)現(xiàn)
pattern是正則模式,對(duì)所有符合此模式的枚舉元素進(jìn)行迭代器運(yùn)算,并將運(yùn)算結(jié)果保存到數(shù)組中并返回。
需要注意,這里的iterator參數(shù)是可選的,此時(shí)僅僅對(duì)枚舉元素進(jìn)行模式匹配,返回所有的匹配結(jié)果
*/
??grep:?function(pattern,?iterator)?{
????var?results?=?[];
????this.each(function(
value,?index)?{
??????var?string
value?=?
value.toString();
??????if?(string
value.match(pattern))
????????results.push((iterator?||?Prototype.K)(
value,?index));
????})
????return?results;
??},
/*
判斷枚舉對(duì)象中是否包含指定值的枚舉元素,這里仍然使用了each方法,而不是循環(huán),可見prototype致力于提供一種ruby化的編程方式,
如果用循環(huán)實(shí)現(xiàn),則是類似于以下的代碼:
for(var?i=0;i<this.length;i++){
if(this[i]==object)return?true;
}
而該函數(shù)中,定義了迭代器:
function(
value)?{
??????if?(
value?==?object)?{
????????found?=?true;
????????throw?$break;
??????}
????}
這個(gè)迭代器作為each方法的參數(shù)。
*/
??include:?function(object)?{
????var?found?=?false;
????this.each(function(
value)?{
??????if?(
value?==?object)?{
????????found?=?true;
????????throw?$break;
??????}
????});
????return?found;
??},
/*
字面意思是“注入”,其作用相當(dāng)于將memo作為聯(lián)系各個(gè)迭代器的全局變量,每次迭代都對(duì)其進(jìn)行操作,返回操作的最后結(jié)果。例如對(duì)于數(shù)組:
var?arr=[1,2,3];
現(xiàn)在想將其字符串化為:123
如果不調(diào)用join方法,傳統(tǒng)做法是:
var?s="";
for(var?i=0;i<arr.length;i++){
s+=arr[i];
}
現(xiàn)在通過調(diào)用inject函數(shù),則:
var?s=arr.inject("",function(memo,
value){return?memo+
value});
兩者運(yùn)行的結(jié)果是完全相同的。
*/
??inject:?function(memo,?iterator)?{
????this.each(function(
value,?index)?{
??????memo?=?iterator(memo,?
value,?index);
????});
????return?memo;
??},
/*
在所有枚舉元素上調(diào)用method方法,并可以給這個(gè)方法傳遞參數(shù)
返回所有method的執(zhí)行結(jié)果,作為數(shù)組返回
*/
??invoke:?function(method)?{
????var?args?=?$A(arguments).slice(1);
????return?this.collect(function(
value)?{
??????return?
value[method].apply(
value,?args);
????});
??},
/*
返回最大的迭代器返回值
*/
??max:?function(iterator)?{
????var?result;
????this.each(function(
value,?index)?{
??????
value?=?(iterator?||?Prototype.K)(
value,?index);
??????if?(
value?>=?(result?||?
value))
????????result?=?
value;
????});
????return?result;
??},
/*
返回最小的迭代器返回值
*/
??min:?function(iterator)?{
????var?result;
????this.each(function(
value,?index)?{
??????
value?=?(iterator?||?Prototype.K)(
value,?index);
??????if?(
value?<=?(result?||?
value))
????????result?=?
value;
????});
????return?result;
??},
/*
按照迭代器的返回結(jié)果,將枚舉元素分為兩個(gè)數(shù)組trues和falses,其中trues包括迭代器返回true的枚舉元素,falses則相反。
*/
??partition:?function(iterator)?{
????var?trues?=?[],?falses?=?[];
????this.each(function(
value,?index)?{
??????((iterator?||?Prototype.K)(
value,?index)??
????????trues?:?falses).push(
value);
????});
????return?[trues,?falses];
??},
/*
返回所有枚舉元素的property屬性
*/
??pluck:?function(property)?{
????var?results?=?[];
????this.each(function(
value,?index)?{
??????results.push(
value[property]);
????});
????return?results;
??},
/*
返回所有迭代器執(zhí)行結(jié)果為false的枚舉元素
*/
??reject:?function(iterator)?{
????var?results?=?[];
????this.each(function(
value,?index)?{
??????if?(!iterator(
value,?index))
????????results.push(
value);
????});
????return?results;
??},
/*
結(jié)構(gòu)復(fù)雜的一個(gè)函數(shù),作用是根據(jù)迭代器iterator的結(jié)果對(duì)枚舉元素進(jìn)行排序。使iterator執(zhí)行結(jié)果小的元素排在前面。
主要包括三個(gè)函數(shù)的調(diào)用:
1。collect方法,返回的每個(gè)數(shù)組元素包括:值和迭代器運(yùn)行該值的結(jié)果,用{
value:
value,criteria:iterator(
value,index)}得到
2。對(duì)collect返回的數(shù)組執(zhí)行sort方法,這時(shí)數(shù)組對(duì)象內(nèi)置的對(duì)象,參數(shù)是一個(gè)委托函數(shù),用于指定排序規(guī)則。其標(biāo)準(zhǔn)是對(duì)迭代器返回的值排序,小的在前面
3。對(duì)sort的結(jié)果執(zhí)行pluck方法,即返回
value屬性的值,于是最后還是返回的枚舉對(duì)象中的原有值,只是根據(jù)迭代器iterator的結(jié)果對(duì)這些元素進(jìn)行排序
*/
??sortBy:?function(iterator)?{
????return?this.collect(function(
value,?index)?{
??????return?{
value:?
value,?criteria:?iterator(
value,?index)};
????}).sort(function(left,?right)?{
??????var?a?=?left.criteria,?b?=?right.criteria;
??????return?a?<?b???-1?:?a?>?b???1?:?0;
????}).pluck('
value');
??},
/*
將枚舉對(duì)象轉(zhuǎn)換為數(shù)組,使用了collect方法和Prototype.K函數(shù),減少了重復(fù)代碼
*/
??toArray:?function()?{
????return?this.collect(Prototype.K);
??},
/*
壓縮函數(shù),實(shí)現(xiàn)復(fù)雜,作用尚不能體會(huì)-_-。
接收的參數(shù)需要是可枚舉對(duì)象,可以有多個(gè)參數(shù)。最后一個(gè)參數(shù)是迭代器,可選。
作用是將自身和參數(shù)組成的二維陣列進(jìn)行行列對(duì)換,并切除多余的數(shù)據(jù),或補(bǔ)充缺少的數(shù)據(jù)(用undefined)。切換后的行數(shù)由調(diào)用者中元素的個(gè)數(shù)決定,而列數(shù)是數(shù)組參數(shù)的個(gè)數(shù)加1。
每個(gè)數(shù)組參數(shù)的第一個(gè)元素順序組成第一行,第二個(gè)元素順序組成第二行,依次類推。直到調(diào)用者中的元素用完為止。
迭代器的作用就是對(duì)轉(zhuǎn)換后的每一行進(jìn)行一次運(yùn)算。
例如:
var?arr1=[1,2,3];
var?arr2=[4,5,6];
var?arr3=[7,8,9];
var?arr=arr1.zip(arr2,arr3);
//使用迭代器輸出結(jié)果,inspect用于輸出數(shù)組語法表示的數(shù)組字符串,后面有介紹
arr.each(function(s){
document.write(s.inspect());
document.write("<br/>");
}
);
得到的結(jié)果為:
[1,?4,?7]
[2,?5,?8]
[3,?6,?9]
如果讓arr1=[1,2],其他不變,則執(zhí)行結(jié)果為:
[1,?4,?7]
[2,?5,?8]
*/
??zip:?function()?{
????var?iterator?=?Prototype.K,?args?=?$A(arguments);
????if?(typeof?args.last()?==?'function')
??????iterator?=?args.pop();
//將自身枚舉對(duì)象作為一個(gè)元素,與參數(shù)(也是可枚舉的)組成一個(gè)數(shù)組,并將枚舉對(duì)象轉(zhuǎn)換為數(shù)組(通過$A迭代器)
????var?collections?=?[this].concat(args).map($A);
????return?this.map(function(
value,?index)?{
??????iterator(
value?=?collections.pluck(index));
??????return?
value;
????});
??},
/*
這實(shí)際上這是一個(gè)待實(shí)現(xiàn)的抽象方法,在Array對(duì)象中有對(duì)其進(jìn)行的重定義
所以將this轉(zhuǎn)換為數(shù)組(toArray()),再調(diào)用inspect。
對(duì)于非數(shù)組形式的枚舉對(duì)象,則會(huì)加上'#<Enumerable:....>'這樣的形式
*/
??inspect:?function()?{
????return?'#<Enumerable:'?+?this.toArray().inspect()?+?'>';
??}
}
//對(duì)Enumerable基類的一些方法做了快速鏈接
Object.extend(Enumerable,?{
??map:?????Enumerable.collect,
??find:????Enumerable.detect,
??select:??Enumerable.findAll,
??member:??Enumerable.include,
??entries:?Enumerable.toArray
});
/*
將一個(gè)對(duì)象轉(zhuǎn)換為數(shù)組。
對(duì)于字符串則直接變?yōu)樽址麛?shù)組,例如$A("abc")將得到,["a","b","c"]
否則集合對(duì)象變?yōu)閿?shù)組,這類對(duì)象包括函數(shù)的參數(shù)集合arguments,<select>的options集合,
<form>的elements集合等等,一個(gè)節(jié)點(diǎn)的所有子結(jié)點(diǎn)childNodes等等。
*/
var?$A?=?Array.from?=?function(iterable)?{
if?(!iterable)?return?[];
if?(iterable.toArray)?{
return?iterable.toArray();
}?else?{
var?results?=?[];
for?(var?i?=?0;?i?<?iterable.length;?i++)
results.push(iterable[i]);
return?results;
}
}
/*
讓數(shù)組繼承于Enumarable對(duì)象(基類)
*/
Object.extend(Array.prototype,?Enumerable);
/*
做一個(gè)鏈接,prototype中一般私有的成員或抽象成員都用下劃線開頭,這里的_reverse大概就是起一個(gè)說明性的作用,將其作為抽象方法使用。
*/
Array.prototype._reverse?=?Array.prototype.reverse;
/*
為數(shù)組對(duì)象添加一些快捷方法
*/
Object.extend(Array.prototype,?{
/*
迭代器方法,源于Ruby中的迭代器用法
_each方法的作用就是將數(shù)組的每個(gè)元素作為iterator函數(shù)的參數(shù),并執(zhí)行iterator方法。例如對(duì)于數(shù)組:var?arr=[1,2,3,4,5,6];
如果要顯示其中的每個(gè)元素,通常的做法是
for(var?i=0;i<arr.length;i++){
alert(arr[i]);
}
而使用此方法則:
arr._each(function(s){alert(s)});
因此,在Ruby的代碼中很少出現(xiàn)循環(huán),這個(gè)函數(shù)使得
javascript同樣也能夠?qū)崿F(xiàn)。
*/
??_each:?function(iterator)?{
????for?(var?i?=?0;?i?<?this.length;?i++)
??????iterator(this[i]);
??},
//清空數(shù)組
??clear:?function()?{
????this.length?=?0;
????return?this;
??},
//獲取第一個(gè)元素的值
??first:?function()?{
????return?this[0];
??},
//獲取最后一個(gè)元素的值
??last:?function()?{
????return?this[this.length?-?1];
??},
/*
用于刪除一個(gè)數(shù)組中的未定義值和null值
這里的select是從Emurable中繼承的方法,而select又是findAll函數(shù)的別名
*/
??compact:?function()?{
????return?this.select(function(
value)?{
??????return?
value?!=?undefined?||?
value?!=?null;
????});
??},
/*
將一個(gè)枚舉對(duì)象中的所有數(shù)組元素全部展開,最后返回一個(gè)數(shù)組,是一個(gè)遞歸的過程
*/
??flatten:?function()?{
????return?this.inject([],?function(array,?
value)?{
??????return?array.concat(
value.constructor?==?Array??
????????
value.flatten()?:?[
value]);
????});
??},
/*
從數(shù)組中刪除參數(shù)指定的元素,返回刪除后的結(jié)果
*/
??without:?function()?{
????var?
values?=?$A(arguments);
????return?this.select(function(
value)?{
??????return?!
values.include(
value);
????});
??},
/*
返回一個(gè)元素在數(shù)組中的索引
*/
??indexOf:?function(object)?{
????for?(var?i?=?0;?i?<?this.length;?i++)
??????if?(this[i]?==?object)?return?i;
????return?-1;
??},
/*
將數(shù)組元素順序逆轉(zhuǎn),inline用于確保是數(shù)組
*/
??reverse:?function(inline)?{
????return?(inline?!==?false???this?:?this.toArray())._reverse();
??},
/*
取出數(shù)組的第一個(gè)元素并返回
*/
??shift:?function()?{
????var?result?=?this[0];
????for?(var?i?=?0;?i?<?this.length?-?1;?i++)
??????this[i]?=?this[i?+?1];
????this.length--;
????return?result;
??},
/*
返回?cái)?shù)組的字符串表示
*/
??inspect:?function()?{
????return?'['?+?this.map(Object.inspect).join(',?')?+?']';
??}
});
/*
定義哈希對(duì)象的通用操作
*/
var?Hash?=?{
/*
實(shí)現(xiàn)可枚舉接口。
對(duì)hash對(duì)象中的每個(gè)元素進(jìn)行迭代操作,迭代器被認(rèn)為接收一個(gè)數(shù)組參數(shù),數(shù)組的第一個(gè)元素是key,第二個(gè)元素是
value同時(shí),此數(shù)組對(duì)象還增加了兩個(gè)屬性key和
value。分表表示鍵和值。
*/
??_each:?function(iterator)?{
????for?(key?in?this)?{
??????var?
value?=?this[key];
??????if?(typeof?
value?==?'function')?continue;//不處理方法
??????var?pair?=?[key,?
value];
??????pair.key?=?key;
??????pair.
value?=?
value;
??????iterator(pair);
????}
??},
/*
返回所有的鍵組成的數(shù)組
*/
??keys:?function()?{
????return?this.pluck('key');
??},
/*
返回所有的值組成的數(shù)組
*/
??
values:?function()?{
????return?this.pluck('
value');
??},
/*
將兩個(gè)hash對(duì)象合并,如果鍵相同,則用參數(shù)中相應(yīng)鍵對(duì)應(yīng)的值覆蓋調(diào)用者的。
*/
??merge:?function(hash)?{
????return?$H(hash).inject($H(this),?function(mergedHash,?pair)?{
??????mergedHash[pair.key]?=?pair.
value;
??????return?mergedHash;
????});
??},
/*
將hash對(duì)象轉(zhuǎn)換為查詢字符串表示的形式
*/
??toQueryString:?function()?{
????return?this.map(function(pair)?{
??????return?pair.map(encodeURIComponent).join('=');
????}).join('&');
??},
/*
獲取hash對(duì)象的字符串表示
*/
??inspect:?function()?{
????return?'#<Hash:{'?+?this.map(function(pair)?{
??????return?pair.map(Object.inspect).join(':?');
????}).join(',?')?+?'}>';
??}
}
/*
將一個(gè)對(duì)象轉(zhuǎn)換為哈希對(duì)象,對(duì)象的屬性名(方法名)作為key,值作為
value同時(shí)hash對(duì)象也是一個(gè)可枚舉對(duì)象
*/
function?$H(object)?{
/*
object?||?{}使得參數(shù)為空時(shí)也能夠創(chuàng)建一個(gè)hash對(duì)象
*/
var?hash?=?Object.extend({},?object?||?{});
Object.extend(hash,?Enumerable);
Object.extend(hash,?Hash);
return?hash;
}
/*
又一個(gè)實(shí)現(xiàn)Enumerable接口的對(duì)象。
有了這個(gè)類,基本上就可以完全避免使用for循環(huán)了,一個(gè)例子,計(jì)算1到100的和:
傳統(tǒng)寫法:
var?s=0;
for(var?i=0;i<=100;i++){
s+=i;
}
document.write(s);
使用ObjectRange:
var?s=$R(0,100,false).inject(0,function(s,i){
return?s+i;
});
document.write(s);
*/
ObjectRange?=?Class.create();
Object.extend(ObjectRange.prototype,?Enumerable);
Object.extend(ObjectRange.prototype,?{
/*
構(gòu)造函數(shù),start表示開始的位置,end表示結(jié)束的位置,exclusive表示是否排除最后一個(gè)索引位置
exclusive=true時(shí)對(duì)應(yīng)于:
for(var?i=start;i<end;i++){
//語句
}
exclusive=false時(shí)對(duì)應(yīng)于:
for(var?i=start;i<=end;i++){
//語句
}
*/
??initialize:?function(start,?end,?exclusive)?{
????this.start?=?start;
????this.end?=?end;
????this.exclusive?=?exclusive;
??},
/*
實(shí)現(xiàn)枚舉接口的_each方法
相當(dāng)于:
for(var?i=start;i<end;i++){
iterator(i);
}
*/
??_each:?function(iterator)?{
????var?
value?=?this.start;
????do?{
??????iterator(
value);
??????
value?=?
value.succ();
????}?while?(this.include(
value));
??},
/*
判斷是否包含指定的索引
*/
??include:?function(
value)?{
????if?(
value?<?this.start)
??????return?false;
????if?(this.exclusive)
??????return?
value?<?this.end;
????return?
value?<=?this.end;
??}
});
/*
做一個(gè)快速鏈接,用于生成ObjectRange對(duì)象
*/
var?$R?=?function(start,?end,?exclusive)?{
??return?new?ObjectRange(start,?end,?exclusive);
}
/*
封裝XMLHttpRequest的相關(guān)操作
*/
var?Ajax?=?{
//瀏覽器兼容的獲取XMLHttpRequest對(duì)象的函數(shù)
getTransport:?function()?{
return?Try.these(
function()?{return?new?ActiveXObject('Msxml2.XMLHTTP')},
function()?{return?new?ActiveXObject('Microsoft.XMLHTTP')},
function()?{return?new?XMLHttpRequest()}
)?||?false;
},
//當(dāng)前激活的請(qǐng)求數(shù)目
activeRequestCount:?0
}
/*
Ajax的返回值
*/
Ajax.Responders?=?{
??responders:?[],
//實(shí)現(xiàn)枚舉接口的_each方法
??_each:?function(iterator)?{
????this.responders._each(iterator);
??},
??register:?function(responderToAdd)?{
????if?(!this.include(responderToAdd))
??????this.responders.push(responderToAdd);
??},
??unregister:?function(responderToRemove)?{
????this.responders?=?this.responders.without(responderToRemove);
??},
??dispatch:?function(callback,?request,?transport,?json)?{
????this.each(function(responder)?{
??????if?(responder[callback]?&&?typeof?responder[callback]?==?'function')?{
????????try?{
??????????responder[callback].apply(responder,?[request,?transport,?json]);
????????}?catch?(e)?{}
??????}
????});
??}
};
/*
讓Ajax.Responders可枚舉迭代
*/
Object.extend(Ajax.Responders,?Enumerable);
Ajax.Responders.register({
onCreate:?function()?{
Ajax.activeRequestCount++;
},
onComplete:?function()?{
Ajax.activeRequestCount--;
}
});
//定義Ajax的基類
Ajax.Base?=?function()?{};
Ajax.Base.prototype?=?{
/*
設(shè)置XMLHttp調(diào)用的參數(shù),提供了默認(rèn)值:method:'post',異步,無參數(shù)
*/
??setOptions:?function(options)?{
????this.options?=?{
??????method:???????'post',
??????asynchronous:?true,
??????parameters:???''
????}
????Object.extend(this.options,?options?||?{});
??},
/*
判斷請(qǐng)求是否成功
*/
??responseIsSuccess:?function()?{
????return?this.transport.status?==?undefined
????????||?this.transport.status?==?0
????????||?(this.transport.status?>=?200?&&?this.transport.status?<?300);
??},
/*
判斷請(qǐng)求是否失敗
*/
??responseIsFailure:?function()?{
????return?!this.responseIsSuccess();
??}
}
//定義一個(gè)Ajax請(qǐng)求類
Ajax.Request?=?Class.create();
Ajax.Request.Events?=
??['Uninitialized',?'Loading',?'Loaded',?'Interactive',?'Complete'];
Ajax.Request.prototype?=?Object.extend(new?Ajax.Base(),?{
??initialize:?function(url,?options)?{
????this.transport?=?Ajax.getTransport();
????this.setOptions(options);
????this.request(url);
??},
??request:?function(url)?{
????var?parameters?=?this.options.parameters?||?'';
????if?(parameters.length?>?0)?parameters?+=?'&_=';
????try?{
??????this.url?=?url;
??????if?(this.options.method?==?'get'?&&?parameters.length?>?0)
????????this.url?+=?(this.url.match(/\?/)???'&'?:?'?')?+?parameters;
??????Ajax.Responders.dispatch('onCreate',?this,?this.transport);
??????this.transport.open(this.options.method,?this.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)?{
??????this.dispatchException(e);
????}
??},
??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');
????}
????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;
????if?(readyState?!=?1)
??????this.respondToReadyState(this.transport.readyState);
??},
??header:?function(name)?{
????try?{
??????return?this.transport.getResponseHeader(name);
????}?catch?(e)?{}
??},
??evalJSON:?function()?{
????try?{
??????return?eval(this.header('X-JSON'));
????}?catch?(e)?{}
??},
??evalResponse:?function()?{
????try?{
??????return?eval(this.transport.responseText);
????}?catch?(e)?{
??????this.dispatchException(e);
????}
??},
??respondToReadyState:?function(readyState)?{
????var?event?=?Ajax.Request.Events[readyState];
????var?transport?=?this.transport,?json?=?this.evalJSON();
????if?(event?==?'Complete')?{
??????try?{
????????(this.options['on'?+?this.transport.status]
?????????||?this.options['on'?+?(this.responseIsSuccess()???'Success'?:?'Failure')]
?????????||?Prototype.emptyFunction)(transport,?json);
??????}?catch?(e)?{
????????this.dispatchException(e);
??????}
??????if?((this.header('Content-type')?||?'').match(/^text\/
javascript/i))
????????this.evalResponse();
????}
????try?{
??????(this.options['on'?+?event]?||?Prototype.emptyFunction)(transport,?json);
??????Ajax.Responders.dispatch('on'?+?event,?this,?transport,?json);
????}?catch?(e)?{
??????this.dispatchException(e);
????}
????/*?Avoid?memory?leak?in?MSIE:?clean?up?the?oncomplete?event?handler?*/
????if?(event?==?'Complete')
??????this.transport.onreadystatechange?=?Prototype.emptyFunction;
??},
??dispatchException:?function(exception)?{
????(this.options.onException?||?Prototype.emptyFunction)(this,?exception);
????Ajax.Responders.dispatch('onException',?this,?exception);
??}
});
Ajax.Updater?=?Class.create();
Object.extend(Object.extend(Ajax.Updater.prototype,?Ajax.Request.prototype),?{
??initialize:?function(container,?url,?options)?{
????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(transport,?object)?{
??????this.updateContent();
??????onComplete(transport,?object);
????}).bind(this);
????this.request(url);
??},
??updateContent:?function()?{
????var?receiver?=?this.responseIsSuccess()??
??????this.containers.success?:?this.containers.failure;
????var?response?=?this.transport.responseText;
????if?(!this.options.evalScripts)
??????response?=?response.stripScripts();
????if?(receiver)?{
??????if?(this.options.insertion)?{
????????new?this.options.insertion(receiver,?response);
??????}?else?{
????????Element.update(receiver,?response);
??????}
????}
????if?(this.responseIsSuccess())?{
??????if?(this.onComplete)
????????setTimeout(this.onComplete.bind(this),?10);
????}
??}
});
Ajax.PeriodicalUpdater?=?Class.create();
Ajax.PeriodicalUpdater.prototype?=?Object.extend(new?Ajax.Base(),?{
??initialize:?function(container,?url,?options)?{
????this.setOptions(options);
????this.onComplete?=?this.options.onComplete;
????this.frequency?=?(this.options.frequency?||?2);
????this.decay?=?(this.options.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?||?Prototype.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);
??}
});
document.getElementsByClassName?=?function(className,?parentElement)?{
??var?children?=?($(parentElement)?||?document.body).getElementsByTagName('*');
??return?$A(children).inject([],?function(elements,?child)?{
????if?(child.className.match(new?RegExp("(^|\\s)"?+?className?+?"(\\s|$)")))
??????elements.push(child);
????return?elements;
??});
}
/*--------------------------------------------------------------------------*/
//定義一些Html節(jié)點(diǎn)通用的操作
if?(!window.Element)?{
??var?Element?=?new?Object();
}
Object.extend(Element,?{
/*
判斷節(jié)點(diǎn)是否可見
*/
??visible:?function(element)?{
????return?$(element).style.display?!=?'none';
??},
/*
切換節(jié)點(diǎn)的可見狀態(tài)
*/
??toggle:?function()?{
????for?(var?i?=?0;?i?<?arguments.length;?i++)?{
??????var?element?=?$(arguments[i]);
??????Element[Element.visible(element)???'hide'?:?'show'](element);
????}
??},
/*
隱藏參數(shù)所指定的節(jié)點(diǎn)
*/
??hide:?function()?{
????for?(var?i?=?0;?i?<?arguments.length;?i++)?{
??????var?element?=?$(arguments[i]);
??????element.style.display?=?'none';
????}
??},
/*
顯示參數(shù)所指定的節(jié)點(diǎn)
*/
??show:?function()?{
????for?(var?i?=?0;?i?<?arguments.length;?i++)?{
??????var?element?=?$(arguments[i]);
??????element.style.display?=?'';
????}
??},
/*
刪除一個(gè)節(jié)點(diǎn)
*/
??remove:?function(element)?{
????element?=?$(element);
????element.parentNode.removeChild(element);
??},
/*
用指定html填充element表示的節(jié)點(diǎn)
setTimeout是極具技巧的用法,讓人驚嘆。
update函數(shù)之所以會(huì)取代:element.innerHTML=html的用法,主要因?yàn)樗鼘?shí)現(xiàn)了瀏覽器的兼容性:
(1)對(duì)于IE,如果給innerHTML賦值的字符串中含有腳本標(biāo)記,腳本是被忽略的,不起作用;而firefox則會(huì)執(zhí)行腳本;
(2)setTimeout使得可以在函數(shù)內(nèi)可以通過eval定義全局函數(shù),這是由于setTimeout的默認(rèn)空間就是全局空間決定的(它是window對(duì)象的方法,而所有全局變量和全局函數(shù)實(shí)際上都是window對(duì)象的屬性和方法)。
*/
??update:?function(element,?html)?{
????$(element).innerHTML?=?html.stripScripts();
????setTimeout(function()?{html.evalScripts()},?10);
??},
//獲取節(jié)點(diǎn)的高度
??getHeight:?function(element)?{
????element?=?$(element);
????return?element.offsetHeight;
??},
//獲取一個(gè)元素的class,返回一個(gè)數(shù)組,包括了所有的class名稱,ClassNames后面實(shí)現(xiàn)
??classNames:?function(element)?{
????return?new?Element.ClassNames(element);
??},
/*判斷一個(gè)元素是否具有指定的class值*/
??hasClassName:?function(element,?className)?{
????if?(!(element?=?$(element)))?return;
????return?Element.classNames(element).include(className);
??},
//為一個(gè)節(jié)點(diǎn)添加class名稱
??addClassName:?function(element,?className)?{
????if?(!(element?=?$(element)))?return;
????return?Element.classNames(element).add(className);
??},
//從一個(gè)節(jié)點(diǎn)移除一個(gè)class名稱
??removeClassName:?function(element,?className)?{
????if?(!(element?=?$(element)))?return;
????return?Element.classNames(element).remove(className);
??},
??//?removes?whitespace-only?text?node?children
??//刪除空白文本節(jié)點(diǎn),使用此方法能夠使得childNodes屬性對(duì)所有瀏覽器兼容,否則ie不認(rèn)為空白文本節(jié)點(diǎn)是子節(jié)點(diǎn)。而firefox則會(huì)認(rèn)為這些節(jié)點(diǎn)是子節(jié)點(diǎn)。
??cleanWhitespace:?function(element)?{
????element?=?$(element);
????for?(var?i?=?0;?i?<?element.childNodes.length;?i++)?{
??????var?node?=?element.childNodes[i];
??????if?(node.nodeType?==?3?&&?!/\S/.test(node.node
value))
????????Element.remove(node);
????}
??},
//判斷一個(gè)節(jié)點(diǎn)是否為空,如果全是空白內(nèi)容,也認(rèn)為空
??empty:?function(element)?{
????return?$(element).innerHTML.match(/^\s*$/);
??},
/*
將滾動(dòng)條滾動(dòng)到指定節(jié)點(diǎn)的位置
*/
??scrollTo:?function(element)?{
????element?=?$(element);
????var?x?=?element.x???element.x?:?element.offsetLeft,
????????y?=?element.y???element.y?:?element.offsetTop;
????window.scrollTo(x,?y);
??},
/*
得到指定節(jié)點(diǎn)的指定樣式的絕對(duì)值。
即可以獲得繼承得到的樣式。
*/
??getStyle:?function(element,?style)?{
????element?=?$(element);
????var?
value?=?element.style[style.camelize()];
????if?(!
value)?{
??????if?(document.defaultView?&&?document.defaultView.getComputedStyle)?{
????????var?css?=?document.defaultView.getComputedStyle(element,?null);
????????
value?=?css???css.getProperty
value(style)?:?null;
??????}?else?if?(element.currentStyle)?{
????????
value?=?element.currentStyle[style.camelize()];
??????}
????}
????if?(window.opera?&&?['left',?'top',?'right',?'bottom'].include(style))
??????if?(Element.getStyle(element,?'position')?==?'static')?
value?=?'auto';
????return?
value?==?'auto'???null?:?
value;
??},
/*
設(shè)置指定節(jié)點(diǎn)的樣式,這里可以由style參數(shù)同時(shí)指定多個(gè)屬性
例如:
Element.setStyle($("someElement"),{color:'#ff0000',background-color:'#000000'});
就將指定節(jié)點(diǎn)的樣式設(shè)置為紅字黑底
*/
??setStyle:?function(element,?style)?{
????element?=?$(element);
????for?(name?in?style)
??????element.style[name.camelize()]?=?style[name];
??},
/*
返回節(jié)點(diǎn)的寬度和高度,以{width:xx,height:xx}形式返回。
該方法使得無論節(jié)點(diǎn)可見與否,都能夠獲取其顯示時(shí)的大小。
*/
??getDimensions:?function(element)?{
????element?=?$(element);
????if?(Element.getStyle(element,?'display')?!=?'none')
??????return?{width:?element.offsetWidth,?height:?element.offsetHeight};
????//?All?*Width?and?*Height?properties?give?0?on?elements?with?display?none,
????//?so?enable?the?element?temporarily
????var?els?=?element.style;
????var?originalVisibility?=?els.visibility;
????var?originalPosition?=?els.position;
????els.visibility?=?'hidden';
????els.position?=?'absolute';
????els.display?=?'';
????var?originalWidth?=?element.clientWidth;
????var?originalHeight?=?element.clientHeight;
????els.display?=?'none';
????els.position?=?originalPosition;
????els.visibility?=?originalVisibility;
????return?{width:?originalWidth,?height:?originalHeight};
??},
/*
使的元素相對(duì)定位
*/
??makePositioned:?function(element)?{
????element?=?$(element);
????var?pos?=?Element.getStyle(element,?'position');
????if?(pos?==?'static'?||?!pos)?{
??????element._madePositioned?=?true;
??????element.style.position?=?'relative';
??????//?Opera?returns?the?offset?relative?to?the?positioning?context,?when?an
??????//?element?is?position?relative?but?top?and?left?have?not?been?defined
??????if?(window.opera)?{
????????element.style.top?=?0;
????????element.style.left?=?0;
??????}
????}
??},
/*
取消節(jié)點(diǎn)的相對(duì)定位。
*/
??undoPositioned:?function(element)?{
????element?=?$(element);
????if?(element._madePositioned)?{
??????element._madePositioned?=?undefined;
??????element.style.position?=
????????element.style.top?=
????????element.style.left?=
????????element.style.bottom?=
????????element.style.right?=?'';
????}
??},
/*
*/
??makeClipping:?function(element)?{
????element?=?$(element);
????if?(element._overflow)?return;
????element._overflow?=?element.style.overflow;
????if?((Element.getStyle(element,?'overflow')?||?'visible')?!=?'hidden')
??????element.style.overflow?=?'hidden';
??},
??undoClipping:?function(element)?{
????element?=?$(element);
????if?(element._overflow)?return;
????element.style.overflow?=?element._overflow;
????element._overflow?=?undefined;
??}
});
var?Toggle?=?new?Object();
Toggle.display?=?Element.toggle;
/*--------------------------------------------------------------------------*/
Abstract.Insertion?=?function(adjacency)?{
??this.adjacency?=?adjacency;
}
Abstract.Insertion.prototype?=?{
??initialize:?function(element,?content)?{
????this.element?=?$(element);
????this.content?=?content.stripScripts();
????if?(this.adjacency?&&?this.element.insertAdjacentHTML)?{
??????try?{
????????this.element.insertAdjacentHTML(this.adjacency,?this.content);
??????}?catch?(e)?{
????????if?(this.element.tagName.toLowerCase()?==?'tbody')?{
??????????this.insertContent(this.contentFromAnonymousTable());
????????}?else?{
??????????throw?e;
????????}
??????}
????}?else?{
??????this.range?=?this.element.ownerDocument.createRange();
??????if?(this.initializeRange)?this.initializeRange();
??????this.insertContent([this.range.createContextualFragment(this.content)]);
????}
????setTimeout(function()?{content.evalScripts()},?10);
??},
??contentFromAnonymousTable:?function()?{
????var?div?=?document.createElement('div');
????div.innerHTML?=?'<table><tbody>'?+?this.content?+?'</tbody></table>';
????return?$A(div.childNodes[0].childNodes[0].childNodes);
??}
}
var?Insertion?=?new?Object();
Insertion.Before?=?Class.create();
Insertion.Before.prototype?=?Object.extend(new?Abstract.Insertion('beforeBegin'),?{
??initializeRange:?function()?{
????this.range.setStartBefore(this.element);
??},
??insertContent:?function(fragments)?{
????fragments.each((function(fragment)?{
??????this.element.parentNode.insertBefore(fragment,?this.element);
????}).bind(this));
??}
});
Insertion.Top?=?Class.create();
Insertion.Top.prototype?=?Object.extend(new?Abstract.Insertion('afterBegin'),?{
??initializeRange:?function()?{
????this.range.selectNodeContents(this.element);
????this.range.collapse(true);
??},
??insertContent:?function(fragments)?{
????fragments.reverse(false).each((function(fragment)?{
??????this.element.insertBefore(fragment,?this.element.firstChild);
????}).bind(this));
??}
});
Insertion.Bottom?=?Class.create();
Insertion.Bottom.prototype?=?Object.extend(new?Abstract.Insertion('beforeEnd'),?{
??initializeRange:?function()?{
????this.range.selectNodeContents(this.element);
????this.range.collapse(this.element);
??},
??insertContent:?function(fragments)?{
????fragments.each((function(fragment)?{
??????this.element.appendChild(fragment);
????}).bind(this));
??}
});
Insertion.After?=?Class.create();
Insertion.After.prototype?=?Object.extend(new?Abstract.Insertion('afterEnd'),?{
??initializeRange:?function()?{
????this.range.setStartAfter(this.element);
??},
??insertContent:?function(fragments)?{
????fragments.each((function(fragment)?{
??????this.element.parentNode.insertBefore(fragment,
????????this.element.nextSibling);
????}).bind(this));
??}
});
/*--------------------------------------------------------------------------*/
Element.ClassNames?=?Class.create();
Element.ClassNames.prototype?=?{
??initialize:?function(element)?{
????this.element?=?$(element);
??},
??_each:?function(iterator)?{
????this.element.className.split(/\s+/).select(function(name)?{
??????return?name.length?>?0;
????})._each(iterator);
??},
??set:?function(className)?{
????this.element.className?=?className;
??},
??add:?function(classNameToAdd)?{
????if?(this.include(classNameToAdd))?return;
????this.set(this.toArray().concat(classNameToAdd).join('?'));
??},
??remove:?function(classNameToRemove)?{
????if?(!this.include(classNameToRemove))?return;
????this.set(this.select(function(className)?{
??????return?className?!=?classNameToRemove;
????}).join('?'));
??},
??toString:?function()?{
????return?this.toArray().join('?');
??}
}
Object.extend(Element.ClassNames.prototype,?Enumerable);
var?Field?=?{
??clear:?function()?{
????for?(var?i?=?0;?i?<?arguments.length;?i++)
??????$(arguments[i]).
value?=?'';
??},
??focus:?function(element)?{
????$(element).focus();
??},
??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?=?$(element);
????element.focus();
????if?(element.select)
??????element.select();
??}
}
/*--------------------------------------------------------------------------*/
var?Form?=?{
??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)?{
????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;
??},
??getInputs:?function(form,?typeName,?name)?{
????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?=?'';
????}
??},
??findFirstElement:?function(form)?{
????return?Form.getElements(form).find(function(element)?{
??????return?element.type?!=?'hidden'?&&?!element.disabled?&&
????????['input',?'select',?'textarea'].include(element.tagName.toLowerCase());
????});
??},
??focusFirstElement:?function(form)?{
????Field.activate(Form.findFirstElement(form));
??},
??reset:?function(form)?{
????$(form).reset();
??}
}
Form.Element?=?{
??serialize:?function(element)?{
????element?=?$(element);
????var?method?=?element.tagName.toLowerCase();
????var?parameter?=?Form.Element.Serializers[method](element);
????if?(parameter)?{
??????var?key?=?encodeURIComponent(parameter[0]);
??????if?(key.length?==?0)?return;
??????if?(parameter[1].constructor?!=?Array)
????????parameter[1]?=?[parameter[1]];
??????return?parameter[1].map(function(
value)?{
????????return?key?+?'='?+?encodeURIComponent(
value);
??????}).join('&');
????}
??},
??get
value:?function(element)?{
????element?=?$(element);
????var?method?=?element.tagName.toLowerCase();
????var?parameter?=?Form.Element.Serializers[method](element);
????if?(parameter)
??????return?parameter[1];
??}
}
Form.Element.Serializers?=?{
//對(duì)input標(biāo)記序列化
??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;
??},
//對(duì)單選框和復(fù)選框序列化
??inputSelector:?function(element)?{
????if?(element.checked)
??????return?[element.name,?element.
value];
??},
//對(duì)文本框序列化
??textarea:?function(element)?{
????return?[element.name,?element.
value];
??},
//對(duì)下拉列表框序列化
??select:?function(element)?{
????return?Form.Element.Serializers[element.type?==?'select-one'??
??????'selectOne'?:?'selectMany'](element);
??},
//對(duì)單選下拉列表框序列化
??selectOne:?function(element)?{
????var?
value?=?'',?opt,?index?=?element.selectedIndex;
????if?(index?>=?0)?{
??????opt?=?element.options[index];
??????
value?=?opt.
value;
??????if?(!
value?&&?!('
value'?in?opt))
????????
value?=?opt.text;
????}
????return?[element.name,?
value];
??},
//對(duì)多選下拉列表框序列化
??selectMany:?function(element)?{
????var?
value?=?new?Array();
????for?(var?i?=?0;?i?<?element.length;?i++)?{
??????var?opt?=?element.options[i];
??????if?(opt.selected)?{
????????var?opt
value?=?opt.
value;
????????if?(!opt
value?&&?!('
value'?in?opt))
??????????opt
value?=?opt.text;
????????
value.push(opt
value);
??????}
????}
????return?[element.name,?
value];
??}
}
/*--------------------------------------------------------------------------*/
var?$F?=?Form.Element.get
value;
/*--------------------------------------------------------------------------*/
Abstract.TimedObserver?=?function()?{}
Abstract.TimedObserver.prototype?=?{
??initialize:?function(element,?frequency,?callback)?{
????this.frequency?=?frequency;
????this.element???=?$(element);
????this.callback??=?callback;
????this.last
value?=?this.get
value();
????this.registerCallback();
??},
??registerCallback:?function()?{
????setInterval(this.onTimerEvent.bind(this),?this.frequency?*?1000);
??},
??onTimerEvent:?function()?{
????var?
value?=?this.get
value();
????if?(this.last
value?!=?
value)?{
??????this.callback(this.element,?
value);
??????this.last
value?=?
value;
????}
??}
}
Form.Element.Observer?=?Class.create();
Form.Element.Observer.prototype?=?Object.extend(new?Abstract.TimedObserver(),?{
??get
value:?function()?{
????return?Form.Element.get
value(this.element);
??}
});
Form.Observer?=?Class.create();
Form.Observer.prototype?=?Object.extend(new?Abstract.TimedObserver(),?{
??get
value:?function()?{
????return?Form.serialize(this.element);
??}
});
/*--------------------------------------------------------------------------*/
Abstract.EventObserver?=?function()?{}
Abstract.EventObserver.prototype?=?{
??initialize:?function(element,?callback)?{
????this.element??=?$(element);
????this.callback?=?callback;
????this.last
value?=?this.get
value();
????if?(this.element.tagName.toLowerCase()?==?'form')
??????this.registerFormCallbacks();
????else
??????this.registerCallback(this.element);
??},
??onElementEvent:?function()?{
????var?
value?=?this.get
value();
????if?(this.last
value?!=?
value)?{
??????this.callback(this.element,?
value);
??????this.last
value?=?
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())?{
????????case?'checkbox':
????????case?'radio':
??????????Event.observe(element,?'click',?this.onElementEvent.bind(this));
??????????break;
????????case?'password':
????????case?'text':
????????case?'textarea':
????????case?'select-one':
????????case?'select-multiple':
??????????Event.observe(element,?'change',?this.onElementEvent.bind(this));
??????????break;
??????}
????}
??}
}
Form.Element.EventObserver?=?Class.create();
Form.Element.EventObserver.prototype?=?Object.extend(new?Abstract.EventObserver(),?{
??get
value:?function()?{
????return?Form.Element.get
value(this.element);
??}
});
Form.EventObserver?=?Class.create();
Form.EventObserver.prototype?=?Object.extend(new?Abstract.EventObserver(),?{
??get
value:?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)));
??},
??pointerX:?function(event)?{
????return?event.pageX?||?(event.clientX?+
??????(document.documentElement.scrollLeft?||?document.body.scrollLeft));
??},
??pointerY:?function(event)?{
????return?event.pageY?||?(event.clientY?+
??????(document.documentElement.scrollTop?||?document.body.scrollTop));
??},
??stop:?function(event)?{
????if?(event.preventDefault)?{
??????event.preventDefault();
??????event.stopPropagation();
????}?else?{
??????event.return
value?=?false;
??????event.cancelBubble?=?true;
????}
??},
??//?find?the?first?node?with?the?given?tagName,?starting?from?the
??//?node?the?event?was?triggered?on;?traverses?the?DOM?upwards
??findElement:?function(event,?tagName)?{
????var?element?=?Event.element(event);
????while?(element.parentNode?&&?(!element.tagName?||
????????(element.tagName.toUpperCase()?!=?tagName.toUpperCase())))
??????element?=?element.parentNode;
????return?element;
??},
??observers:?false,
??_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++)?{
??????Event.stopObserving.apply(this,?Event.observers[i]);
??????Event.observers[i][0]?=?null;
????}
????Event.observers?=?false;
??},
??observe:?function(element,?name,?observer,?useCapture)?{
????var?element?=?$(element);
????useCapture?=?useCapture?||?false;
????if?(name?==?'keypress'?&&
????????(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
????????||?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.match(/Konqueror|Safari|KHTML/)
????????||?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?*/
Event.observe(window,?'unload',?Event.unloadCache,?false);
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];
??},
??positionedOffset:?function(element)?{
????var?
valueT?=?0,?
valueL?=?0;
????do?{
??????
valueT?+=?element.offsetTop??||?0;
??????
valueL?+=?element.offsetLeft?||?0;
??????element?=?element.offsetParent;
??????if?(element)?{
????????p?=?Element.getStyle(element,?'position');
????????if?(p?==?'relative'?||?p?==?'absolute')?break;
??????}
????}?while?(element);
????return?[
valueL,?
valueT];
??},
??offsetParent:?function(element)?{
????if?(element.offsetParent)?return?element.offsetParent;
????if?(element?==?document.body)?return?element;
????while?((element?=?element.parentNode)?&&?element?!=?document.body)
??????if?(Element.getStyle(element,?'position')?!=?'static')
????????return?element;
????return?document.body;
??},
??//?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
??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';
??},
??page:?function(forElement)?{
????var?
valueT?=?0,?
valueL?=?0;
????var?element?=?forElement;
????do?{
??????
valueT?+=?element.offsetTop??||?0;
??????
valueL?+=?element.offsetLeft?||?0;
??????//?Safari?fix
??????if?(element.offsetParent==document.body)
????????if?(Element.getStyle(element,'position')=='absolute')?break;
????}?while?(element?=?element.offsetParent);
????element?=?forElement;
????do?{
??????
valueT?-=?element.scrollTop??||?0;
??????
valueL?-=?element.scrollLeft?||?0;
????}?while?(element?=?element.parentNode);
????return?[
valueL,?
valueT];
??},
??clone:?function(source,?target)?{
????var?options?=?Object.extend({
??????setLeft:????true,
??????setTop:?????true,
??????setWidth:???true,
??????setHeight:??true,
??????offsetTop:??0,
??????offsetLeft:?0
????},?arguments[2]?||?{})
????//?find?page?position?of?source
????source?=?$(source);
????var?p?=?Position.page(source);
????//?find?coordinate?system?to?use
????target?=?$(target);
????var?delta?=?[0,?0];
????var?parent?=?null;
????//?delta?[0,0]?will?do?fine?with?position:?fixed?elements,
????//?position:absolute?needs?offsetParent?deltas
????if?(Element.getStyle(target,'position')?==?'absolute')?{
??????parent?=?Position.offsetParent(target);
??????delta?=?Position.page(parent);
????}
????//?correct?by?body?offsets?(fixes?Safari)
????if?(parent?==?document.body)?{
??????delta[0]?-=?document.body.offsetLeft;
??????delta[1]?-=?document.body.offsetTop;
????}
????//?set?position
????if(options.setLeft)???target.style.left??=?(p[0]?-?delta[0]?+?options.offsetLeft)?+?'px';
????if(options.setTop)????target.style.top???=?(p[1]?-?delta[1]?+?options.offsetTop)?+?'px';
????if(options.setWidth)??target.style.width?=?source.offsetWidth?+?'px';
????if(options.setHeight)?target.style.height?=?source.offsetHeight?+?'px';
??},
??absolutize:?function(element)?{
????element?=?$(element);
????if?(element.style.position?==?'absolute')?return;
????Position.prepare();
????var?offsets?=?Position.positionedOffset(element);
????var?top?????=?offsets[1];
????var?left????=?offsets[0];
????var?width???=?element.clientWidth;
????var?height??=?element.clientHeight;
????element._originalLeft???=?left?-?parseFloat(element.style.left??||?0);
????element._originalTop????=?top??-?parseFloat(element.style.top?||?0);
????element._originalWidth??=?element.style.width;
????element._originalHeight?=?element.style.height;
????element.style.position?=?'absolute';
????element.style.top????=?top?+?'px';;
????element.style.left???=?left?+?'px';;
????element.style.width??=?width?+?'px';;
????element.style.height?=?height?+?'px';;
??},
??relativize:?function(element)?{
????element?=?$(element);
????if?(element.style.position?==?'relative')?return;
????Position.prepare();
????element.style.position?=?'relative';
????var?top??=?parseFloat(element.style.top??||?0)?-?(element._originalTop?||?0);
????var?left?=?parseFloat(element.style.left?||?0)?-?(element._originalLeft?||?0);
????element.style.top????=?top?+?'px';
????element.style.left???=?left?+?'px';
????element.style.height?=?element._originalHeight;
????element.style.width??=?element._originalWidth;
??}
}
//?Safari?returns?margins?on?body?which?is?incorrect?if?the?child?is?absolutely
//?positioned.??For?performance?reasons,?redefine?Position.cumulativeOffset?for
//?KHTML/WebKit?only.
if?(/Konqueror|Safari|KHTML/.test(navigator.userAgent))?{
??Position.cumulativeOffset?=?function(element)?{
????var?
valueT?=?0,?
valueL?=?0;
????do?{
??????
valueT?+=?element.offsetTop??||?0;
??????
valueL?+=?element.offsetLeft?||?0;
??????if?(element.offsetParent?==?document.body)
????????if?(Element.getStyle(element,?'position')?==?'absolute')?break;
??????element?=?element.offsetParent;
????}?while?(element);
????return?[
valueL,?
valueT];
??}
}[/code]