SoFunction
Updated on 2025-04-13

prototype source code Chinese description

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


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


/** 
* Create an object, think from the variable name, the original meaning may be to define an abstract class, and then create new objects in the future, extend it.
* But from the application of subsequent codes, Abstract is more about keeping the namespace clear.
* In other words, we can add a new object definition to the Abstract object instance.
 * 
* Understanding from Java, it is to dynamically create internal classes for an object.
 */ 
var Abstract = new Object(); 

/** 
* Getting all attributes and methods of parameter objects is a bit like multiple inheritance. But this inheritance is obtained dynamically.
* like:
 * var a = new ObjectA(), b = new ObjectB(); 
 * var c = (b); 
* At this time, the c object has both the properties and methods of the a and b objects. But unlike multiple inheritance, c instanceof ObjectB will return false.
 */ 
 = 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.
* In other words, this reference in the new function is changed to an object provided by the parameter.
* for example:
 * <input type="text"  value="aaa"> 
 * <input type="text"  value="bbb"> 
 * ................. 
 * <script> 
 * var aaa = ("aaa"); 
 * var bbb = ("bbb"); 
 *  = function() {alert();} 
 * aaa.showValue2 = (bbb); 
 * </script> 
* Then, the call will return "aaa", but the call to 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/library/en-us/script56/html/
* There is also a call method, which is similar to apply. You can study it together.
 */ 
 = function(object) { 
 var method = this; 
 return function() { 
 (object, arguments); 
 } 


/** 
* Same as bind, but this method is generally used as event processing for html control objects. So you have to pass the event object
* Note that at this time, it was used. Its difference from    seems to be just a definition of parameter form.
* Like java two overload methods.
 */ 
 = function(object) { 
 var method = this; 
 return function(event) { 
 (object, event || ); 
 } 


/** 
* Convert the integer form RGB color value to HEX form
 */ 
 = function() { 
 var digits = (16); 
 if (this < 16) return '0' + digits; 
 return digits; 


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

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

 return returnValue; 
 } 


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

/** 
* A well-designed timing actuator
* First, create a PeriodicalExecuter type from (),
* Then set the prototype using the syntax form of the object direct quantity.
 * 
* It should be noted specifically is the rgisterCallback method, which calls the function prototype method bind defined above and passes itself as parameters.
* The reason for this is that setTimeout always uses the window object as the current object by default, that is, if the registerCallback method is defined as follows:
 * registerCallback: function() { 
 * setTimeout(,  * 1000); 
 * } 
* Then, the method execution fails because it cannot access the property.
*  After using bind, this method can correctly find this, which is the current instance of PeriodicalExecuter.
 */ 
var PeriodicalExecuter = (); 
 = { 
 initialize: function(callback, frequency) { 
  = callback; 
  = frequency; 
  = false; 

 (); 
 }, 

 registerCallback: function() { 
 setTimeout((this),  * 1000); 
 }, 

 onTimerEvent: function() { 
 if (!) { 
 try { 
  = true; 
 (); 
 } finally { 
  = false; 
 } 
 } 

 (); 
 } 


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

/** 
* This function is Ruby. I think its main functions are two
* 1. It is probably the simplified call of (id).
* For example: $("aaa") will return the aaa object
* 2. Get the object array
* For example: $("aaa","bbb") returns an array with two input control objects with ids "aaa" and "bbb".
 */ 
function $() { 
 var elements = new Array(); 

 for (var i = 0; i < ; i++) { 
 var element = arguments[i]; 
 if (typeof element == 'string') 
 element = (element); 

 if ( == 1) 
 return element; 

 (element); 
 } 

 return elements; 

/** 
* Definition Ajax object, static method getTransport method returns an XMLHttp object
 */ 
var Ajax = { 
 getTransport: function() { 
 return ( 
 function() {return new ActiveXObject('')}, 
 function() {return new ActiveXObject('')}, 
 function() {return new XMLHttpRequest()} 
 ) || false; 
 }, 

 emptyFunction: function() {} 


/** 
* I thought the Ajax object at this time played the role of a namespace.
*  Declared as a base object type
* Note  There is not created using the method of () , I think it is because the author does not want it to be instantiated by the library user.
* The author will inherit it in the declaration of other object types.
* It's like a private abstract class in java
 */ 
 = function() {}; 
 = { 
 /** 
* The usage of extend (see the definition in) is really refreshing
* options First set the default attribute, then extend the parameter object, and there are attributes with the same name in the parameter object, so the default attribute value is overridden.
* Think about it if I write such an implementation, it should be similar to the following:
 setOptions: function(options) { 
  = ?  : 'post'; 
 .......... 
 } 
I think many times, java limits the creativity of js.
 */ 
 setOptions: function(options) { 
  = { 
 method: 'post', 
 asynchronous: true, 
 parameters: '' 
 }.extend(options || {}); 
 } 


/** 
*  Encapsulation XmlHttp
 */ 
 = (); 

/** 
* Define four events (states), refer to /workshop/author/dhtml/reference/properties/readystate_1.asp
 */ 
 = 
 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 

/** 
 * 
 */ 
 = (new ()).extend({ 
 initialize: function(url, options) { 
  = (); 
 (options); 

 try { 
 if ( == 'get') 
 url += '?' +  + '&_='; 

 /** 
* It seems that the asynchronous method is forced here, rather than according to the value
 */ 
 (, url, true); 

 /** 
* The callback function for each step in the transmission process is provided here.
 */ 
 if () { 
  = (this); 
 setTimeout((function() {(1)}).bind(this), 10); 
 } 

 ('X-Requested-With', 'XMLHttpRequest'); 
 ('X-Prototype-Version', ); 

 if ( == 'post') { 
 ('Connection', 'close'); 
 ('Content-type', 
 'application/x-www-form-urlencoded'); 
 } 

 ( == 'post' ? 
  + '&_=' : null); 

 } catch (e) { 
 } 
 }, 

 onStateChange: function() { 
 var readyState = ; 
 /** 
* If it is not the Loading state, call the callback function
 */ 
 if (readyState != 1) 
 (); 
 }, 

 /** 
* The callback function is defined in the attribute, such as:
 var option = { 
 onLoaded : function(req) {...}; 
 ...... 
 } 
 new (url, option); 
 */ 
 respondToReadyState: function(readyState) { 
 var event = [readyState]; 
 (['on' + event] || )(); 
 } 
}); 

/** 
*  Used to bind an html element to the return value of the XmlHttp call. Similar to the bind with buffalo.
* If there is an insertion(from ) object in options, insertion can provide more insertion control.
 */ 
 = (); 
 = (new ()).extend({ 
 initialize: function(container, url, options) { 
  = $(container); 
 (options); 

 if () { 
  = ; 
  = (this); 
 } 

  = new (url, ); 

 if (!) 
 (); 
 }, 

 updateContent: function() { 
 if () { 
 new (, 
 ); 
 } else { 
  = ; 
 } 

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

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

 /** 
* Determine whether the value of the parameter reference object is empty. If it is empty, return false, otherwise true
 */ 
 present: function() { 
 for (var i = 0; i < ; 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 in an editable state
 */ 
 activate: function(element) { 
 $(element).focus(); 
 $(element).select(); 
 } 


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

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

 for (var i = 0; i < ; i++) { 
 var queryComponent = (elements[i]); 
 if (queryComponent) 
 (queryComponent); 
 } 

 return ('&'); 
 }, 

 /** 
* Get all element objects of the form
 */ 
 getElements: function(form) { 
 form = $(form); 
 var elements = new Array(); 

 for (tagName in ) { 
 var tagElements = (tagName); 
 for (var j = 0; j < ; j++) 
 (tagElements[j]); 
 } 
 return elements; 
 }, 

 /** 
* Put elements of the specified form in an unavailable state
 */ 
 disable: function(form) { 
 var elements = (form); 
 for (var i = 0; i < ; i++) { 
 var element = elements[i]; 
 (); 
  = 'true'; 
 } 
 }, 

 /** 
* Make the first element of the form that is not of hidden type and is in available state focus
 */ 
 focusFirstElement: function(form) { 
 form = $(form); 
 var elements = (form); 
 for (var i = 0; i < ; i++) { 
 var element = elements[i]; 
 if ( != 'hidden' && !) { 
 (element); 
 break; 
 } 
 } 
 }, 

 /* 
* Reset the form
 */ 
 reset: function(form) { 
 $(form).reset(); 
 } 


/** 
* Form Element Tool Class
 */ 
 = { 
 /** 
* Return the value of the form element first serialize and then URL encoded.
 */ 
 serialize: function(element) { 
 element = $(element); 
 var method = (); 
 var parameter = [method](element); 

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

 /** 
* Return the serialized value of form element
 */ 
 getValue: function(element) { 
 element = $(element); 
 var method = (); 
 var parameter = [method](element); 

 if (parameter) 
 return parameter[1]; 
 } 


/** 
* The so-called serialization of prototype is actually combining the name and value of the form into an array
 */ 
 = { 
 input: function(element) { 
 switch (()) { 
 case 'hidden': 
 case 'password': 
 case 'text': 
 return (element); 
 case 'checkbox': 
 case 'radio': 
 return (element); 
 } 
 return false; 
 }, 

 inputSelector: function(element) { 
 if () 
 return [, ]; 
 }, 

 textarea: function(element) { 
 return [, ]; 
 }, 

 /** 
* It seems that it does not support multiple selection boxes (select-multiple)
 */ 
 select: function(element) { 
 var index = ; 
 var value = [index].value || [index].text; 
 return [, (index >= 0) ? value : '']; 
 } 


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

/** 
*  Maybe it will be used frequently, so I made a quick quote
 */ 
var $F = ; 

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

/** 
*  It's useless to create () , and the intention should be the same
*  As the name suggests, it uses the Observer design pattern to track specified form elements.
* When the value of the form element changes, execute the callback function.
 * 
* I think Observer is similar to registering onchange events, the difference is that onchange events are only activated when the element loses focus.
* The same is similar to the onpropertychange event, but it only focuses on the changes in the value of the form element and provides timeout control.
 * 
* In addition, the benefits of Observer are probably that it is more object-oriented, and it can dynamically replace callback functions, which is more flexible than registering events.
* Observer should be competent for dynamic data verification, or the connection of multiple associated drop-down option lists, etc.
 * 
 */ 
 = function() {} 

/** 
* This design is the same as PeriodicalExecuter. The bind method is the core of the implementation.
 */ 
 = { 
 initialize: function(element, frequency, callback) { 
  = frequency; 
  = $(element); 
  = callback; 

  = (); 
 (); 
 }, 

 registerCallback: function() { 
 setTimeout((this),  * 1000); 
 }, 

 onTimerEvent: function() { 
 var value = (); 
 if ( != value) { 
 (, value); 
  = value; 
 } 

 (); 
 } 


/** 
*  It's actually the same
* Note  It is not used to track the entire form, I think it is probably just to reduce writing (this is a design principle of Ruby)
 */ 
 = (); 
 = (new ()).extend({ 
 getValue: function() { 
 return (); 
 } 
}); 

 = (); 
 = (new ()).extend({ 
 getValue: function() { 
 return (); 
 } 
}); 

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

 for (var i = 0; i < ; i++) { 
 var child = children[i]; 
 var classNames = (' '); 
 for (var j = 0; j < ; j++) { 
 if (classNames[j] == className) { 
 (child); 
 break; 
 } 
 } 
 } 

 return elements; 


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

/** 
* Element is like a java tool class, mainly used to hide/display/removal objects and obtain simple properties of objects.
 * 
 */ 
var Element = { 
 toggle: function() { 
 for (var i = 0; i < ; i++) { 
 var element = $(arguments[i]); 
  = 
 ( == 'none' ? '' : 'none'); 
 } 
 }, 

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

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

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

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


/** 
* A symbolic connection was made for  presumably for compatibility considerations
 */ 
var Toggle = new Object(); 
 = ; 

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

/** 
* Implementation of dynamic insertion of content, the object in the Jscript implementation of MS has an insertAdjacentHTML method (/workshop/author/dhtml/reference/methods/)
* This is considered an object-form encapsulation.
 */ 
 = function(adjacency) { 
  = adjacency; 


 = { 
 initialize: function(element, content) { 
  = $(element); 
  = content; 

 if ( && ) { 
 (, ); 
 } else { 
 /** 
* gecko does not support the insertAdjacentHTML method, but the following code can be used instead.
 */ 
  = (); 
 /** 
* If the initializeRange method is defined, it will be implemented. Here it is equivalent to defining an abstract initializeRange method.
 */ 
 if () (); 
  = (); 

 /** 
* insertContent is also an abstract method, and subclasses must be implemented
 */ 
 (); 
 } 
 } 


/** 
* prototype deepened my experience, which is to write js, how to follow the principle of Don't Repeat Yourself (DRY)
* The above is considered an abstract class, defining an abstract method called initializeRange
* var Insertion = new Object() Create a namespace
* |Top|Bottom|After is like four static inner classes in four javas, which are inherited from and implement the initializeRange method.
 */ 
var Insertion = new Object(); 

 = (); 
 = (new ('beforeBegin')).extend({ 
 initializeRange: function() { 
 (); 
 }, 

 /** 
* Insert the content in front of the specified node, at the same level as the specified node
 */ 
 insertContent: function() { 
 (, ); 
 } 
}); 

 = (); 
 = (new ('afterBegin')).extend({ 
 initializeRange: function() { 
 (); 
 (true); 
 }, 

 /** 
* Insert the content into the first child node of the specified node, and the content becomes the first child node of the node.
 */ 
 insertContent: function() { 
 (, ); 
 } 
}); 

 = (); 
 = (new ('beforeEnd')).extend({ 
 initializeRange: function() { 
 (); 
 (); 
 }, 

 /** 
* Insert the content at the end of the specified node, so the content becomes the last child node of the node.
 */ 
 insertContent: function() { 
 (); 
 } 
}); 

 = (); 
 = (new ('afterEnd')).extend({ 
 initializeRange: function() { 
 (); 
 }, 

 /** 
* Insert the content behind the specified node, at the same level as the specified node
 */ 
 insertContent: function() { 
 (, 
 ); 
 } 
}); 

/* This is the original version containing the error
if (!) { 
// Based on code from / 
 = function(object, parameters) { 
var parameterStrings = new Array(); 
if (!object) object = window; 
if (!parameters) parameters = new Array(); 

for (var i = 0; i < ; 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(object, parameters) { 
 var parameterStrings = new Array(); 
 if (!object) object = window; 
 if (!parameters) parameters = new Array(); 

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

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

 return result; 
 } 


A subclass of Effect




 = (); 
 = { 
 initialize: function(element, frequency) { 
  = $(element); 
  = frequency?frequency:1000; 
 .effect_blink = this; 
 (); 
 }, 

 blink: function() { 
 if () clearTimeout(); 
 try { 
  =  == 'hidden'?'visible':'hidden'; 
 } catch (e) {} 
  = setTimeout((this), ); 
 } 
};