Home  >  Article  >  Web Front-end  >  Prototype source code Chinese description prototype.js_prototype

Prototype source code Chinese description prototype.js_prototype

WBOY
WBOYOriginal
2016-05-16 19:25:56980browse

/**
* Define a global object, the attribute Version will be replaced with the current version number when released
*/
var Prototype = {
Version: '@@VERSION@@'
}

/**
* Create a type, note that its attribute create is a method and returns a constructor.
* Generally used as follows
* var X = Class.create(); returns a type, similar to a Class instance of java.
* To use the X type, you need to continue to use new X() to obtain an instance, just like java's Class.newInstance() method.
*
* The returned constructor will execute the method named initialize, which is the name of the constructor method of the Ruby object.
* The initialize method has not been defined at this time. When creating a new type in subsequent code, a corresponding method with the same name will be created.
*
* If you must understand it from Java. You can understand it as using Class.create() to create a class that inherits the java.lang.Class class. Of course java does not allow this, because the Class class is final
*
*/
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}

/**
* To create an object, think in terms of variable names. The original intention may be to define an abstract class, and extend it when creating new objects in the future.
* But judging from the application of the subsequent code, Abstract is more about keeping the namespace clear.
* In other words, we can add a new object definition to the Abstract object instance.
*
* To understand from java, it is to dynamically create an internal class for an object.
*/
var Abstract = new Object();

/**
* Get all properties and methods of the parameter object, a bit like multiple inheritance. But this inheritance is obtained dynamically.
* For example:
* var a = new ObjectA(), b = new ObjectB();
* var c = a.extend(b);
* At this time, object c also owns a and b object properties and methods. But unlike multiple inheritance, c instanceof ObjectB will return false.
*/
Object.prototype.extend = function(object) {
for (property in object) {
this[property] = object[property];
}
return this;
}

/**
* This method is very interesting. It encapsulates a javascript function object and returns a new function object. The body of the new function object is the same as the original object, but the bind() method parameter will be used as the object of the current object.
* That is to say, the this reference in the new function is changed to the object provided by the parameter.
* For example:
*
*
* .............................
*

* Then, calling aaa.showValue will return "aaa", but calling aaa.showValue2 will return "bbb".
*
* apply is a new method that only appeared after ie5.5 (Netscape seems to have supported it very early).
* For more information about this method, please refer to MSDN http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthApply.asp
* There is also a call method, which is similar to apply . You can study it together.
*/
Function.prototype.bind = function(object) {
var method = this;
return function() {
method.apply(object, arguments);
}
}

/**
* Same as bind, but this method is generally used for event processing of html control objects. So the event object needs to be passed
* Note that Function.call is used at this time. The difference between it and Function.apply seems to be just the definition of parameter form. <script> <BR> * var aaa = document.getElementById("aaa"); <BR> * var bbb = document.getElementById("bbb"); <BR> * aaa.showValue = function() {alert(this.value);} <BR> * aaa.showValue2 = aaa.showValue.bind(bbb); <BR> * </script> * Just like java's two overloaded methods. 
 */ 
Function.prototype.bindAsEventListener = function(object) { 
 var method = this; 
 return function(event) { 
 method.call(object, event || window.event); 
 } 


/**
* Convert RGB color values ​​in integer form to HEX form
*/ 
Number.prototype.toColorPart = function() { 
 var digits = this.toString(16); 
 if (this  return digits; 


/**
* Typical Ruby style function, calls the methods in the parameters one by one, and returns the return value of the first successfully executed method
*/ 
var Try = { 
 these: function() { 
 var returnValue; 

 for (var i = 0; i  var lambda = arguments[i]; 
 try { 
 returnValue = lambda(); 
 break; 
 } catch (e) {} 
 } 

 return returnValue; 
 } 


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

/**
* A well-designed scheduled executor
* First create a PeriodicalExecuter type by Class.create(),
* Then set the prototype using the syntax of object literals.
*
* What needs special explanation is the rgisterCallback method, which calls the function prototype method bind defined above and passes itself as a parameter.
* The reason for this is because setTimeout always takes the window object as the current object by default, that is, if the registerCallback method is defined as follows:
* registerCallback: function() {
* setTimeout(this. onTimerEvent, this.frequency * 1000);
* }
* Then, the this.onTimeoutEvent method fails to execute because it cannot access the this.currentlyExecuting property.
* After using bind, this method can correctly find this, which is the current instance of PeriodicalExecuter.
*/ 
var PeriodicalExecuter = Class.create(); 
PeriodicalExecuter.prototype = { 
 initialize: function(callback, frequency) { 
 this.callback = callback; 
 this.frequency = frequency; 
 this.currentlyExecuting = false; 

 this.registerCallback(); 
 }, 

 registerCallback: function() { 
 setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000); 
 }, 

 onTimerEvent: function() { 
 if (!this.currentlyExecuting) { 
 try { 
 this.currentlyExecuting = true; 
 this.callback(); 
 } finally { 
 this.currentlyExecuting = false; 
 } 
 } 

 this.registerCallback(); 
 } 


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

/** 
 * 这个函数就 Ruby 了。我觉得它的作用主要有两个 
 * 1. 大概是 document.getElementById(id) 的最简化调用。 
 * 比如:$("aaa") 将返回上 aaa 对象 
 * 2. 得到对象数组 
 * 比如: $("aaa","bbb") 返回一个包括id为"aaa"和"bbb"两个input控件对象的数组。 
 */ 
function $() { 
 var elements = new Array(); 

 for (var i = 0; i  var element = arguments[i]; 
 if (typeof element == 'string') 
 element = document.getElementById(element); 

 if (arguments.length == 1) 
 return element; 

 elements.push(element); 
 } 

 return elements; 

/**
* Define Ajax object, static method getTransport method returns an XMLHttp object
*/ 
var Ajax = { 
 getTransport: function() { 
 return Try.these( 
 function() {return new ActiveXObject('Msxml2.XMLHTTP')}, 
 function() {return new ActiveXObject('Microsoft.XMLHTTP')}, 
 function() {return new XMLHttpRequest()} 
 ) || false; 
 }, 

 emptyFunction: function() {} 


/**
* I thought the Ajax object at this time acted as a namespace.
* Ajax.Base is declared as a basic object type
* Note that Ajax.Base is not created using Class.create(). I think it is because the author does not want Ajax.Base to be instanced by the library user change.
* The author's declarations of other object types will inherit from it.
* Just like a private abstract class in java
*/ 
Ajax.Base = function() {}; 
Ajax.Base.prototype = { 
 /** 
 * extend (见prototype.js中的定义) 的用法真是让人耳目一新 
 * options 首先设置默认属性,然后再 extend 参数对象,那么参数对象中也有同名的属性,那么就覆盖默认属性值。 
 * 想想如果我写这样的实现,应该类似如下: 
 setOptions: function(options) { 
 this.options.methed = options.methed? options.methed : 'post'; 
 .......... 
 } 
 我想很多时候,java 限制了 js 的创意。 
 */ 
 setOptions: 函数(选项) { 
 this.options = { 
 方法: 'post', 
 异步: true, 
 参数: '' 
 } .extend(选项||{}); 
 } 


/** 
 * Ajax.Request 封装 XmlHttp 
 */ 
Ajax.Request = Class.create(); 

/** 
 * 定义四种事件(状态), 参考http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp 
 */ 
Ajax.Request.Events = 
 ['未初始化','正在加载','已加载','交互式','已完成']; 

/** 
 * 
 */ 
Ajax.Request.prototype = (new Ajax.Base()).extend({ 
 初始化: 函数(url, 选项) { 
this.transport = Ajax.getTransport(); 
 this.setOptions(options); 

 尝试 { 
 if (this.options.method == 'get') 
 url = ' ?'    this.options.parameters   '&_='; 

 /** 
 * 此处好像强制使用了异步方式,而不是依照 this.options.asynchronous 的值 
 */ 
 this.transport.open(this.options.method, url, true); 

 /** 
 * 这里提供了 XmlHttp 传输过程中每个步骤的回调函数 
 */ 
 if (this.options.asynchronous) { 
 this.transport.onreadystatechange = this.onStateChange.bind(this); 
 setTimeout((function() { this.respondToReadyState(1)}).bind(this), 10); 
 } 

 this.transport.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 
 this .transport.setRequestHeader('X-Prototype-Version', Prototype.Version); 

 if (this.options.method == 'post') { 
 this.transport.setRequestHeader('Connection' , '关闭'); 
 this.transport.setRequestHeader('Content-type', 
 'application/x-www-form-urlencoded'); 
 } 

 this.transport .send(this.options.method == 'post'? 
 this.options.parameters  '&_=' : null); 

 } catch (e) { 
 } 
 }, 

 onStateChange: function() { 
 var readyState = this.transport.readyState; 
 /** 
 * 如果不是 Loading 状态,就调用回调函数 
 */ 
 if (readyState != 1) 
 this.respondToReadyState(this.transport.readyState); 
 }, 

 /** 
 * 回调函数定义在 this.options 属性中,比如: 
 var option = { 
 onLoaded : function(req) {...}; 
 ...... 
 } 
 new Ajax.Request(url, option); 
 */ 
 respondToReadyState: function(readyState) { 
 var event = Ajax.Request.Events[readyState]; 
 (this.options['on'  事件] || Ajax.emptyFunction)(this.transport); 
 } 
}); 

/** 
 * Ajax.Updater 用于绑定一个 html 元素与 XmlHttp 调用的返回值。类似于 buffalo 的绑定。
 * 如果选项包含插入(来自 dom.js) ) 对象的话,插入能够提供更多的插入控制。 
 */ 
Ajax.Updater = Class.create(); 
Ajax.Updater.prototype = (new Ajax.Base()).extend({ 
 initialize: function(container, url, options) { 
 this.container = $(container); 
 this.setOptions(options); 

 if (this.options.asynchronous) { 
 this.onComplete = this.options.onComplete; 
 this.options.onComplete = this.updateContent.bind(this); 
 } 

 this.request = new Ajax.Request(url, this.options); 

 if (!this.options.asynchronous) 
 this.updateContent(); 
 }, 

 updateContent: function() { 
 if (this.options.insertion) { 
 new this.options.insertion(this.container, 
 this.request.transport.responseText); 
 } else { 
 this.container.innerHTML = this.request.transport.responseText; 
 } 

 if (this.onComplete) { 
 setTimeout((function() {this.onComplete(this.request)}).bind(this), 10); 
 } 
 } 
}); 
/**
* Provides some simple static methods for the tool class of page element object
*/ 
var Field = { 
 /**
* Clear the value of the parameter reference object
*/ 
 clear: function() { 
 for (var i = 0; i  $(arguments[i]).value = ''; 
 }, 

 /**
* Make the parameter reference object gain focus
*/ 
 focus: function(element) { 
 $(element).focus(); 
 }, 

 /**
* Determine whether the parameter reference object value is empty. If it is empty, return false, otherwise true
*/ 
 present: function() { 
 for (var i = 0; i  if ($(arguments[i]).value == '') return false; 
 return true; 
 }, 

 /**
* Make the selected parameter reference object
*/ 
 select: function(element) { 
 $(element).select(); 
 }, 

 /**
* Make the parameter reference object editable
*/ 
 activate: function(element) { 
 $(element).focus(); 
 $(element).select(); 
 } 


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

/**
* Form tool class
*/ 
var Form = { 
 /**
* Combine the serialized values ​​of the form elements into the form of QueryString
*/ 
 serialize: function(form) { 
 var elements = Form.getElements($(form)); 
 var queryComponents = new Array(); 

 for (var i = 0; i  var queryComponent = Form.Element.serialize(elements[i]); 
 if (queryComponent) 
 queryComponents.push(queryComponent); 
 } 

 return queryComponents.join('&'); 
 }, 

 /**
* Get all element objects of the form
*/ 
 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  elements.push(tagElements[j]); 
 } 
 return elements; 
 }, 

 /** 
 * 将指定表单的元素置于不可用状态 
 */ 
 禁用: function(form) { 
 var elements = Form.getElements(form); 
for (var i = 0; i var element = elements[i]; 
 element.blur(); 
 element.disable = 'true'; 
 } 
 }, 

 /** 
 * 使表单的第一个非 hidden 类型而且处于可用状态的元素获得焦点 
 */ 
 focusFirstElement: 函数(表单) { 
 表单 = $(表单); 
var elements = Form.getElements(form); 
for (var i = 0; i var element = elements[i]; 
 if (element.type !='隐藏' && !element.disabled) { 
 Field.activate(element); 
 休息; 
 } 
 } 
 }, 

 /* 
 * 重置表单 
 */ 
 重置: 函数(表单) { 
 $(表单) 。重置(); 
 } 


/** 
 * 表单元素工具类 
 */ 
Form.Element = { 
 /** 
 * 返回表单元素的值先序列化再进行 URL 编码后的值 
 */ 
 序列化: 函数(元素) { 
 元素 = $(元素); 
var method = element.tagName.toLowerCase(); 
 var 参数 = Form.Element.Serializers[method](element); 

 if (参数) 
 return encodeURIComponent(parameter[0])  '='   
 encodeURIComponent(parameter[1]); 
 }, 

 /** 
 * 返回表单元素序列化后的值 
 */ 
 getValue: 函数(元素) { 
 元素 = $(元素); 
var method = element.tagName.toLowerCase(); 
 var 参数 = Form.Element.Serializers[method](element); 

 if(参数)
 返回参数[1]; 
 } 


/** 
 * prototype 的所谓序列化其实就是将表单的名字和值组合成一个数组 
 */ 
Form.Element.Serializers = { 
 输入:函数(元素) { 
 switch (element.type) .toLowerCase()) { 
 case '隐藏': 
 case '密码': 
 case '文本': 
 return Form.Element.Serializers.textarea(element); 
 case ‘checkbox’: 
 case ‘radio’: 
 return Form.Element.Serializers.inputSelector(element); 
 } 
 返回 false; 
 }, 

 inputSelector: 函数(元素) { 
 if (element.checked) 
 return [element.name, element.value]; 
 }, 

 textarea: 函数(元素) { 
 return [element.name, element.value]; 
 }, 

 /** 
 * 看样子,也不支持多选框(select-multiple) 
 */ 
 select: 函数(元素) { 
 var index = element.selectedIndex; 
 var 值 = element.options[index].value || 元素.选项[索引].文本; 
 返回[元素.名称,(索引>=0)? 价值 : '']; 
 } 


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

/**
* Form.Element.getValue may be used frequently, so I made a shortcut reference
*/ 
var $F = Form.Element.getValue; 

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

/**
* Abstract.TimedObserver is not created using Class.create(), it should have the same intention as Ajax.Base
* Abstract.TimedObserver, as the name suggests, applies the Observer design pattern to track specified form elements,
* When When the value of the form element changes, the callback function is executed
*
* I think Observer is similar to registering the onchange event. The difference is that the onchange event is only fired when the element loses focus.
* It is also similar to the onpropertychange event, but it only focuses on changes in the value of form elements and provides timeout control.
*
* In addition, the advantage of Observer is probably that it is more object-oriented. In addition, the callback function can be dynamically changed, which is more flexible than registering events.
* Observer should be capable of dynamic data verification, or linkage of multiple associated drop-down option lists, etc.
*
*/ 
Abstract.TimedObserver = function() {} 

/**
* This design is the same as PeriodicalExecuter, the bind method is the core of the implementation
*/ 
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() { 
 var value = this.getValue(); 
 if (this.lastValue != value) { 
 this.callback(this.element, value); 
 this.lastValue = value; 
 } 

 this.registerCallback(); 
 } 


/**
* Form.Element.Observer and Form.Observer are actually the same
* Note that Form.Observer is not used to track the entire form. I think it is just to reduce writing (this is a design principle of Ruby)
*/ 
Form.Element.Observer = Class.create(); 
Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({ 
 getValue: function() { 
 return Form.Element.getValue(this.element); 
 } 
}); 

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

/**
* Get the object array based on the name of the class attribute, supporting multiple classes
*
*/ 
document.getElementsByClassName = function(className) { 
 var children = document.getElementsByTagName('*') || document.all; 
 var elements = new Array(); 

 for (var i = 0; i  var child = children[i]; 
 var classNames = child.className.split(' '); 
 for (var j = 0; j  if (classNames[j] == className) { 
 elements.push(child); 
 break; 
 } 
 } 
 } 

 return elements; 


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

/** 
 * Element 就象一个 java 的工具类,主要用来 隐藏/显示/销除 对象,以及获取对象的简单属性。 
 * 
 */ 
var Element = { 
 toggle: function() { 
 for (var i = 0; i  var element = $(arguments[i]); 
 element.style.display = 
 (element.style.display == 'none' ? '' : 'none'); 
 } 
 }, 

 hide: function() { 
 for (var i = 0; i  var element = $(arguments[i]); 
 element.style.display = 'none'; 
 } 
 }, 

 show: function() { 
 for (var i = 0; i  var element = $(arguments[i]); 
 element.style.display = ''; 
 } 
 }, 

 remove: function(element) { 
 element = $(element); 
 element.parentNode.removeChild(element); 
 }, 

 getHeight: function(element) { 
 element = $(element); 
 return element.offsetHeight; 
 } 


/**
* Made a symbolic link for Element.toggle, probably for compatibility reasons
*/ 
var Toggle = new Object(); 
Toggle.display = Element.toggle; 

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

/**
* For the implementation of dynamically inserted content, the object in MS's Jscript implementation has an insertAdjacentHTML method (http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/insertadjacenthtml.asp)
* Here It is regarded as an encapsulation in the form of an object.
*/ 
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 does not support the insertAdjacentHTML method, but you can use the following code instead
*/ 
 this.range = this.element.ownerDocument.createRange(); 
 /**
* If the initializeRange method is defined, it will be executed. This is equivalent to defining an abstract initializeRange method
*/ 
 if (this.initializeRange) this.initializeRange(); 
 this.fragment = this.range.createContextualFragment(this.content); 

 /**
* insertContent is also an abstract method, subclasses must implement it
*/ 
 this.insertContent(); 
 } 
 } 


/** 
 * prototype 加深了我的体会,就是写js 如何去遵循 Don't Repeat Yourself (DRY) 原则 
 * 上文中 Abstract.Insertion 算是一个抽象类,定义了名为 initializeRange 的一个抽象方法 
 * var Insertion = new Object() 建立一个命名空间 
 * Insertion.Before|Top|Bottom|After 就象是四个java中的四个静态内部类,而它们分别继承于Abstract.Insertion,并实现了initializeRange方法。 
 */ 
var Insertion = new Object(); 

Insertion.Before = Class.create(); 
Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({ 
 initializeRange: function() { 
 this.range.setStartBefore(this.element); 
 }, 

 /**
* Insert content in front of the specified node, at the same level as the specified node
*/ 
 insertContent: function() { 
 this.element.parentNode.insertBefore(this.fragment, this.element); 
 } 
}); 

Insertion.Top = Class.create(); 
Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({ 
 initializeRange: function() { 
 this.range.selectNodeContents(this.element); 
 this.range.collapse(true); 
 }, 

 /**
* Insert the content before the first child node of the specified node, so the content becomes the first child node of the node
*/ 
 insertContent: function() { 
 this.element.insertBefore(this.fragment, this.element.firstChild); 
 } 
}); 

Insertion.Bottom = Class.create(); 
Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({ 
 initializeRange: function() { 
 this.range.selectNodeContents(this.element); 
 this.range.collapse(this.element); 
 }, 

 /**
* Insert the content to the end of the specified node, so the content becomes the last child node of the node
*/ 
 insertContent: function() { 
 this.element.appendChild(this.fragment); 
 } 
}); 

Insertion.After = Class.create(); 
Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({ 
 initializeRange: function() { 
 this.range.setStartAfter(this.element); 
 }, 

 /**
* Insert content after the specified node, at the same level as the specified node
*/ 
 insertContent: function() { 
 this.element.parentNode.insertBefore(this.fragment, 
 this.element.nextSibling); 
 } 
}); 

/* 这是包含错误的原版本 
if (!Function.prototype.apply) { 
// Based on code from http://www.youngpup.net/ 
Function.prototype.apply = function(object, parameters) { 
var parameterStrings = new Array(); 
if (!object) object = window; 
if (!parameters) parameters = new Array(); 

for (var i = 0; i parameterStrings[i] = 'x['   i   ']'; //Error 1 

object.__apply__ = this; 
var result = eval('obj.__apply__('   //Error 2 
parameterStrings[i].join(', ')   ')'); 
object.__apply__ = null; 

return result; 


*/ 

if (!Function.prototype.apply) { 
 Function.prototype.apply = function(object, parameters) { 
 var parameterStrings = new Array(); 
 if (!object) object = window; 
 if (!parameters) parameters = new Array(); 

 for (var i = 0; i  parameterStrings[i] = 'parameters['   i   ']'; 

 object.__apply__ = this; 
 var result = eval('object.__apply__('   parameterStrings.join(', ')   ')'); 
 object.__apply__ = null; 

 return result; 
 } 


Effect 的一个子类




Effect.Blink = Class.create(); 
Effect.Blink.prototype = { 
 initialize: function(element, frequency) { 
 this.element = $(element); 
 this.frequency = frequency?frequency:1000; 
 this.element.effect_blink = this; 
 this.blink(); 
 }, 

 blink: function() { 
 if (this.timer) clearTimeout(this.timer); 
 try { 
 this.element.style.visibility = this.element.style.visibility == 'hidden'?'visible':'hidden'; 
 } catch (e) {} 
 this.timer = setTimeout(this.blink.bind(this), this.frequency); 
 } 
};

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn