var Request = new Class({

 //̳Chain,EventsOptionsĽӿʵ
 Implements: [Chain, Events, Options],

 options: {
  /*onRequest: $empty, //ʼ¼
  onSuccess: $empty,  //ɹ¼
  onFailure: $empty,  //ʧ¼
  onException: $empty,*/ //쳣¼
  url: '',    //Ajaxĵַ
  data: '',    //Ajaxύĵַ
  headers: {    //AjaxHTTPͷ
   //ԴϢ
   'X-Requested-With': 'XMLHttpRequest',
   //ܵ
   'Accept': 'text/javascrpt, text/html, application/xml, text/xml, */*'
  },
  //Ƿ첽
  async: true,
  //ύʽ
  method: 'post',
  //ͻʱĴʽ
  link: 'ignore',
  //ж״̬ɹķ
  isSuccess: null,
  //ж״̬ɹķ
  emulation: true,
  //Ƿʹurl
  urlEncoded: true,
  //POSTʱʹõı
  encoding: 'utf-8',
  //ִַ̬ű
  evalscrpts: 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));
 },

 //ַаĽű
 processscrpts: function(text){
  //ַΪűֱӽִ
  if (this.options.evalResponse || (/(ecma|java)scrpt/).test(this.getHeader('Content-type'))) return $exec(text);
  //ַʱ˳űòǷִаű
  return text.stripscrpts(this.options.evalscrpts);
 },

 //ɹ
 success: function(text, xml){
  //¼
  this.onSuccess(this.processscrpts(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') ptions = {data: options};

  //ʵָʹ,ԽòӺsendʱ
  var ld = 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);
  }

  //putdeleteύʽʱĴ,ΪAjax֧putdelete,ͨһ_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');
 }

};

//Elementsend
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;
 }

});

