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', thisthis.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 阅读(999) 评论(0)  编辑  收藏 所属分类: javascriptajax

只有注册用户登录后才能发表评论。


网站导航: