var Ajax = {
getTransport: function() {
/*之前定义的Try.these终于派上用场了*/
return Try.these(
function() {return new XMLHttpRequest()},
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
) || false;
},
/*用来统计活动的连接数*/
activeRequestCount: 0
}
/*Ajax.Responders同样mixin了Enumerable对象*/
Ajax.Responders = {
responders: [],
_each: function(iterator) {
this.responders._each(iterator);
},
/*注册一个responder,重复注册没有任何副作用*/
/*参数为responder的名称*/
register: function(responder) {
if (!this.include(responder))
this.responders.push(responder);
},
/*撤销注册*/
/*参数为responder的名称*/
unregister: function(responder) {
this.responders = this.responders.without(responder);
},
/*遍历所有注册的responder,并在调用其上的用callback指示的函数,这些函数接受一个数组作为参数
数组包含request,transport,json三项*/
dispatch: function(callback, request, transport, json) {
this.each(function(responder) {
if (typeof responder[callback] == 'function') {
try {
responder[callback].apply(responder, [request, transport, json]);
} catch (e) {}
}
});
}
};
Object.extend(Ajax.Responders, Enumerable);
/*定义两个静态方法
onCreate和onComplete
*/
Ajax.Responders.register({
onCreate: function() {
/*做的工作很简单,增加一个活动链接数*/
Ajax.activeRequestCount++;
},
onComplete: function() {
Ajax.activeRequestCount--;
}
});
Ajax.Base = function() {};
Ajax.Base.prototype = {
/*Ajax.Base的每个实例都有一个setOptions方法,此方法接收一个哈希对象作为参数,或者不传入参数,使用默认值*/
/*哈希对象的格式为{param1:value1,param2:value2...}*/
/*parameters为一个字符串,形为param1=value1¶m2=value2....¶mn=valuen#dock*/
/*经过处理后parameters会被拆分然后填充到一个对象中去,对象的格式为
{param1=value1;param2=value2....paramn=valuen}
*/
/*默认值为
method: 'post',
asynchronous: true,
contentType: 'application/x-www-form-urlencoded',
encoding: 'UTF-8',
parameters: ''
可能含有的其他参数
postBody
requestHeaders
evalScripts(Ajax.Updater)
insertion(Ajax.Updater)
frequency(Ajax.PeriodicalUpdater)
decay(Ajax.PeriodicalUpdater)
可能含有的方法,用户只需要定义自己关心事件的回调函数。
onSuccess
onFailure
on2xx
on3xx
on4xx
on5xx
onUninitialized
onLoading
onLoaded
onInteractive
onComplete
*/
/*usage
ajaxBaseInstance.setOptions( {postBody:"...",onSuccess:function(){...},onComplete:function(){...}} );
*/
setOptions: function(options) {
this.options = {
method: 'post',
asynchronous: true,
contentType: 'application/x-www-form-urlencoded',
encoding: 'UTF-8',
parameters: ''
}
Object.extend(this.options, options || {});
this.options.method = this.options.method.toLowerCase();
if (typeof this.options.parameters == 'string')
this.options.parameters = this.options.parameters.toQueryParams();
}
}
Ajax.Request = Class.create();
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
_complete: false,
initialize: function(url, options) {
this.transport = Ajax.getTransport();
this.setOptions(options);
/*新建一个AjaxRequest对象的时候直接发起请求*/
this.request(url);
},
/* (如果state等于Complete) (如果response是js代码)
/*onCreate---->onLoading------> on2xx -------------->调用evalResponse-----------> onUninitialized------
on3xx onLoading |
on4xx onLoaded |
on5xx onInteractive | (transport状态更改会导致循环这个过程)
onSuccess onComplete |
onFailure |
|___________________________________________________________________|
注:所有这些函数均接受两个参数transport, json
*/
request: function(url) {
this.url = url;
this.method = this.options.method;
var params = Object.clone(this.options.parameters);
/*如果this.method既不是get也不是post(在setOptions的时候被重写了,可能是put或者delete之类的)
则把this.method的值保存在params._method中,并把this.method设置为post(因为不论put还是delete,
其实都要向服务器端提交数据,在现有绝大部分浏览器仅支持get和post的情况下可以用post来模拟)*/
if (!['get', 'post'].include(this.method)) {
// simulate other verbs over post
params['_method'] = this.method;
this.method = 'post';
}
/*将params对象保存到this.parameters对象中,params将会保存toQueryString后的结果*/
this.parameters = params;
if (params = Hash.toQueryString(params)) {
// when GET, append parameters to URL
/*这里多说一句,万一我传入的url包含锚点符号怎么办?。。。*/
if (this.method == 'get')
this.url += (this.url.include('?') ? '&' : '?') + params;
else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
params += '&_=';
}
try {
if (this.options.onCreate) this.options.onCreate(this.transport);
/*在创建Ajax调用之前*/
/*对所有注册的Responders调用它们的onCreate方法*/
Ajax.Responders.dispatch('onCreate', this, this.transport);
/*发起一个Ajax调用,默认使用异步方法*/
/*如果是同步的话会一直在这里等到连接建立或者超时*/
this.transport.open(this.method.toUpperCase(), this.url,
this.options.asynchronous);
/*如果是使用异步调用,等待10毫秒后将会调用respondToReadyState方法,参数为1表示Loading状态*/
/*人为的触发一个1xx事件(这个时候还没有使用onStateChange回调),以便于做一些等待时的处理,譬如显示一个Loading提示,或者进度条什么的*/
if (this.options.asynchronous)
setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
/*设置transport.onreadystatechange事件的回调函数为Ajax.Request.prototype.onStateChange*/
/*无论是同步还是异步,这个onStatechange事件都会被调用*/
this.transport.onreadystatechange = this.onStateChange.bind(this);
this.setRequestHeaders();
this.body = this.method == 'post' ? (this.options.postBody || params) : null;
/*发送请求*/
this.transport.send(this.body);
/* Force Firefox to handle ready state 4 for synchronous requests */
if (!this.options.asynchronous && this.transport.overrideMimeType)
this.onStateChange();
}
catch (e) {
this.dispatchException(e);
}
},
/*
1xx:信息响应类,表示接收到请求并且继续处理
2xx:处理成功响应类,表示动作被成功接收、理解和接受
3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
5xx:服务端错误,服务器不能正确执行一个正确的请求
*/
onStateChange: function() {
var readyState = this.transport.readyState;
if (readyState > 1 && !((readyState == 4) && this._complete/*在一个成功的调用之前,这个值一直都被设置为false*/))
this.respondToReadyState(this.transport.readyState);
},
/*核心是调用transport.setRequestHeader方法*/
setRequestHeaders: function() {
var headers = {
'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
};
if (this.method == 'post') {
headers['Content-type'] = this.options.contentType +
(this.options.encoding ? '; charset=' + this.options.encoding : '');
/* Force "Connection: close" for older Mozilla browsers to work
* around a bug where XMLHttpRequest sends an incorrect
* Content-length header. See Mozilla Bugzilla #246651.
*/
if (this.transport.overrideMimeType &&
(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
headers['Connection'] = 'close';
}
// user-defined headers
if (typeof this.options.requestHeaders == 'object') {
var extras = this.options.requestHeaders;
if (typeof extras.push == 'function')
for (var i = 0, length = extras.length; i < length; i += 2)
headers[extras[i]] = extras[i+1];
else
$H(extras).each(function(pair) { headers[pair.key] = pair.value });
}
for (var name in headers)
this.transport.setRequestHeader(name, headers[name]);
},
/*transport.status为false,null,undefined,NaN也表示success?*/
/*或者transport.status为2xx*/
success: function() {
return !this.transport.status
|| (this.transport.status >= 200 && this.transport.status < 300);
},
respondToReadyState: function(readyState) {
var state = Ajax.Request.Events[readyState];
var transport = this.transport, json = this.evalJSON();
/*查看this.options里面是否定义了onXXX(2xx~5xx)或者onSuccess或者onFailure方法*/
/*如果定义了就调用相应的方法,如果没有定义则调用空方法*/
if (state == 'Complete') {
try {
/*设置this._complete参数为true*/
this._complete = true;
(this.options['on' + this.transport.status]
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(transport, json);
} catch (e) {
this.dispatchException(e);
}
/*通过比较响应的Content-type来查看是否是javascript代码,如果是,就动态eval返回的js代码*/
var contentType = this.getHeader('Content-type');
if (contentType && contentType.strip().
match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
this.evalResponse();
}
/*根据返回结果不同执行不同的回调函数*/
/*
onUninitialized
onLoading
onLoaded
onInteractive
onComplete
*/
/*从代码可以看出来如果transport的状态码为4,这里会先后执行两个回调函数,
首先是onXXX(2xx~5xx)或者onSuccess或者onFailure方法
接着是上述函数之一*/
try {
(this.options['on' + state] || Prototype.emptyFunction)(transport, json);
Ajax.Responders.dispatch('on' + state, this, transport, json);
} catch (e) {
this.dispatchException(e);
}
if (state == 'Complete') {
// avoid memory leak in MSIE: clean up
this.transport.onreadystatechange = Prototype.emptyFunction;
}
},
getHeader: function(name) {
try {
return this.transport.getResponseHeader(name);
} catch (e) { return null }
},
evalJSON: function() {
try {
var json = this.getHeader('X-JSON');
return json ? json.evalJSON() : null;
} catch (e) { return null }
},
evalResponse: function() {
try {
return eval((this.transport.responseText || '').unfilterJSON());
} catch (e) {
this.dispatchException(e);
}
},
dispatchException: function(exception) {
/*首先进行统一的异常处理(通过定义this.options.onException方法
然后分别执行每个注册了的Responder的onException方法*/
(this.options.onException || Prototype.emptyFunction)(this, exception);
Ajax.Responders.dispatch('onException', this, exception);
}
});
Ajax.Updater = Class.create();
/*这里有一个小技巧是先mixin,然后复写其中需要的方法*/
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
initialize: function(container, url, options) {
/*这里用来指定Ajax调用成功和失败后分别用来更新的DOM对象
参数可以是一个代表指定DOM元素的字符串(无论成功失败均更新此DOM对象),也可以是一个包含success和failure属性的对象*/
this.container = {
success: (container.success || container),
failure: (container.failure || (container.success ? null : container))
}
this.transport = Ajax.getTransport();
this.setOptions(options);
/*在用户定义的onComplete事件前插入一个updateContent()事件*/
/*这个updateContent事件是已经定义好的,所以无论用户是否定义onComplete,
这个updateContent动作都是一定会执行的*/
var onComplete = this.options.onComplete || Prototype.emptyFunction;
this.options.onComplete = (function(transport, param) {
this.updateContent();
onComplete(transport, param);
}).bind(this);
this.request(url);
},
updateContent: function() {
var receiver = this.container[this.success() ? 'success' : 'failure'];
var response = this.transport.responseText;
/*如果用户没有在options中指定evalScripts属性,会自动过滤掉返回结果中的<script...></script>标签*/
if (!this.options.evalScripts) response = response.stripScripts();
if (receiver = $(receiver)) {
if (this.options.insertion)
new this.options.insertion(receiver, response);
else
receiver.update(response);
}
if (this.success()) {
if (this.onComplete)
setTimeout(this.onComplete.bind(this), 10);
}
}
});
/*周期性的更新某个DOM*/
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() {
/*当transport状态变为complete的时候调用updateComplete方法*/
this.options.onComplete = this.updateComplete.bind(this);
this.onTimerEvent();
},
stop: function() {
this.updater.options.onComplete = undefined;
clearTimeout(this.timer);
(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
},
/*这个方法主要有两个作用:
1.保存返回结果,主要是用来比较两次更新的结果,如果结果一直没有发生变化的话,更新的周期会呈指数性越来越长
2.设置一个定时器,在decay*frequency秒后进行下下一次更新
*/
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);
}
});
分享到:
相关推荐
其中里面包含了prototype1.5 prototype1.5.1 prototype1.5.1.1 prototype1.5.1.2 prototype1.6.0。... http://blog.csdn.net/boat1980/archive/2008/04/10/2278259.aspx这个连接有1.5.1.1代码的注释。
使用JSDoc等工具进行文档注释,不仅可以方便自动生成API文档,还提高了代码的可读性和可维护性。例如: ```javascript /** * @function addNumbers * @param {number} num1 - 第一个数字 * @param {number} ...
- **JSDoc介绍**:一种为JavaScript代码添加文档注释的方法。 - **安装与使用**:指导如何安装JSDoc插件,并编写注释文档。 **5.2 使用Firefox扩展验证HTML内容** - **HTMLValidator**:用于检查HTML文档是否符合...
1.5保护网页源代码 1.6 保护自己的网页不被放入框架 1.7 保护自己的网页不被放入框架 1.8 打印页面的出错原因 1.9 当前网页调用其他网页 1.10 倒计时载入页面 1.11 定义网页的关键字 1.12 进入页面同时弹出欢迎...
1.5保护网页源代码 1.6 保护自己的网页不被放入框架 1.7 保护自己的网页不被放入框架 1.8 打印页面的出错原因 1.9 当前网页调用其他网页 1.10 倒计时载入页面 1.11 定义网页的关键字 1.12 进入页面同时弹出欢迎...