Prototype1.5.1.1代码注释(二)

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&param2=value2....&paramn=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);
  }
});
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值