原帖地址:http://space.flash8.net/space/?18713/viewspace-406767.html
原作者:我佛山人
var
Request
=
new
Class({
// 继承Chain,Events和Options的接口实现
Implements: [Chain, Events, Options],
options: {
/* onRequest: $empty, //开始事件
onSuccess: $empty, //成功事件
onFailure: $empty, //失败事件
onException: $empty, */ // 异常事件
url: '' , // Ajax请求的地址
data: '' , // Ajax提交的地址
headers: { // Ajax请求的HTTP头数据
// 来源信息
' X-Requested-With ' : ' XMLHttpRequest ' ,
// 接受的内容类型
' Accept ' : ' text/javascrīpt, text/html, application/xml, text/xml, */* '
},
// 是否异步请求
async: true ,
// 提交方式
method: ' post ' ,
// 多次请求冲突时的处理方式
link: ' ignore ' ,
// 判断状态成功的方法
isSuccess: null ,
// 判断状态成功的方法
emulation: true ,
// 是否使用url编码
urlEncoded: true ,
// POST请求时使用的编码
encoding: ' utf-8 ' ,
// 动态执行字符脚本
evalscrīpts: false ,
// 动态执行返回字符脚本
evalResponse: false
},
// 构造函数
initialize: function (options) {
// 使用浏览器特性创建一个XmlHttpRequest对象
this .xhr = new Browser.Request();
this .setOptions(options);
// 判断状态成功的方法,如果参数中不提供,使用默认方法
this .options.isSuccess = this .options.isSuccess || this .isSuccess;
this .headers = new Hash( this .options.headers);
},
onStateChange: function () {
// 如果状态码不等于4或者没有正执行的请求,直接返回
if ( this .xhr.readyState != 4 || ! this .running) return ;
// 设置标记
this .running = false ;
// 初始化状态码
this .status = 0 ;
// 设置状态码
$ try ( function () {
this .status = this .xhr.status;
}.bind( this ));
// 如果状态码显示请求成功
if ( this .options.isSuccess.call( this , this .status)) {
// 提取响应数据
this .response = {text: this .xhr.responseText, xml: this .xhr.responseXML};
// 请求成功
this .success( this .response.text, this .response.xml);
} else {
// 响应数据全为null
this .response = {text: null , xml: null };
// 请求失败
this .failure();
}
// 置空状态改变事件监听
this .xhr.onreadystatechange = $empty;
},
// 判断请求状态是否成功
isSuccess: function () {
// 状态码大于等于200小于300时为成功
return (( this .status >= 200 ) && ( this .status < 300 ));
},
// 处理返回字符中包含的脚本
processscrīpts: function (text) {
// 如果返回字符为纯脚本,直接解释执行
if ( this .options.evalResponse || ( / (ecma|java)scrīpt / ).test( this .getHeader( ' Content-type ' ))) return $exec(text);
// 混合字符时过滤出脚本,根据配置参数是否执行包含脚本
return text.stripscrīpts( this .options.evalscrīpts);
},
// 请求成功
success: function (text, xml) {
// 触发事件
this .onSuccess( this .processscrīpts(text), xml);
},
// 触发请求成功事件
onSuccess: function () {
// 同时触发请求完成和请求成功事件
this .fireEvent( ' onComplete ' , arguments).fireEvent( ' onSuccess ' , arguments).callChain();
},
// 请求失败
failure: function () {
// 触发事件
this .onFailure();
},
// 触发请求失败事件
onFailure: function () {
// 同时触发请求完成和请求失败事件
this .fireEvent( ' onComplete ' ).fireEvent( ' onFailure ' , this .xhr);
},
// 设置请求的HTTP头部数据
setHeader: function (name, value) {
this .headers.set(name, value);
return this ;
},
// 获取响应的HTTP头部数据
getHeader: function (name) {
return $ try ( function () {
return this .xhr.getResponseHeader(name);
}.bind( this ));
},
// 检查约束,类似Fx的同名方法
check: function () {
// 如果请求线程没有在运行中,返回真允许运行当前线程的请求
if ( ! this .running) return true ;
// 如果当前有正运行的请求线程
switch ( this .options.link) {
// 配置了link参数为cancel时,直接取消正执行的线程,启用新线程
case ' cancel ' : this .cancel(); return true ;
// 等待当前线程执行完成再执行新线程
case ' chain ' : this .chain( this .send.bind( this , arguments)); return false ;
}
return false ;
},
// 发送请求
send: function (options) {
// 检查约束
if ( ! this .check(options)) return this ;
// 设置标记
this .running = true ;
// 根据参数类型处理
var type = $type(options);
if (type == ' string ' || type == ' element ' ) options = {data: options};
// 实现更灵活的使用,可以将配置参数延后在send时
var old = this .options;
options = $extend({data: old.data, url: old.url, method: old.method}, options);
var data = options.data, url = options.url, method = options.method;
// 不同类型的处理
switch ($type(data)) {
// 如果是HTML Element,读取它的值,并根据name值构成name=value的名值对
case ' element ' : data = $(data).toQueryString(); break ;
case ' object ' : case ' hash ' : data = Hash.toQueryString(data);
}
// put和delete提交方式时的处理,因为Ajax不支持put和delete,所以通过添加一个_method给后台处理
if ( this .options.emulation && [ ' put ' , ' delete ' ].contains(method)) {
var _method = ' _method= ' + method;
data = (data) ? _method + ' & ' + data : _method;
method = ' post ' ;
}
// POST方式指定编码时的处理,需要设置HTTP头部信息
if ( this .options.urlEncoded && method == ' post ' ) {
var encoding = ( this .options.encoding) ? ' ; charset= ' + this .options.encoding : '' ;
this .headers.set( ' Content-type ' , ' application/x-www-form-urlencoded ' + encoding);
}
// GET方式时的处理,直接将提交的数据加在请求地址后面
if (data && method == ' get ' ) {
// 判断请求地址中有否包含问号
url = url + (url.contains( ' ? ' ) ? ' & ' : ' ? ' ) + data;
data = null ;
}
// 打开请求端口,注意以下代码中xhr对象的方法的执行顺序
this .xhr.open(method.toUpperCase(), url, this .options.async);
// 监听请求的响应状态更改事件
this .xhr.onreadystatechange = this .onStateChange.bind( this );
// 设置所有HTTP头部信息
this .headers.each( function (value, key) {
if ( ! $ try ( function () {
this .xhr.setRequestHeader(key, value);
return true ;
// 出错时触发异常事件
}.bind( this ))) this .fireEvent( ' onException ' , [key, value]);
}, this );
// 触发onRequest事件
this .fireEvent( ' onRequest ' );
// 发送数据
this .xhr.send(data);
// 如果是同步执行,不需要事件监听,直接进到状态处理方法
if ( ! this .options.async) this .onStateChange();
return this ;
},
// 取消请求
cancel: function () {
// 如果没有正运行的线程,不必操作,直接返回
if ( ! this .running) return this ;
// 设置标记
this .running = false ;
// 中止请求
this .xhr.abort();
// 置状态改变监听为空
this .xhr.onreadystatechange = $empty;
// 创建新请求线程
this .xhr = new Browser.Request();
// 触发onCancel事件
this .fireEvent( ' onCancel ' );
return this ;
}
});
// 就地执行的匿名函数
( function () {
// HTTP的方法处理
var methods = {};
// 处理用于Request继承实现的方法
[ ' get ' , ' post ' , ' GET ' , ' POST ' , ' PUT ' , ' DELETE ' ].each( function (method) {
methods[method] = function () {
// 优先处理参数中字符类型的为请求地址,另一个有定义的参数为提交数据
var params = Array.link(arguments, {url: String.type, data: $defined});
// 最终仍需要通过调用send方法执行
return this .send($extend(params, {method: method.toLowerCase()}));
};
});
/*
扩展实现,现在可以使用Request.get,Request.put之类的方法了,并且方法名不区分大小写,如:
Request.get('http://wfsr.net/action.aspx', 'id=1');
*/
Request.implement(methods);
})();
// 单件模式,根据Request为Element实现的扩展
Element.Properties.send = {
// setter
set: function (options) {
// 先从临时对象取实例
var send = this .retrieve( ' send ' );
// 如果存在,取消执行
if (send) send.cancel();
// 删除缓存的Request实例,缓存配置参数
return this .eliminate( ' send ' ).store( ' send:options ' , $extend({
data: this , link: ' cancel ' , method: this .get( ' method ' ) || ' post ' , url: this .get( ' action ' )
}, options));
},
// getter
get: function (options) {
// 如果提供了配置参数并且未存在实例
if (options || ! this .retrieve( ' send ' )) {
// 如果没有缓存配置参数,调用setter缓存
if (options || ! this .retrieve( ' send:options ' )) this .set( ' send ' , options);
// 根据缓存参数,缓存Request实例
this .store( ' send ' , new Request( this .retrieve( ' send:options ' )));
}
// 返回缓存中的实例
return this .retrieve( ' send ' );
}
};
// Element的send方法
Element.implement({
// Element可以直接执行send方法实现Ajax请求了
send: function (url) {
// 读取缓存中的Request实例
var sender = this .get( ' send ' );
// 发送请求,Request的配置参数需要单独设置
sender.send({data: this , url: url || sender.options.url});
return this ;
}
});
// 继承Chain,Events和Options的接口实现
Implements: [Chain, Events, Options],
options: {
/* onRequest: $empty, //开始事件
onSuccess: $empty, //成功事件
onFailure: $empty, //失败事件
onException: $empty, */ // 异常事件
url: '' , // Ajax请求的地址
data: '' , // Ajax提交的地址
headers: { // Ajax请求的HTTP头数据
// 来源信息
' X-Requested-With ' : ' XMLHttpRequest ' ,
// 接受的内容类型
' Accept ' : ' text/javascrīpt, text/html, application/xml, text/xml, */* '
},
// 是否异步请求
async: true ,
// 提交方式
method: ' post ' ,
// 多次请求冲突时的处理方式
link: ' ignore ' ,
// 判断状态成功的方法
isSuccess: null ,
// 判断状态成功的方法
emulation: true ,
// 是否使用url编码
urlEncoded: true ,
// POST请求时使用的编码
encoding: ' utf-8 ' ,
// 动态执行字符脚本
evalscrīpts: false ,
// 动态执行返回字符脚本
evalResponse: false
},
// 构造函数
initialize: function (options) {
// 使用浏览器特性创建一个XmlHttpRequest对象
this .xhr = new Browser.Request();
this .setOptions(options);
// 判断状态成功的方法,如果参数中不提供,使用默认方法
this .options.isSuccess = this .options.isSuccess || this .isSuccess;
this .headers = new Hash( this .options.headers);
},
onStateChange: function () {
// 如果状态码不等于4或者没有正执行的请求,直接返回
if ( this .xhr.readyState != 4 || ! this .running) return ;
// 设置标记
this .running = false ;
// 初始化状态码
this .status = 0 ;
// 设置状态码
$ try ( function () {
this .status = this .xhr.status;
}.bind( this ));
// 如果状态码显示请求成功
if ( this .options.isSuccess.call( this , this .status)) {
// 提取响应数据
this .response = {text: this .xhr.responseText, xml: this .xhr.responseXML};
// 请求成功
this .success( this .response.text, this .response.xml);
} else {
// 响应数据全为null
this .response = {text: null , xml: null };
// 请求失败
this .failure();
}
// 置空状态改变事件监听
this .xhr.onreadystatechange = $empty;
},
// 判断请求状态是否成功
isSuccess: function () {
// 状态码大于等于200小于300时为成功
return (( this .status >= 200 ) && ( this .status < 300 ));
},
// 处理返回字符中包含的脚本
processscrīpts: function (text) {
// 如果返回字符为纯脚本,直接解释执行
if ( this .options.evalResponse || ( / (ecma|java)scrīpt / ).test( this .getHeader( ' Content-type ' ))) return $exec(text);
// 混合字符时过滤出脚本,根据配置参数是否执行包含脚本
return text.stripscrīpts( this .options.evalscrīpts);
},
// 请求成功
success: function (text, xml) {
// 触发事件
this .onSuccess( this .processscrīpts(text), xml);
},
// 触发请求成功事件
onSuccess: function () {
// 同时触发请求完成和请求成功事件
this .fireEvent( ' onComplete ' , arguments).fireEvent( ' onSuccess ' , arguments).callChain();
},
// 请求失败
failure: function () {
// 触发事件
this .onFailure();
},
// 触发请求失败事件
onFailure: function () {
// 同时触发请求完成和请求失败事件
this .fireEvent( ' onComplete ' ).fireEvent( ' onFailure ' , this .xhr);
},
// 设置请求的HTTP头部数据
setHeader: function (name, value) {
this .headers.set(name, value);
return this ;
},
// 获取响应的HTTP头部数据
getHeader: function (name) {
return $ try ( function () {
return this .xhr.getResponseHeader(name);
}.bind( this ));
},
// 检查约束,类似Fx的同名方法
check: function () {
// 如果请求线程没有在运行中,返回真允许运行当前线程的请求
if ( ! this .running) return true ;
// 如果当前有正运行的请求线程
switch ( this .options.link) {
// 配置了link参数为cancel时,直接取消正执行的线程,启用新线程
case ' cancel ' : this .cancel(); return true ;
// 等待当前线程执行完成再执行新线程
case ' chain ' : this .chain( this .send.bind( this , arguments)); return false ;
}
return false ;
},
// 发送请求
send: function (options) {
// 检查约束
if ( ! this .check(options)) return this ;
// 设置标记
this .running = true ;
// 根据参数类型处理
var type = $type(options);
if (type == ' string ' || type == ' element ' ) options = {data: options};
// 实现更灵活的使用,可以将配置参数延后在send时
var old = this .options;
options = $extend({data: old.data, url: old.url, method: old.method}, options);
var data = options.data, url = options.url, method = options.method;
// 不同类型的处理
switch ($type(data)) {
// 如果是HTML Element,读取它的值,并根据name值构成name=value的名值对
case ' element ' : data = $(data).toQueryString(); break ;
case ' object ' : case ' hash ' : data = Hash.toQueryString(data);
}
// put和delete提交方式时的处理,因为Ajax不支持put和delete,所以通过添加一个_method给后台处理
if ( this .options.emulation && [ ' put ' , ' delete ' ].contains(method)) {
var _method = ' _method= ' + method;
data = (data) ? _method + ' & ' + data : _method;
method = ' post ' ;
}
// POST方式指定编码时的处理,需要设置HTTP头部信息
if ( this .options.urlEncoded && method == ' post ' ) {
var encoding = ( this .options.encoding) ? ' ; charset= ' + this .options.encoding : '' ;
this .headers.set( ' Content-type ' , ' application/x-www-form-urlencoded ' + encoding);
}
// GET方式时的处理,直接将提交的数据加在请求地址后面
if (data && method == ' get ' ) {
// 判断请求地址中有否包含问号
url = url + (url.contains( ' ? ' ) ? ' & ' : ' ? ' ) + data;
data = null ;
}
// 打开请求端口,注意以下代码中xhr对象的方法的执行顺序
this .xhr.open(method.toUpperCase(), url, this .options.async);
// 监听请求的响应状态更改事件
this .xhr.onreadystatechange = this .onStateChange.bind( this );
// 设置所有HTTP头部信息
this .headers.each( function (value, key) {
if ( ! $ try ( function () {
this .xhr.setRequestHeader(key, value);
return true ;
// 出错时触发异常事件
}.bind( this ))) this .fireEvent( ' onException ' , [key, value]);
}, this );
// 触发onRequest事件
this .fireEvent( ' onRequest ' );
// 发送数据
this .xhr.send(data);
// 如果是同步执行,不需要事件监听,直接进到状态处理方法
if ( ! this .options.async) this .onStateChange();
return this ;
},
// 取消请求
cancel: function () {
// 如果没有正运行的线程,不必操作,直接返回
if ( ! this .running) return this ;
// 设置标记
this .running = false ;
// 中止请求
this .xhr.abort();
// 置状态改变监听为空
this .xhr.onreadystatechange = $empty;
// 创建新请求线程
this .xhr = new Browser.Request();
// 触发onCancel事件
this .fireEvent( ' onCancel ' );
return this ;
}
});
// 就地执行的匿名函数
( function () {
// HTTP的方法处理
var methods = {};
// 处理用于Request继承实现的方法
[ ' get ' , ' post ' , ' GET ' , ' POST ' , ' PUT ' , ' DELETE ' ].each( function (method) {
methods[method] = function () {
// 优先处理参数中字符类型的为请求地址,另一个有定义的参数为提交数据
var params = Array.link(arguments, {url: String.type, data: $defined});
// 最终仍需要通过调用send方法执行
return this .send($extend(params, {method: method.toLowerCase()}));
};
});
/*
扩展实现,现在可以使用Request.get,Request.put之类的方法了,并且方法名不区分大小写,如:
Request.get('http://wfsr.net/action.aspx', 'id=1');
*/
Request.implement(methods);
})();
// 单件模式,根据Request为Element实现的扩展
Element.Properties.send = {
// setter
set: function (options) {
// 先从临时对象取实例
var send = this .retrieve( ' send ' );
// 如果存在,取消执行
if (send) send.cancel();
// 删除缓存的Request实例,缓存配置参数
return this .eliminate( ' send ' ).store( ' send:options ' , $extend({
data: this , link: ' cancel ' , method: this .get( ' method ' ) || ' post ' , url: this .get( ' action ' )
}, options));
},
// getter
get: function (options) {
// 如果提供了配置参数并且未存在实例
if (options || ! this .retrieve( ' send ' )) {
// 如果没有缓存配置参数,调用setter缓存
if (options || ! this .retrieve( ' send:options ' )) this .set( ' send ' , options);
// 根据缓存参数,缓存Request实例
this .store( ' send ' , new Request( this .retrieve( ' send:options ' )));
}
// 返回缓存中的实例
return this .retrieve( ' send ' );
}
};
// Element的send方法
Element.implement({
// Element可以直接执行send方法实现Ajax请求了
send: function (url) {
// 读取缓存中的Request实例
var sender = this .get( ' send ' );
// 发送请求,Request的配置参数需要单独设置
sender.send({data: this , url: url || sender.options.url});
return this ;
}
});