protoype.js学习之Ajax对象(三)

protoype.js学习之Ajax对象(三)

  1  /* *
  2   * Ajax类,getTransport类方法返回一个xmlhttp对象,注意这里就用到了Try.these
  3    */
  4  var  Ajax  =  {
  5      getTransport:  function () {
  6           return  Try.these(
  7                   function () {
  8                       return   new  XMLHttpRequest()
  9                  },
 10                   function () {
 11                       return   new  ActiveXObject('Msxml2.XMLHTTP')
 12                  },
 13                   function () {
 14                       return   new  ActiveXObject('Microsoft.XMLHTTP')
 15                  }
 16                  )  ||   false ;
 17      },
 18 
 19      activeRequestCount:  0
 20  }
 21 
 22  Ajax.Responders  =  {
 23      responders: [],
 24 
 25      _each:  function (iterator) {
 26           this .responders._each(iterator);
 27      },
 28 
 29      register:  function (responderToAdd) {
 30           if  ( ! this .include(responderToAdd))
 31               this .responders.push(responderToAdd);
 32      },
 33 
 34      unregister:  function (responderToRemove) {
 35           this .responders  =   this .responders.without(responderToRemove);
 36      },
 37 
 38      dispatch:  function (callback, request, transport, json) {
 39           this .each( function (responder) {
 40               if  (responder[callback]  &&   typeof  responder[callback]  ==  ' function ') {
 41                   try  {
 42                      responder[callback].apply(responder, [request, transport, json]);
 43                  }  catch  (e) {
 44                  }
 45              }
 46          });
 47      }
 48  };
 49 
 50  Object.extend(Ajax.Responders, Enumerable);
 51 
 52  Ajax.Responders.register({
 53      onCreate:  function () {
 54          Ajax.activeRequestCount ++ ;
 55      },
 56 
 57      onComplete:  function () {
 58          Ajax.activeRequestCount -- ;
 59      }
 60  });
 61  /* *
 62   *  Ajax.Base 声明为一个基础对象类型,这里没有使用Class.create()方法,
 63   *  因为Ajax.Base不需要实例化,只是作为基类,让子类来继承。
 64    */
 65  Ajax.Base  =   function () {
 66  };
 67  /* *
 68   * Ajax.Base提供了一些Ajax.Request Ajax.PeriodicalUpdater共享的方法
 69   * 这些方法被放在一个基类中,避免在Ajax.Request和Ajax.PeriodicalUpdater都实现。他们继承Ajax.Base
 70   * The Ajax Object的options决定了怎样的Ajax Request执行,在Ajax Request初始化时,
 71   * options首先设置默认属性,然后再extend参数对象,参数对象中有同名的属性,就覆盖默认的属性值
 72   * 有一些属性是Ajax请求必须的,所以在Ajax.base对象中设置了默认值
 73   *                      
 74    */
 75  Ajax.Base.prototype  =  {
 76      setOptions:  function (options) {
 77           this .options  =  {
 78              method:       'post',
 79              asynchronous:  true ,
 80              contentType:  'application / x - www - form - urlencoded',
 81              parameters:   ''
 82          }
 83          Object.extend( this .options, options  ||  {});
 84      },
 85       // 下面两个方法判断是否正常返回了响应
 86      responseIsSuccess:  function () {
 87           return   this .transport.status  ==  undefined
 88                   ||   this .transport.status  ==   0
 89                   ||  ( this .transport.status  >=   200   &&   this .transport.status  <   300 );
 90      },
 91 
 92      responseIsFailure:  function () {
 93           return   ! this .responseIsSuccess();
 94      }
 95  }
 96  // Request对象,这回用了Class.create(),因为下面initialize初始化
 97  Ajax.Request  =  Class.create();
 98 
 99  // Events对象,对应XMLHttpRequest中的请求状态
100  // Uninitialized: 未初始化;Loading: 正在加载 ;Loaded:已加载;Interactive:交互中;Complete:完成
101 
102  Ajax.Request.Events  =
103  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
104 
105  /* *
106   * Ajax.Request继承了Ajax.Base,并且添加了一个构造方法(initialize)
107   * initialize方法是必须的,因为Ajax请求属性继承Ajax.Base的后,至少还需要一个请求URL,才能工作。
108   * 有趣的是,初始化方法中调用了一个request的方法,所以请求被立刻执行,当Ajax.Request实例被初始化时。
109    */
110  Ajax.Request.prototype  =  Object.extend( new  Ajax.Base(), {
111      initialize:  function (url, options) {
112           this .transport  =  Ajax.getTransport();
113           this .setOptions(options); // 设置请求的的属性,调用Ajax.Base方法
114           this .request(url);
115      },
116 
117  /* *
118   * 在Prototype中,一个Ajax.Request是一个到服务器的一个异步的请求,一个没有处理的response返回给调用者。
119    */
120      request:  function (url) {
121           // 从Ajax.Base’s options对象中,获得请求参数字符串,如果this.options.parameters为空,请求参数被设空。
122           // 如果参数不为空,在请求参数后添加"&_=",应该是确保url的唯一性,避免浏览器缓存请求的结果。
123           var  parameters  =   this .options.parameters  ||  '';
124           if  (parameters.length  >   0 ) parameters  +=  ' & _ = ';
125          
126           /*  Simulate other verbs over post  */
127           if  ( this .options.method  !=  'get'  &&   this .options.method  !=  'post') {
128              parameters  +=  (parameters.length  >   0   ?  ' & ' : '')  +  '_method = +   this .options.method;
129               this .options.method  =  'post';
130          }
131           // 下面请求将被发送,为了捕捉网络的异常,相关的代码被放到try块中
132           try  {
133               this .url  =  url;
134               // 如果请求方法是get,参数将被连接到URL后
135               if  ( this .options.method  ==  'get'  &&  parameters.length  >   0 )
136                   this .url  +=  ( this .url.match( / \ ?/ ?  ' & ' : ' ? ')  +  parameters;
137                  
138               // onCreate事件被发给Responders对象,
139               // 请求开始 ,Ajax.Responders中注册onCreate的方法就会执行
140              Ajax.Responders.dispatch('onCreate',  this this .transport);
141              
142               // XMLHttpRequest.open()方法,建立对服务器的调用。
143               this .transport.open( this .options.method,  this .url,
144                       this .options.asynchronous);
145                      
146               // 这里提供了XMLHttpRequest传输过程中每个步骤的回调函数
147               // 每10ms会调用一次这个方法,传的参数是Loading
148               if  ( this .options.asynchronous)
149                  setTimeout( function () {
150                       this .respondToReadyState( 1 )
151                  }.bind( this ),  10 );
152                  
153               // XMLHttpRequest状态改变时,onStateChange方法调用。
154               this .transport.onreadystatechange  =   this .onStateChange.bind( this );
155              
156               // 设置request头
157               this .setRequestHeaders();
158              
159               // 如果方法是post,参数被作为POST headers 被send()方法发送出去,否则,什么也不发送。
160               var  body  =   this .options.postBody  ?   this .options.postBody : parameters;
161               this .transport.send( this .options.method  ==  'post'  ?  body :  null );
162 
163               /*  Force Firefox to handle ready state 4 for synchronous requests  */
164               if  ( ! this .options.asynchronous  &&   this .transport.overrideMimeType)
165                   this .onStateChange();
166 
167          }  catch  (e) {
168              
169               this .dispatchException(e);
170          }
171      },
172      
173       // 设置request头
174      setRequestHeaders:  function () {
175           // 默认的请求头被建立,requestHeaders是个数组
176           var  requestHeaders  =
177                  ['X - Requested - With', 'XMLHttpRequest',
178                          'X - Prototype - Version', Prototype.Version,
179                          'Accept', 'text / javascript, text / html, application / xml, text / xml,  * /* '];
180          //如果请求用post方法,Content-type将被添加到请求头中
181          if (this.options.method == 'post') {
182              requestHeaders.push('Content-type', this.options.contentType);
183 
184              /* Force "Connection: close" for Mozilla browsers to work around
185                          * a bug where XMLHttpReqeuest sends an incorrect Content-length
186                          * header. See Mozilla Bugzilla #246651.
187                           */
188               if  ( this .transport.overrideMimeType)
189                  requestHeaders.push('Connection', 'close');
190          }
191           // 如果在request的options中有requestHeaders属性
192           // 它们将添加到requestHeaders
193           if  ( this .options.requestHeaders)
194              requestHeaders.push.apply(requestHeaders,  this .options.requestHeaders);
195              
196           // requestHeaders中每一个key-value对,通过XMLhttpRequest的setRequestHeader方法设置请求实例的请求头属性中
197          
198           for  ( var  i  =   0 ; i  <  requestHeaders.length; i  +=   2 )
199               this .transport.setRequestHeader(requestHeaders[i], requestHeaders[i  +   1 ]);
200      },
201       /* *
202       * 状态变化,并且readyState不是Loading时就调用回调函数。
203        */
204      onStateChange:  function () {
205           var  readyState  =   this .transport.readyState;
206           if  (readyState  !=   1 )
207               this .respondToReadyState( this .transport.readyState);
208      },
209      
210       // 根据name取得XMLHttpRequest中指定的HTTP头
211      header:  function (name) {
212           try  {
213               return   this .transport.getResponseHeader(name);
214          }  catch  (e) {
215          }
216      },
217       // 执行HTTP头中'X-JSON'对应的内容
218      evalJSON:  function () {
219           try  {
220               return  eval('('  +   this .header('X - JSON')  +  ')');
221          }  catch  (e) {
222          }
223      },
224      
225  /* *
226   * 执行包含在response text中的脚本,并返回结果
227    */
228      evalResponse:  function () {
229           try  {
230               return  eval( this .transport.responseText);
231          }  catch  (e) {
232               this .dispatchException(e);
233          }
234      },
235 
236      respondToReadyState:  function (readyState) {
237           // readyState将被映射到Events定义得事件
238           var  event  =  Ajax.Request.Events[readyState];
239           // 获取当前的XMLHttpRequest对象,执行HTTP头中'X-JSON'对应的内容
240           var  transport  =   this .transport, json  =   this .evalJSON();
241           // 如果event == 'Complete',response响应被接受。
242           if  (event  ==  'Complete') {
243               try  { // 按优先级触发事件 如果处理函数不存在则执行emptyFunction忽略此事件
244                  ( this .options['on'  +   this .transport.status]
245                           ||   this .options['on'  +  ( this .responseIsSuccess()  ?  'Success' : 'Failure')]
246                           ||  Prototype.emptyFunction)(transport, json);
247              }  catch  (e) {
248                   this .dispatchException(e);
249              }
250               // 如果响应的Content-type是 text/javascript,prototype将用evalResponse方法执行包含在请求的js代码
251               if  (( this .header('Content - type')  ||  '').match( /^ text\ / javascript / i))
252                   this .evalResponse();
253          }
254       // XMLHttpRequest状态的变化,onLoaded, onInteractive, onComplete方法调用并传json对象
255       // 同时事件还发送给Responders对象
256           try  {
257              ( this .options['on'  +  event]  ||  Prototype.emptyFunction)(transport, json);
258              Ajax.Responders.dispatch('on'  +  event,  this , transport, json);
259          }  catch  (e) {
260               this .dispatchException(e);
261          }
262 
263           /*  Avoid memory leak in MSIE: clean up the oncomplete event handler  */
264           if  (event  ==  'Complete')
265               this .transport.onreadystatechange  =  Prototype.emptyFunction;
266      },
267 
268      dispatchException:  function (exception) { //  处理异常,Prototype.emptyFunction为默认的处理方法
269          ( this .options.onException  ||  Prototype.emptyFunction)( this , exception);
270          Ajax.Responders.dispatch('onException',  this , exception);
271      }
272  });
273 
274  /* *
275   * Ajax.Updater继承Ajax.Request,是一格Ajax.Request功能增强类。它执行和Ajax.Request同样的任务,
276   * 但是response不仅可以传递个onComplete处理,还可以响应后的HTML结果自动放到指定的container
277    */
278  Ajax.Updater  =  Class.create();
279 
280  Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
281       // 初始化方法比Ajax.Request多了一个container参数,response
282       // 将被放置在container中
283      initialize:  function (container, url, options) {
284           // success:请求响应成功时,接收response的容器对象
285           this .containers  =  {
286              success: container.success  ?  $(container.success) : $(container),
287              failure: container.failure  ?  $(container.failure) :
288                       (container.success  ?   null  : $(container))
289          }
290 
291           this .transport  =  Ajax.getTransport();
292           this .setOptions(options);
293 
294           var  onComplete  =   this .options.onComplete  ||  Prototype.emptyFunction;
295          
296           // onComplete扩展包含一个对updateContent()方法的调用
297           this .options.onComplete  =  ( function (transport, object) {
298               this .updateContent();
299              onComplete(transport, object);
300          }).bind( this );
301 
302           this .request(url);
303      },
304 
305      updateContent:  function () {
306           // 如果AJAX请求响应成功,则获取接收的容器对象,否则获取请求错误时的容器对象。
307           var  receiver  =   this .responseIsSuccess()  ?
308                          this .containers.success :  this .containers.failure;
309           var  response  =   this .transport.responseText;
310          
311           // 如果this.options.evalScripts为false,从response移出所有的JavaScript脚本
312           if  ( ! this .options.evalScripts)
313              response  =  response.stripScripts();
314      
315           // 如果接收对象存在
316           if  (receiver) {
317               // 判断是将响应文本insertion还是update到receiver对象中
318               if  ( this .options.insertion) {
319                   new   this .options.insertion(receiver, response);
320              }  else  {
321                  Element.update(receiver, response);
322              }
323          }
324 
325           if  ( this .responseIsSuccess()) {
326               if  ( this .onComplete)
327                  setTimeout( this .onComplete.bind( this ),  10 );
328          }
329      }
330  });
331 
332  /* *
333   * Ajax.PeriodicalUpdater对Ajax.Updater进行包装。对Ajax.Updater添加了一个定时器。
334   * 周期性的执行Ajax.Updater对象来更新DOM element
335    */
336  Ajax.PeriodicalUpdater  =  Class.create();
337  Ajax.PeriodicalUpdater.prototype  =  Object.extend( new  Ajax.Base(), {
338      initialize:  function (container, url, options) {
339           this .setOptions(options);
340           this .onComplete  =   this .options.onComplete;
341 
342           this .frequency  =  ( this .options.frequency  ||   2 ); // 两次刷新之间的间隔 
343           this .decay  =  ( this .options.decay  ||   1 ); // 重负执行任务的时候保持的衰败水平
344 
345           this .updater  =  {};
346           this .container  =  container;
347           this .url  =  url;
348 
349           this .start(); // 开始Ajax.Updater循环
350      },
351 
352      start:  function () {
353           // 使options的onComplete的指针指向当前的Ajax.PeriodicalUpdater对的updateComplete方法。
354           this .options.onComplete  =   this .updateComplete.bind( this );
355           this .onTimerEvent();
356      },
357 
358      stop:  function () {
359           this .updater.options.onComplete  =  undefined;
360          clearTimeout( this .timer);
361          ( this .onComplete  ||  Prototype.emptyFunction).apply( this , arguments);
362      },
363 
364      updateComplete:  function (request) {
365           if  ( this .options.decay) {
366               this .decay  =  (request.responseText  ==   this .lastText  ?
367                             this .decay  *   this .options.decay :  1 );
368 
369               this .lastText  =  request.responseText;
370          }
371           //  返回一个标志(timer)留作被stop函数调用, 以停止定时器
372           this .timer  =  setTimeout( this .onTimerEvent.bind( this ),
373                   this .decay  *   this .frequency  *   1000 );
374      },
375 
376      onTimerEvent:  function () {
377           this .updater  =   new  Ajax.Updater( this .container,  this .url,  this .options);
378      }
379  });
posted on 2006-12-21 17:29 windfree 阅读(867) 评论(0)   编辑   收藏 所属分类: javascript 、 ajax

你可能感兴趣的:(protoype.js学习之Ajax对象(三))