prototype框架最早是出于方便Ruby開發人員進行javascript開發所構建的,從這個版本上更加體現的淋漓盡致。
比起1.3.1版本,1.4.0中的編程思想和技巧更加令人拍案叫絕,對于開拓編程思路很有幫助。

該版本主要加入了迭代器思想,也是Ruby中的一個核心概念,從而使用此框架進行javascript開發幾乎可以避免for循環的使用。
/*----------------------------------------------------------------------------------------------------*/


/*
定義prototype對象,告知版本信息,有利于程序的自動檢測
ScriptFragment是正則表達式,用于捕獲字符串中的<script>標記及其中的內容
emptyFunction:空函數
K:返回參數自身的函數,后面會有應用

在這里使用了直接定義對象的語法:
var?obj={
property1:value1,
property2:value2,
....
}
后面會經常用到
*/
var?Prototype?=?{
??Version:?'1.4.0',
??ScriptFragment:?'(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',

??emptyFunction:?function()?{},
??K:?function(x)?{return?x}
}

/*
定義創建類的模式,使用此模式創建的類能夠實現構造函數
其中initialize是一個抽象方法,apply使得能對其保持參數。
如果直接調用this.initialize(arguments),則整個參數數組作為了一個參數。
*/
var?Class?=?{
??create:?function()?{
????return?function()?{
??????this.initialize.apply(this,?arguments);
????}
??}
}

//表示命名空間或者抽象類的東西,使代碼邏輯更加清楚
var?Abstract?=?new?Object();

/*
將source的所有屬性復制到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這里少了下面的函數:
Object.prototype.extend?=?function(object)?{
??return?Object.extend.apply(this,?[this,?object]);
}
所以原先基于1.3.1框架的js腳本升級到1.4.0時會產生兼容性問題。只要在1.4.0里加上上述函數即可。
去掉的原因大概因為為每個object都增加extend方法顯的很浪費,畢竟95%的對象是不會用到的。
而且增加了extend方法也為反射枚舉帶來一定的麻煩,這從后面Hash對象的用法可以看到。
*/

/*
將對象轉換為字符串,這里能夠更詳細一些,只要對象自定義了inspect函數。而不是原來對象的toString總是[object]。
例如后面對數組定義了inspect函數,使得
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;
??}
}

/*
一個很重要的方法,能夠將函數綁定到某個對象運行
和1.3.1版本相比,原來不能在綁定的時候就添加參數,而現在可以。
例如:
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)));
??}
}
/*
將函數作為對象的事件監聽器,這樣可以產生獨立而且通用的事件處理程序,例如要對單擊事件進行處理:
function?clickHandler(element){
//處理element的單擊事件
}

假設有節點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);
??}
}

/*
所有的數字類型都是Number類的實例,下面就是給Number類定義一些方法
*/
Object.extend(Number.prototype,?{
/*
將數字轉換為顏色的形式
*/
??toColorPart:?function()?{
????var?digits?=?this.toString(16);
????if?(this?<?16)?return?'0'?+?digits;
????return?digits;
??},
//加1
??succ:?function()?{
????return?this?+?1;
??},
/*
執行指定次數的循環,例如獲取10個隨機數
var?ran=[]
var?c=10;
c.times(function(){
ran.push(Math.random());
});
$R是ObjectRange對象的快捷創建形式,后面會有介紹。
*/
??times:?function(iterator)?{
????$R(0,?this,?true).each(iterator);
????return?this;
??}
});

/*
Try對象,僅有一個方法these
*/
var?Try?=?{
/*
根據參數指定的函數進行調用,返回第一個調用成功的值
在后面跨瀏覽器建立XMLHttpRequest對象時就用到了。
如果所有都不成功則返回undefined
*/
??these:?function()?{
????var?returnvalue;

????for?(var?i?=?0;?i?<?arguments.length;?i++)?{
??????var?lambda?=?arguments[i];
??????try?{
????????returnvalue?=?lambda();
????????break;
??????}?catch?(e)?{}
????}

????return?returnvalue;
??}
}

/*--------------------------------------------------------------------------*/


/*
定時器類,比起window.setInterval函數,該類能夠使得回調函數不會被并發調用,見onTimerEvent的注釋。
*/
var?PeriodicalExecuter?=?Class.create();
PeriodicalExecuter.prototype?=?{
/*
構造函數,指定回調函數和執行頻率,單位為秒
*/
??initialize:?function(callback,?frequency)?{
????this.callback?=?callback;
????this.frequency?=?frequency;
????this.currentlyExecuting?=?false;
????this.registerCallback();
??},
/*
開始執行定時器,一般不要顯示調用,在構造函數中被調用
注意這里寫為:
this.onTimerEvent.bind(this)
如果寫為:
this.onTimerEvent
則onTimerEvent中的函數的this指針將指向window對象,即setInterval的默認對象。
*/
??registerCallback:?function()?{
????setInterval(this.onTimerEvent.bind(this),?this.frequency?*?1000);
??},
/*
相當于回調函數的一個代理。
在傳統的setInterval函數中,時間一到,便強制執行回調函數,而這里加入了currentlyExecuting屬性判斷,
則如果callback函數的執行時間超過了一個時間片,則阻止其被重復執行。
*/
??onTimerEvent:?function()?{
????if?(!this.currentlyExecuting)?{
??????try?{
????????this.currentlyExecuting?=?true;
????????this.callback();
??????}?finally?{
????????this.currentlyExecuting?=?false;
??????}
????}
??}
}

/*--------------------------------------------------------------------------*/
/*
很方便的一個快速鏈接函數,能夠獲得參數所指定的頁面節點,如果有多個參數則返回數組。
參數的形式既可以是節點的id值,也可以是節點的引用,即$($("someId"))和$("someId")是等價的;
*/
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;
}

/*
為字符串對象添加方法,和前面為Number添加方法的原理相同
*/
Object.extend(String.prototype,?{
/*
將Html轉換為純文本,例如:
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'),?'');
??},
//提取字符串中的腳本,返回所有腳本內容組成的數組
??extractScripts:?function()?{
????var?matchAll?=?new?RegExp(Prototype.ScriptFragment,?'img');//先找到所有包括<script>的代碼標記
????var?matchOne?=?new?RegExp(Prototype.ScriptFragment,?'im'); //再對每個腳本刪除<script>標記
????return?(this.match(matchAll)?||?[]).map(function(scriptTag)?{
??????return?(scriptTag.match(matchOne)?||?['',?''])[1];
????});
??},
//先提取字符串中的腳本塊,再執行這些腳本
??evalScripts:?function()?{
????return?this.extractScripts().map(eval);
??},
/*
利用瀏覽器本身的機制對Html字符串進行編碼,例如將<轉換為&lt;
*/
??escapeHTML:?function()?{
????var?div?=?document.createElement('div');
????var?text?=?document.createTextNode(this);
????div.appendChild(text);
????return?div.innerHTML;
??},
/*
對Html進行解碼
*/
??unescapeHTML:?function()?{
????var?div?=?document.createElement('div');
????div.innerHTML?=?this.stripTags();
????return?div.childNodes[0]???div.childNodes[0].nodevalue?:?'';
??},
//獲取查詢字符串數組,例如通過document.location.toQueryParams()就可以得到由鍵和值組成的哈希表(用對象表示)。
??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;
????});
??},
//將字符串轉換為字符數組
??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是觀察的意思。這里大概就是將字符串轉換為可觀察的形式。這里將轉義字符寫成轉義前的字符串形式,
例如:
var?s="abc\ndef";

alert(s);
將得到兩行字符串,上一行是abc,下一行是def

alert(s.inspect());
將得到abc\ndef
即給字符串賦值時的形式,這和數組的inspect作用類似。
*/
??inspect:?function()?{
????return?"'"?+?this.replace('\\',?'\\\\').replace("'",?'\\\'')?+?"'";
??}
});

//做一個名稱鏈接
String.prototype.parseQuery?=?String.prototype.toQueryParams;

//定義了兩個異常對象,主要用于迭代控制
var?$break????=?new?Object();
var?$continue?=?new?Object();

/*
這是一個非常Ruby的機制,事實上可以將Enumerable看作一個枚舉接口,
而_each是必須實現的方法,只要實現了此方法的類,都可以調用接口類中的其他成員。
例如后面Array就實現了此接口,也是最典型的應用
*/
var?Enumerable?=?{
/*
對可枚舉對象的每個成員調用iterator(迭代器)方法,
如果迭代器方法拋出$continue異常,則繼續執行,如果拋出$break異常,則不再繼續迭代

其中調用了_each這個抽象方法,
_each是由具體的繼承于Enumerable的類實現的

index計數器的作用是用于告訴迭代器當前執行到第幾個元素,是迭代器可選實現的。
*/
??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;
????}
??},
/*
判斷枚舉對象中的所有元素是否都能使得迭代器返回true。如果沒有指定迭代器,則判斷所有元素是否都對應于布爾類型的true
如果所有都滿足,則返回true;否則返回false;
注意這里就使用了$break異常,用于實現“邏輯與”操作的短路效果
另外值得注意的一個技巧是使用了!!將一個變量強制轉換為布爾類型,可以參考: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;
},
/*
判斷枚舉對象中的所有元素是否有滿足指定迭代器的值(返回true),如果有則返回true,否則返回false
其原理和all方法類似

如果數組為空,仍然返回true,這一點有點匪夷所思。
*/
??any:?function(iterator)?{
????var?result?=?true;
????this.each(function(value,?index)?{
??????if?(result?=?!!(iterator?||?Prototype.K)(value,?index))
????????throw?$break;
????});
????return?result;
??},
/*
返回所有枚舉元素通過迭代器執行的結果,作為數組返回
*/
??collect:?function(iterator)?{
????var?results?=?[];
????this.each(function(value,?index)?{
??????results.push(iterator(value,?index));
????});
????return?results;
??},
/*
返回第一個能夠使得迭代器返回true的枚舉元素的值,如果沒有true,則返回"undefined",即result未被賦值
這有可能是作者考慮的一個小失誤,畢竟返回"undefined"并不是一個好的風格(僅是猜測)
*/
??detect:?function?(iterator)?{
????var?result;
????this.each(function(value,?index)?{
??????if?(iterator(value,?index))?{
????????result?=?value;
????????throw?$break;
??????}
????});
????return?result;
??},
/*
返回所有能夠使得迭代器返回true的枚舉元素,作為數組返回。
*/
??findAll:?function(iterator)?{
????var?results?=?[];
????this.each(function(value,?index)?{
??????if?(iterator(value,?index))
????????results.push(value);
????});
????return?results;
??},
/*
grep是unix類操作系統下的一個經典命令,而這里則是javascript的一個類似實現
pattern是正則模式,對所有符合此模式的枚舉元素進行迭代器運算,并將運算結果保存到數組中并返回。
需要注意,這里的iterator參數是可選的,此時僅僅對枚舉元素進行模式匹配,返回所有的匹配結果
*/
??grep:?function(pattern,?iterator)?{
????var?results?=?[];
????this.each(function(value,?index)?{
??????var?stringvalue?=?value.toString();
??????if?(stringvalue.match(pattern))
????????results.push((iterator?||?Prototype.K)(value,?index));
????})
????return?results;
??},
/*
判斷枚舉對象中是否包含指定值的枚舉元素,這里仍然使用了each方法,而不是循環,可見prototype致力于提供一種ruby化的編程方式,
如果用循環實現,則是類似于以下的代碼:
for(var?i=0;i<this.length;i++){
if(this[i]==object)return?true;
}
而該函數中,定義了迭代器:
function(value)?{
??????if?(value?==?object)?{
????????found?=?true;
????????throw?$break;
??????}
????}
這個迭代器作為each方法的參數。

*/
??include:?function(object)?{
????var?found?=?false;
????this.each(function(value)?{
??????if?(value?==?object)?{
????????found?=?true;
????????throw?$break;
??????}
????});
????return?found;
??},

/*
字面意思是“注入”,其作用相當于將memo作為聯系各個迭代器的全局變量,每次迭代都對其進行操作,返回操作的最后結果。例如對于數組:
var?arr=[1,2,3];
現在想將其字符串化為:123
如果不調用join方法,傳統做法是:
var?s="";
for(var?i=0;i<arr.length;i++){
s+=arr[i];
}
現在通過調用inject函數,則:
var?s=arr.inject("",function(memo,value){return?memo+value});
兩者運行的結果是完全相同的。
*/
??inject:?function(memo,?iterator)?{
????this.each(function(value,?index)?{
??????memo?=?iterator(memo,?value,?index);
????});
????return?memo;
??},

/*
在所有枚舉元素上調用method方法,并可以給這個方法傳遞參數
返回所有method的執行結果,作為數組返回
*/
??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;
??},
/*
按照迭代器的返回結果,將枚舉元素分為兩個數組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;
??},

/*
返回所有迭代器執行結果為false的枚舉元素
*/
??reject:?function(iterator)?{
????var?results?=?[];
????this.each(function(value,?index)?{
??????if?(!iterator(value,?index))
????????results.push(value);
????});
????return?results;
??},
/*
結構復雜的一個函數,作用是根據迭代器iterator的結果對枚舉元素進行排序。使iterator執行結果小的元素排在前面。
主要包括三個函數的調用:
1。collect方法,返回的每個數組元素包括:值和迭代器運行該值的結果,用{value:value,criteria:iterator(value,index)}得到
2。對collect返回的數組執行sort方法,這時數組對象內置的對象,參數是一個委托函數,用于指定排序規則。其標準是對迭代器返回的值排序,小的在前面
3。對sort的結果執行pluck方法,即返回value屬性的值,于是最后還是返回的枚舉對象中的原有值,只是根據迭代器iterator的結果對這些元素進行排序
*/
??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');
??},

/*
將枚舉對象轉換為數組,使用了collect方法和Prototype.K函數,減少了重復代碼
*/
??toArray:?function()?{
????return?this.collect(Prototype.K);
??},
/*
壓縮函數,實現復雜,作用尚不能體會-_-。
接收的參數需要是可枚舉對象,可以有多個參數。最后一個參數是迭代器,可選。
作用是將自身和參數組成的二維陣列進行行列對換,并切除多余的數據,或補充缺少的數據(用undefined)。切換后的行數由調用者中元素的個數決定,而列數是數組參數的個數加1。
每個數組參數的第一個元素順序組成第一行,第二個元素順序組成第二行,依次類推。直到調用者中的元素用完為止。
迭代器的作用就是對轉換后的每一行進行一次運算。
例如:
var?arr1=[1,2,3];
var?arr2=[4,5,6];
var?arr3=[7,8,9];
var?arr=arr1.zip(arr2,arr3);

//使用迭代器輸出結果,inspect用于輸出數組語法表示的數組字符串,后面有介紹
arr.each(function(s){
document.write(s.inspect());
document.write("<br/>");
}
);
得到的結果為:
[1,?4,?7]
[2,?5,?8]
[3,?6,?9]

如果讓arr1=[1,2],其他不變,則執行結果為:
[1,?4,?7]
[2,?5,?8]

*/
??zip:?function()?{
????var?iterator?=?Prototype.K,?args?=?$A(arguments);
????if?(typeof?args.last()?==?'function')
??????iterator?=?args.pop();
//將自身枚舉對象作為一個元素,與參數(也是可枚舉的)組成一個數組,并將枚舉對象轉換為數組(通過$A迭代器)
????var?collections?=?[this].concat(args).map($A);

????return?this.map(function(value,?index)?{
??????iterator(value?=?collections.pluck(index));
??????return?value;
????});
??},
/*
這實際上這是一個待實現的抽象方法,在Array對象中有對其進行的重定義
所以將this轉換為數組(toArray()),再調用inspect。
對于非數組形式的枚舉對象,則會加上'#<Enumerable:....>'這樣的形式
*/
??inspect:?function()?{
????return?'#<Enumerable:'?+?this.toArray().inspect()?+?'>';
??}
}
//對Enumerable基類的一些方法做了快速鏈接
Object.extend(Enumerable,?{
??map:?????Enumerable.collect,
??find:????Enumerable.detect,
??select:??Enumerable.findAll,
??member:??Enumerable.include,
??entries:?Enumerable.toArray
});

/*
將一個對象轉換為數組。
對于字符串則直接變為字符數組,例如$A("abc")將得到,["a","b","c"]
否則集合對象變為數組,這類對象包括函數的參數集合arguments,<select>的options集合,
<form>的elements集合等等,一個節點的所有子結點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;
}
}
/*
讓數組繼承于Enumarable對象(基類)
*/
Object.extend(Array.prototype,?Enumerable);
/*
做一個鏈接,prototype中一般私有的成員或抽象成員都用下劃線開頭,這里的_reverse大概就是起一個說明性的作用,將其作為抽象方法使用。
*/
Array.prototype._reverse?=?Array.prototype.reverse;

/*
為數組對象添加一些快捷方法
*/
Object.extend(Array.prototype,?{
/*
迭代器方法,源于Ruby中的迭代器用法
_each方法的作用就是將數組的每個元素作為iterator函數的參數,并執行iterator方法。例如對于數組:var?arr=[1,2,3,4,5,6];
如果要顯示其中的每個元素,通常的做法是
for(var?i=0;i<arr.length;i++){
alert(arr[i]);
}
而使用此方法則:
arr._each(function(s){alert(s)});
因此,在Ruby的代碼中很少出現循環,這個函數使得javascript同樣也能夠實現。
*/
??_each:?function(iterator)?{
????for?(var?i?=?0;?i?<?this.length;?i++)
??????iterator(this[i]);
??},

//清空數組
??clear:?function()?{
????this.length?=?0;
????return?this;
??},
//獲取第一個元素的值
??first:?function()?{
????return?this[0];
??},
//獲取最后一個元素的值
??last:?function()?{
????return?this[this.length?-?1];
??},

/*
用于刪除一個數組中的未定義值和null值
這里的select是從Emurable中繼承的方法,而select又是findAll函數的別名
*/
??compact:?function()?{
????return?this.select(function(value)?{
??????return?value?!=?undefined?||?value?!=?null;
????});
??},
/*
將一個枚舉對象中的所有數組元素全部展開,最后返回一個數組,是一個遞歸的過程
*/
??flatten:?function()?{
????return?this.inject([],?function(array,?value)?{
??????return?array.concat(value.constructor?==?Array??
????????value.flatten()?:?[value]);
????});
??},
/*
從數組中刪除參數指定的元素,返回刪除后的結果
*/
??without:?function()?{
????var?values?=?$A(arguments);
????return?this.select(function(value)?{
??????return?!values.include(value);
????});
??},
/*
返回一個元素在數組中的索引
*/
??indexOf:?function(object)?{
????for?(var?i?=?0;?i?<?this.length;?i++)
??????if?(this[i]?==?object)?return?i;
????return?-1;
??},
/*
將數組元素順序逆轉,inline用于確保是數組
*/
??reverse:?function(inline)?{
????return?(inline?!==?false???this?:?this.toArray())._reverse();
??},
/*
取出數組的第一個元素并返回
*/
??shift:?function()?{
????var?result?=?this[0];
????for?(var?i?=?0;?i?<?this.length?-?1;?i++)
??????this[i]?=?this[i?+?1];
????this.length--;
????return?result;
??},
/*
返回數組的字符串表示
*/
??inspect:?function()?{
????return?'['?+?this.map(Object.inspect).join(',?')?+?']';
??}
});
/*
定義哈希對象的通用操作
*/
var?Hash?=?{
/*
實現可枚舉接口。
對hash對象中的每個元素進行迭代操作,迭代器被認為接收一個數組參數,數組的第一個元素是key,第二個元素是value
同時,此數組對象還增加了兩個屬性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);
????}
??},
/*
返回所有的鍵組成的數組
*/
??keys:?function()?{
????return?this.pluck('key');
??},
/*
返回所有的值組成的數組
*/
??values:?function()?{
????return?this.pluck('value');
??},
/*
將兩個hash對象合并,如果鍵相同,則用參數中相應鍵對應的值覆蓋調用者的。
*/
??merge:?function(hash)?{
????return?$H(hash).inject($H(this),?function(mergedHash,?pair)?{
??????mergedHash[pair.key]?=?pair.value;
??????return?mergedHash;
????});
??},
/*
將hash對象轉換為查詢字符串表示的形式
*/
??toQueryString:?function()?{
????return?this.map(function(pair)?{
??????return?pair.map(encodeURIComponent).join('=');
????}).join('&');
??},
/*
獲取hash對象的字符串表示
*/
??inspect:?function()?{
????return?'#<Hash:{'?+?this.map(function(pair)?{
??????return?pair.map(Object.inspect).join(':?');
????}).join(',?')?+?'}>';
??}
}

/*
將一個對象轉換為哈希對象,對象的屬性名(方法名)作為key,值作為value
同時hash對象也是一個可枚舉對象
*/
function?$H(object)?{
/*
object?||?{}使得參數為空時也能夠創建一個hash對象
*/
var?hash?=?Object.extend({},?object?||?{});
Object.extend(hash,?Enumerable);
Object.extend(hash,?Hash);
return?hash;
}

/*
又一個實現Enumerable接口的對象。
有了這個類,基本上就可以完全避免使用for循環了,一個例子,計算1到100的和:
傳統寫法:
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,?{
/*
構造函數,start表示開始的位置,end表示結束的位置,exclusive表示是否排除最后一個索引位置
exclusive=true時對應于:
for(var?i=start;i<end;i++){
//語句
}
exclusive=false時對應于:
for(var?i=start;i<=end;i++){
//語句
}
*/
??initialize:?function(start,?end,?exclusive)?{
????this.start?=?start;
????this.end?=?end;
????this.exclusive?=?exclusive;
??},
/*
實現枚舉接口的_each方法
相當于:
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;
??}
});
/*
做一個快速鏈接,用于生成ObjectRange對象
*/
var?$R?=?function(start,?end,?exclusive)?{
??return?new?ObjectRange(start,?end,?exclusive);
}

/*
封裝XMLHttpRequest的相關操作
*/
var?Ajax?=?{
//瀏覽器兼容的獲取XMLHttpRequest對象的函數
getTransport:?function()?{
return?Try.these(
function()?{return?new?ActiveXObject('Msxml2.XMLHTTP')},
function()?{return?new?ActiveXObject('Microsoft.XMLHTTP')},
function()?{return?new?XMLHttpRequest()}
)?||?false;
},
//當前激活的請求數目
activeRequestCount:?0
}

/*
Ajax的返回值
*/
Ajax.Responders?=?{
??responders:?[],

//實現枚舉接口的_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?=?{
/*
設置XMLHttp調用的參數,提供了默認值:method:'post',異步,無參數
*/
??setOptions:?function(options)?{
????this.options?=?{
??????method:???????'post',
??????asynchronous:?true,
??????parameters:???''
????}
????Object.extend(this.options,?options?||?{});
??},
/*
判斷請求是否成功
*/
??responseIsSuccess:?function()?{
????return?this.transport.status?==?undefined
????????||?this.transport.status?==?0
????????||?(this.transport.status?>=?200?&&?this.transport.status?<?300);
??},
/*
判斷請求是否失敗
*/
??responseIsFailure:?function()?{
????return?!this.responseIsSuccess();
??}
}

//定義一個Ajax請求類
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節點通用的操作
if?(!window.Element)?{
??var?Element?=?new?Object();
}


Object.extend(Element,?{
/*
判斷節點是否可見
*/
??visible:?function(element)?{
????return?$(element).style.display?!=?'none';
??},
/*
切換節點的可見狀態
*/
??toggle:?function()?{
????for?(var?i?=?0;?i?<?arguments.length;?i++)?{
??????var?element?=?$(arguments[i]);
??????Element[Element.visible(element)???'hide'?:?'show'](element);
????}
??},
/*
隱藏參數所指定的節點
*/
??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);
??},
/*
用指定html填充element表示的節點
setTimeout是極具技巧的用法,讓人驚嘆。
update函數之所以會取代:element.innerHTML=html的用法,主要因為它實現了瀏覽器的兼容性:
(1)對于IE,如果給innerHTML賦值的字符串中含有腳本標記,腳本是被忽略的,不起作用;而firefox則會執行腳本;
(2)setTimeout使得可以在函數內可以通過eval定義全局函數,這是由于setTimeout的默認空間就是全局空間決定的(它是window對象的方法,而所有全局變量和全局函數實際上都是window對象的屬性和方法)。
*/
??update:?function(element,?html)?{
????$(element).innerHTML?=?html.stripScripts();
????setTimeout(function()?{html.evalScripts()},?10);
??},
//獲取節點的高度
??getHeight:?function(element)?{
????element?=?$(element);
????return?element.offsetHeight;
??},
//獲取一個元素的class,返回一個數組,包括了所有的class名稱,ClassNames后面實現
??classNames:?function(element)?{
????return?new?Element.ClassNames(element);
??},
/*判斷一個元素是否具有指定的class值*/
??hasClassName:?function(element,?className)?{
????if?(!(element?=?$(element)))?return;
????return?Element.classNames(element).include(className);
??},
//為一個節點添加class名稱
??addClassName:?function(element,?className)?{
????if?(!(element?=?$(element)))?return;
????return?Element.classNames(element).add(className);
??},
//從一個節點移除一個class名稱
??removeClassName:?function(element,?className)?{
????if?(!(element?=?$(element)))?return;
????return?Element.classNames(element).remove(className);
??},

??//?removes?whitespace-only?text?node?children
??//刪除空白文本節點,使用此方法能夠使得childNodes屬性對所有瀏覽器兼容,否則ie不認為空白文本節點是子節點。而firefox則會認為這些節點是子節點。
??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.nodevalue))
????????Element.remove(node);
????}
??},
//判斷一個節點是否為空,如果全是空白內容,也認為空
??empty:?function(element)?{
????return?$(element).innerHTML.match(/^\s*$/);
??},
/*
將滾動條滾動到指定節點的位置
*/
??scrollTo:?function(element)?{
????element?=?$(element);
????var?x?=?element.x???element.x?:?element.offsetLeft,
????????y?=?element.y???element.y?:?element.offsetTop;
????window.scrollTo(x,?y);
??},
/*
得到指定節點的指定樣式的絕對值。
即可以獲得繼承得到的樣式。
*/
??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.getPropertyvalue(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;
??},
/*
設置指定節點的樣式,這里可以由style參數同時指定多個屬性
例如:
Element.setStyle($("someElement"),{color:'#ff0000',background-color:'#000000'});
就將指定節點的樣式設置為紅字黑底
*/
??setStyle:?function(element,?style)?{
????element?=?$(element);
????for?(name?in?style)
??????element.style[name.camelize()]?=?style[name];
??},

/*
返回節點的寬度和高度,以{width:xx,height:xx}形式返回。
該方法使得無論節點可見與否,都能夠獲取其顯示時的大小。
*/
??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};
??},
/*
使的元素相對定位
*/
??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;
??????}
????}
??},
/*
取消節點的相對定位。
*/
??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('&');
????}
??},

??getvalue:?function(element)?{
????element?=?$(element);
????var?method?=?element.tagName.toLowerCase();
????var?parameter?=?Form.Element.Serializers[method](element);

????if?(parameter)
??????return?parameter[1];
??}
}

Form.Element.Serializers?=?{
//對input標記序列化
??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:?function(element)?{
????return?[element.name,?element.value];
??},
//對下拉列表框序列化
??select:?function(element)?{
????return?Form.Element.Serializers[element.type?==?'select-one'??
??????'selectOne'?:?'selectMany'](element);
??},
//對單選下拉列表框序列化
??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];
??},
//對多選下拉列表框序列化
??selectMany:?function(element)?{
????var?value?=?new?Array();
????for?(var?i?=?0;?i?<?element.length;?i++)?{
??????var?opt?=?element.options[i];
??????if?(opt.selected)?{
????????var?optvalue?=?opt.value;
????????if?(!optvalue?&&?!('value'?in?opt))
??????????optvalue?=?opt.text;
????????value.push(optvalue);
??????}
????}
????return?[element.name,?value];
??}
}

/*--------------------------------------------------------------------------*/

var?$F?=?Form.Element.getvalue;

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver?=?function()?{}
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?=?Class.create();
Form.Element.Observer.prototype?=?Object.extend(new?Abstract.TimedObserver(),?{
??getvalue:?function()?{
????return?Form.Element.getvalue(this.element);
??}
});

Form.Observer?=?Class.create();
Form.Observer.prototype?=?Object.extend(new?Abstract.TimedObserver(),?{
??getvalue:?function()?{
????return?Form.serialize(this.element);
??}
});

/*--------------------------------------------------------------------------*/

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())?{
????????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(),?{
??getvalue:?function()?{
????return?Form.Element.getvalue(this.element);
??}
});

Form.EventObserver?=?Class.create();
Form.EventObserver.prototype?=?Object.extend(new?Abstract.EventObserver(),?{
??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)));
??},

??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.returnvalue?=?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]