Prototype源码分析2

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

/**

*表单工具类

*/

varForm={

/**

*将表单元素序列化后的值组合成QueryString的形式

*/

serialize:function(form){

varelements=Form.getElements($(form));

varqueryComponents=newArray();

for(vari=0;i<elements.length;i++){

varqueryComponent=Form.Element.serialize(elements[i]);

if(queryComponent)

queryComponents.push(queryComponent);

}

returnqueryComponents.join('&');

},

/**

*得到表单的所有元素对象

*/

getElements:function(form){

form=$(form);

varelements=newArray();

for(tagNameinForm.Element.Serializers){

vartagElements=form.getElementsByTagName(tagName);

for(varj=0;j<tagElements.length;j++)

elements.push(tagElements[j]);

}

returnelements;

},

/**

*将指定表单的元素置于不可用状态

*/

disable:function(form){

varelements=Form.getElements(form);

for(vari=0;i<elements.length;i++){

varelement=elements[i];

element.blur();

element.disable='true';

}

},

/**

*使表单的第一个非hidden类型而且处于可用状态的元素获得焦点

*/

focusFirstElement:function(form){

form=$(form);

varelements=Form.getElements(form);

for(vari=0;i<elements.length;i++){

varelement=elements[i];

if(element.type!='hidden'&&!element.disabled){

Field.activate(element);

break;

}

}

},

/*

*重置表单

*/

reset:function(form){

$(form).reset();

}

}

/**

*表单元素工具类

*/

Form.Element={

/**

*返回表单元素的值先序列化再进行URL编码后的值

*/

serialize:function(element){

element=$(element);

varmethod=element.tagName.toLowerCase();

varparameter=Form.Element.Serializers[method](element);

if(parameter)

returnencodeURIComponent(parameter[0])+'='+

encodeURIComponent(parameter[1]);

},

/**

*返回表单元素序列化后的值

*/

getValue:function(element){

element=$(element);

varmethod=element.tagName.toLowerCase();

varparameter=Form.Element.Serializers[method](element);

if(parameter)

returnparameter[1];

}

}

/**

*prototype的所谓序列化其实就是将表单的名字和值组合成一个数组

*/

Form.Element.Serializers={

input:function(element){

switch(element.type.toLowerCase()){

case'hidden':

case'password':

case'text':

returnForm.Element.Serializers.textarea(element);

case'checkbox':

case'radio':

returnForm.Element.Serializers.inputSelector(element);

}

returnfalse;

},

inputSelector:function(element){

if(element.checked)

return[element.name,element.value];

},

textarea:function(element){

return[element.name,element.value];

},

/**

*看样子,也不支持多选框(select-multiple)

*/

select:function(element){

varindex=element.selectedIndex;

varvalue=element.options[index].value||element.options[index].text;

return[element.name,(index>=0)?value:''];

}

}

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

/**

*Form.Element.getValue也许会经常用到,所以做了一个快捷引用

*/

var$F=Form.Element.getValue;

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

/**

*Abstract.TimedObserver也没有用Class.create()来创建,和Ajax.Base意图应该一样

*Abstract.TimedObserver顾名思义,是套用Observer设计模式来跟踪指定表单元素,

*当表单元素的值发生变化的时候,就执行回调函数

*

*我想 Observer与注册onchange事件相似,不同点在于onchange事件是在元素失去焦点的时候才激发。

*同样的与onpropertychange事件也相似,不过它只关注表单元素的值的变化,而且提供timeout的控制。

*

*除此之外,Observer的好处大概就在与更面向对象,另外可以动态的更换回调函数,这就比注册事件要灵活一些。

*Observer应该可以胜任动态数据校验,或者多个关联下拉选项列表的连动等等

*

*/

Abstract.TimedObserver=function(){}

/**

*这个设计和PeriodicalExecuter一样,bind方法是实现的核心

*/

Abstract.TimedObserver.prototype={

initialize:function(element,frequency,callback){

this.frequency=frequency;

this.element=$(element);

this.callback=callback;

this.lastValue=this.getValue();

this.registerCallback();

},

registerCallback:function(){

setTimeout(this.onTimerEvent.bind(this),this.frequency*1000);

},

onTimerEvent:function(){

varvalue=this.getValue();

if(this.lastValue!=value){

this.callback(this.element,value);

this.lastValue=value;

}

this.registerCallback();

}

}

/**

*Form.Element.Observer和Form.Observer其实是一样的

*注意Form.Observer并不是用来跟踪整个表单的,我想大概只是为了减少书写(这是Ruby的一个设计原则)

*/

Form.Element.Observer=Class.create();

Form.Element.Observer.prototype=(newAbstract.TimedObserver()).extend({

getValue:function(){

returnForm.Element.getValue(this.element);

}

});

Form.Observer=Class.create();

Form.Observer.prototype=(newAbstract.TimedObserver()).extend({

getValue:function(){

returnForm.serialize(this.element);

}

});

/**

*根据classattribute的名字得到对象数组,支持multipleclass

*

*/

document.getElementsByClassName=function(className){

varchildren=document.getElementsByTagName('*')||document.all;

varelements=newArray();

for(vari=0;i<children.length;i++){

varchild=children[i];

varclassNames=child.className.split('');

for(varj=0;j<classNames.length;j++){

if(classNames[j]==className){

elements.push(child);

break;

}

}

}

returnelements;

}

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

/**

*Element就象一个java的工具类,主要用来隐藏/显示/销除对象,以及获取对象的简单属性。

*

*/

varElement={

toggle:function(){

for(vari=0;i<arguments.length;i++){

varelement=$(arguments[i]);

element.style.display=

(element.style.display=='none'?'':'none');

}

},

hide:function(){

for(vari=0;i<arguments.length;i++){

varelement=$(arguments[i]);

element.style.display='none';

}

},

show:function(){

for(vari=0;i<arguments.length;i++){

varelement=$(arguments[i]);

element.style.display='';

}

},

remove:function(element){

element=$(element);

element.parentNode.removeChild(element);

},

getHeight:function(element){

element=$(element);

returnelement.offsetHeight;

}

}

/**

*为Element.toggle做了一个符号连接,大概是兼容性的考虑

*/

varToggle=newObject();

Toggle.display=Element.toggle;

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

/**

*动态插入内容的实现,MS的Jscript实现中对象有一个insertAdjacentHTML方法(http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/insertadjacenthtml.asp)

*这里算是一个对象形式的封装。

*/

Abstract.Insertion=function(adjacency){

this.adjacency=adjacency;

}

Abstract.Insertion.prototype={

initialize:function(element,content){

this.element=$(element);

this.content=content;

if(this.adjacency&&this.element.insertAdjacentHTML){

this.element.insertAdjacentHTML(this.adjacency,this.content);

}else{

/**

*gecko不支持insertAdjacentHTML方法,但可以用如下代码代替

*/

this.range=this.element.ownerDocument.createRange();

/**

*如果定义了initializeRange方法,则实行,这里相当与定义了一个抽象的initializeRange方法

*/

if(this.initializeRange)this.initializeRange();

this.fragment=this.range.createContextualFragment(this.content);

/**

*insertContent也是一个抽象方法,子类必须实现

*/

this.insertContent();

}

}

}

/**

*prototype加深了我的体会,就是写js如何去遵循 Don’tRepeatYourself(DRY)原则

*上文中Abstract.Insertion算是一个抽象类,定义了名为 initializeRange的一个抽象方法

*varInsertion=newObject() 建立一个命名空间

*Insertion.Before|Top|Bottom|After就象是四个java中的四个静态内部类,而它们分别继承于Abstract.Insertion,并实现了initializeRange方法。

*/

varInsertion=newObject();

Insertion.Before=Class.create();

Insertion.Before.prototype=(newAbstract.Insertion('beforeBegin')).extend({

initializeRange:function(){

this.range.setStartBefore(this.element);

},

/**

*将内容插入到指定节点的前面,与指定节点同级

*/

insertContent:function(){

this.element.parentNode.insertBefore(this.fragment,this.element);

}

});

Insertion.Top=Class.create();

Insertion.Top.prototype=(newAbstract.Insertion('afterBegin')).extend({

initializeRange:function(){

this.range.selectNodeContents(this.element);

this.range.collapse(true);

},

/**

*将内容插入到指定节点的第一个子节点前,于是内容变为该节点的第一个子节点

*/

insertContent:function(){

this.element.insertBefore(this.fragment,this.element.firstChild);

}

});

Insertion.Bottom=Class.create();

Insertion.Bottom.prototype=(newAbstract.Insertion('beforeEnd')).extend({

initializeRange:function(){

this.range.selectNodeContents(this.element);

this.range.collapse(this.element);

},

/**

*将内容插入到指定节点的最后,于是内容变为该节点的最后一个子节点

*/

insertContent:function(){

this.element.appendChild(this.fragment);

}

});

Insertion.After=Class.create();

Insertion.After.prototype=(newAbstract.Insertion('afterEnd')).extend({

initializeRange:function(){

this.range.setStartAfter(this.element);

},

/**

*将内容插入到指定节点的后面,与指定节点同级

*/

insertContent:function(){

this.element.parentNode.insertBefore(this.fragment,

this.element.nextSibling);

}

});

相关推荐