1 | /*
|
---|
2 | Copyright (c) 2006 Yahoo! Inc. All rights reserved.
|
---|
3 | version 0.9.0
|
---|
4 | */
|
---|
5 |
|
---|
6 | /**
|
---|
7 | * The Connection Manager provides a simplified interface to the XMLHttpRequest
|
---|
8 | * object. It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
|
---|
9 | * interactive states and server response returning the results to a pre-defined
|
---|
10 | * callback function you create.
|
---|
11 | * @ class
|
---|
12 | */
|
---|
13 | YAHOO.util.Connect = {};
|
---|
14 |
|
---|
15 | YAHOO.util.Connect =
|
---|
16 | {
|
---|
17 | /**
|
---|
18 | * Array of MSFT ActiveX ids for XMLHttpRequest.
|
---|
19 | * @private
|
---|
20 | * @type array
|
---|
21 | */
|
---|
22 | _msxml_progid:[
|
---|
23 | 'MSXML2.XMLHTTP.5.0',
|
---|
24 | 'MSXML2.XMLHTTP.4.0',
|
---|
25 | 'MSXML2.XMLHTTP.3.0',
|
---|
26 | 'MSXML2.XMLHTTP',
|
---|
27 | 'Microsoft.XMLHTTP'
|
---|
28 | ],
|
---|
29 |
|
---|
30 | /**
|
---|
31 | * Array of HTTP header(s)
|
---|
32 | * @private
|
---|
33 | * @type array
|
---|
34 | */
|
---|
35 | _http_header:[],
|
---|
36 |
|
---|
37 | /**
|
---|
38 | * Property modified by setForm() to determine if the transaction
|
---|
39 | * should be processed as a HTML form POST.
|
---|
40 | * @private
|
---|
41 | * @type boolean
|
---|
42 | */
|
---|
43 | _isFormPost:false,
|
---|
44 |
|
---|
45 | /**
|
---|
46 | * Property modified by setForm() to the HTML form POST body.
|
---|
47 | * @private
|
---|
48 | * @type string
|
---|
49 | */
|
---|
50 | _sFormData:null,
|
---|
51 |
|
---|
52 | /**
|
---|
53 | * The polling frequency, in milliseconds, for HandleReadyState.
|
---|
54 | * when attempting to determine a transaction's XHR readyState.
|
---|
55 | * The default is 300 milliseconds.
|
---|
56 | * @private
|
---|
57 | * @type int
|
---|
58 | */
|
---|
59 | _polling_interval:300,
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * A transaction counter that increments the transaction id for each transaction.
|
---|
63 | * @private
|
---|
64 | * @type int
|
---|
65 | */
|
---|
66 | _transaction_id:0,
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * Member to add an ActiveX id to the existing xml_progid array.
|
---|
70 | * In the event(unlikely) a new ActiveX id is introduced, it can be added
|
---|
71 | * without internal code modifications.
|
---|
72 | * @public
|
---|
73 | * @param string id The ActiveX id to be added to initialize the XHR object.
|
---|
74 | * @return void
|
---|
75 | */
|
---|
76 | setProgId:function(id)
|
---|
77 | {
|
---|
78 | this.msxml_progid.unshift(id);
|
---|
79 | },
|
---|
80 |
|
---|
81 | /**
|
---|
82 | * Instantiates a XMLHttpRequest object and returns an object with two properties:
|
---|
83 | * the XMLHttpRequest instance and the transaction id.
|
---|
84 | * @private
|
---|
85 | * @param {int} transactionId Property containing the transaction id for this transaction.
|
---|
86 | * @return connection object
|
---|
87 | * @type object
|
---|
88 | */
|
---|
89 | createXhrObject:function(transactionId)
|
---|
90 | {
|
---|
91 | var obj,http;
|
---|
92 | try
|
---|
93 | {
|
---|
94 | // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
|
---|
95 | http = new XMLHttpRequest();
|
---|
96 | // Object literal with http and id properties
|
---|
97 | obj = { conn:http, tId:transactionId };
|
---|
98 | }
|
---|
99 | catch(e)
|
---|
100 | {
|
---|
101 | for(var i=0; i<this._msxml_progid.length; ++i){
|
---|
102 | try
|
---|
103 | {
|
---|
104 | // Instantiates XMLHttpRequest for IE and assign to http.
|
---|
105 | http = new ActiveXObject(this._msxml_progid[i]);
|
---|
106 | // Object literal with http and id properties
|
---|
107 | obj = { conn:http, tId:transactionId };
|
---|
108 | }
|
---|
109 | catch(e){}
|
---|
110 | }
|
---|
111 | }
|
---|
112 | finally
|
---|
113 | {
|
---|
114 | return obj;
|
---|
115 | }
|
---|
116 | },
|
---|
117 |
|
---|
118 | /**
|
---|
119 | * This method is called by asyncRequest and syncRequest to create a
|
---|
120 | * valid connection object for the transaction. It also passes a
|
---|
121 | * transaction id and increments the transaction id counter.
|
---|
122 | * @private
|
---|
123 | * @return object
|
---|
124 | */
|
---|
125 | getConnectionObject:function()
|
---|
126 | {
|
---|
127 | var o;
|
---|
128 | var tId = this._transaction_id;
|
---|
129 |
|
---|
130 | try
|
---|
131 | {
|
---|
132 | o = this.createXhrObject(tId);
|
---|
133 | if(o){
|
---|
134 | this._transaction_id++;
|
---|
135 | }
|
---|
136 | }
|
---|
137 | catch(e){}
|
---|
138 | finally
|
---|
139 | {
|
---|
140 | return o;
|
---|
141 | }
|
---|
142 | },
|
---|
143 |
|
---|
144 | /**
|
---|
145 | * Method for initiating an asynchronous request via the XHR object.
|
---|
146 | * @public
|
---|
147 | * @param {string} method HTTP transaction method
|
---|
148 | * @param {string} uri Fully qualified path of resource
|
---|
149 | * @param callback User-defined callback function or object
|
---|
150 | * @param callbackArg User-defined callback arguments
|
---|
151 | * @param {string} postData POST body
|
---|
152 | * @return {object} Returns the connection object
|
---|
153 | */
|
---|
154 | asyncRequest:function(method, uri, callback, postData)
|
---|
155 | {
|
---|
156 | var errorObj;
|
---|
157 | var o = this.getConnectionObject();
|
---|
158 |
|
---|
159 | if(!o){
|
---|
160 | return null;
|
---|
161 | }
|
---|
162 | else{
|
---|
163 | var oConn = this;
|
---|
164 |
|
---|
165 | o.conn.open(method, uri, true);
|
---|
166 | this.handleReadyState(o, callback);
|
---|
167 |
|
---|
168 | if(this._isFormPost){
|
---|
169 | postData = this._sFormData;
|
---|
170 | this._isFormPost = false;
|
---|
171 | }
|
---|
172 | else if(postData){
|
---|
173 | this.initHeader('Content-Type','application/x-www-form-urlencoded');
|
---|
174 | }
|
---|
175 |
|
---|
176 | //Verify whether the transaction has any user-defined HTTP headers
|
---|
177 | //and set them.
|
---|
178 | if(this._http_header.length>0){
|
---|
179 | this.setHeader(o);
|
---|
180 | }
|
---|
181 | postData?o.conn.send(postData):o.conn.send(null);
|
---|
182 |
|
---|
183 | return o;
|
---|
184 | }
|
---|
185 | },
|
---|
186 |
|
---|
187 | /**
|
---|
188 | * This method serves as a timer that polls the XHR object's readyState
|
---|
189 | * property during a transaction, instead of binding a callback to the
|
---|
190 | * onreadystatechange event. Upon readyState 4, handleTransactionResponse
|
---|
191 | * will process the response, and the timer will be cleared.
|
---|
192 | *
|
---|
193 | * @private
|
---|
194 | * @param {object} o The connection object
|
---|
195 | * @param callback User-defined callback object
|
---|
196 | * @param callbackArg User-defined arguments passed to the callback
|
---|
197 | * @return void
|
---|
198 | */
|
---|
199 | handleReadyState:function(o, callback)
|
---|
200 | {
|
---|
201 | var oConn = this;
|
---|
202 | var poll = window.setInterval(
|
---|
203 | function(){
|
---|
204 | if(o.conn.readyState==4){
|
---|
205 | oConn.handleTransactionResponse(o, callback);
|
---|
206 | window.clearInterval(poll);
|
---|
207 | }
|
---|
208 | }
|
---|
209 | ,this._polling_interval);
|
---|
210 | },
|
---|
211 |
|
---|
212 | /**
|
---|
213 | * This method attempts to interpret the server response and
|
---|
214 | * determine whether the transaction was successful, or if an error or
|
---|
215 | * exception was encountered.
|
---|
216 | *
|
---|
217 | * @private
|
---|
218 | * @param {object} o The connection object
|
---|
219 | * @param {function} callback - User-defined callback object
|
---|
220 | * @param {} callbackArg - User-defined arguments to be passed to the callback
|
---|
221 | * @return void
|
---|
222 | */
|
---|
223 | handleTransactionResponse:function(o, callback)
|
---|
224 | {
|
---|
225 | var httpStatus;
|
---|
226 | var responseObject;
|
---|
227 |
|
---|
228 | try{
|
---|
229 | httpStatus = o.conn.status;
|
---|
230 | }
|
---|
231 | catch(e){
|
---|
232 | // 13030 is the custom code to indicate the condition -- in Mozilla/FF --
|
---|
233 | // when the o object's status and statusText properties are
|
---|
234 | // unavailable, and a query attempt throws an exception.
|
---|
235 | httpStatus = 13030;
|
---|
236 | }
|
---|
237 |
|
---|
238 | if(httpStatus == 200){
|
---|
239 | responseObject = this.createResponseObject(o, callback.argument);
|
---|
240 | if(callback.success){
|
---|
241 | if(!callback.scope){
|
---|
242 | callback.success(responseObject);
|
---|
243 | }
|
---|
244 | else{
|
---|
245 | callback.success.apply(callback.scope, [responseObject]);
|
---|
246 | }
|
---|
247 | }
|
---|
248 | }
|
---|
249 | else{
|
---|
250 | switch(httpStatus){
|
---|
251 | // The following case labels are wininet.dll error codes that may be encountered.
|
---|
252 | // Server timeout
|
---|
253 | case 12002:
|
---|
254 | // 12029 to 12031 correspond to dropped connections.
|
---|
255 | case 12029:
|
---|
256 | case 12030:
|
---|
257 | case 12031:
|
---|
258 | // Connection closed by server.
|
---|
259 | case 12152:
|
---|
260 | // See above comments for variable status.
|
---|
261 | case 13030:
|
---|
262 | responseObject = this.createExceptionObject(o, callback.argument);
|
---|
263 | if(callback.failure){
|
---|
264 | if(!callback.scope){
|
---|
265 | callback.failure(responseObject);
|
---|
266 | }
|
---|
267 | else{
|
---|
268 | callback.failure.apply(callback.scope,[responseObject]);
|
---|
269 | }
|
---|
270 | }
|
---|
271 | break;
|
---|
272 | default:
|
---|
273 | responseObject = this.createResponseObject(o, callback.argument);
|
---|
274 | if(callback.failure){
|
---|
275 | if(!callback.scope){
|
---|
276 | callback.failure(responseObject);
|
---|
277 | }
|
---|
278 | else{
|
---|
279 | callback.failure.apply(callback.scope,[responseObject]);
|
---|
280 | }
|
---|
281 | }
|
---|
282 | }
|
---|
283 | }
|
---|
284 |
|
---|
285 | this.releaseObject(o);
|
---|
286 | },
|
---|
287 |
|
---|
288 | /**
|
---|
289 | * This method evaluates the server response, creates and returns the results via
|
---|
290 | * its properties. Success and failure cases(and exceptions) will differ in their defined properties
|
---|
291 | * but property "type" will confirm the transaction's status.
|
---|
292 | * @private
|
---|
293 | * @param {object} o The connection object
|
---|
294 | * @param {} callbackArg User-defined arguments to be passed to the callback
|
---|
295 | * @param {boolean} isSuccess Indicates whether the transaction was successful or not.
|
---|
296 | * @return object
|
---|
297 | */
|
---|
298 | createResponseObject:function(o, callbackArg)
|
---|
299 | {
|
---|
300 | var obj = {};
|
---|
301 |
|
---|
302 | obj.tId = o.tId;
|
---|
303 | obj.status = o.conn.status;
|
---|
304 | obj.statusText = o.conn.statusText;
|
---|
305 | obj.allResponseHeaders = o.conn.getAllResponseHeaders();
|
---|
306 | obj.responseText = o.conn.responseText;
|
---|
307 | obj.responseXML = o.conn.responseXML;
|
---|
308 | if(callbackArg){
|
---|
309 | obj.argument = callbackArg;
|
---|
310 | }
|
---|
311 |
|
---|
312 | return obj;
|
---|
313 | },
|
---|
314 |
|
---|
315 | /**
|
---|
316 | * If a transaction cannot be completed due to dropped or closed connections,
|
---|
317 | * there may be not be enough information to build a full response object.
|
---|
318 | * The object's property "type" value will be "failure", and two additional
|
---|
319 | * unique, properties are added - errorCode and errorText.
|
---|
320 | * @private
|
---|
321 | * @param {int} tId Transaction Id
|
---|
322 | * @param callbackArg The user-defined arguments
|
---|
323 | * @param {string} errorCode Error code associated with the exception.
|
---|
324 | * @param {string} errorText Error message describing the exception.
|
---|
325 | * @return object
|
---|
326 | */
|
---|
327 | createExceptionObject:function(tId, callbackArg)
|
---|
328 | {
|
---|
329 | var COMM_CODE = 0;
|
---|
330 | var COMM_ERROR = 'communication failure';
|
---|
331 |
|
---|
332 | var obj = {};
|
---|
333 |
|
---|
334 | obj.tId = tId;
|
---|
335 | obj.status = COMM_CODE;
|
---|
336 | obj.statusText = COMM_ERROR;
|
---|
337 | if(callbackArg){
|
---|
338 | obj.argument = callbackArg;
|
---|
339 | }
|
---|
340 |
|
---|
341 | return obj;
|
---|
342 | },
|
---|
343 |
|
---|
344 | /**
|
---|
345 | * Accessor that stores the HTTP headers for each transaction.
|
---|
346 | * @public
|
---|
347 | * @param {string} label The HTTP header label
|
---|
348 | * @param {string} value The HTTP header value
|
---|
349 | * @return void
|
---|
350 | */
|
---|
351 | initHeader:function(label,value)
|
---|
352 | {
|
---|
353 | var oHeader = [label,value];
|
---|
354 | this._http_header.push(oHeader);
|
---|
355 | },
|
---|
356 |
|
---|
357 | /**
|
---|
358 | * Accessor that sets the HTTP headers for each transaction.
|
---|
359 | * @private
|
---|
360 | * @param {object} o The connection object for the transaction.
|
---|
361 | * @return void
|
---|
362 | */
|
---|
363 | setHeader:function(o)
|
---|
364 | {
|
---|
365 | var oHeader = this._http_header;
|
---|
366 | for(var i=0;i<oHeader.length;i++){
|
---|
367 | o.conn.setRequestHeader(oHeader[i][0],oHeader[i][1]);
|
---|
368 | }
|
---|
369 | oHeader.splice(0,oHeader.length);
|
---|
370 | },
|
---|
371 |
|
---|
372 | /**
|
---|
373 | * This method assembles the form label and value pairs and
|
---|
374 | * constructs an encoded POST body. Both syncRequest()
|
---|
375 | * and asyncRequest() will automatically initialize the
|
---|
376 | * transaction with a HTTP header Content-Type of
|
---|
377 | * application/x-www-form-urlencoded.
|
---|
378 | * @public
|
---|
379 | * @param {string} formName value of form name attribute
|
---|
380 | * @return void
|
---|
381 | */
|
---|
382 | setForm:function(formName)
|
---|
383 | {
|
---|
384 | this._sFormData = '';
|
---|
385 | var oForm = document.forms[formName];
|
---|
386 | var oElement, elName, elValue;
|
---|
387 | // iterate over the form elements collection to construct the
|
---|
388 | // label-value pairs.
|
---|
389 | for (var i=0; i<oForm.elements.length; i++){
|
---|
390 | oElement = oForm.elements[i];
|
---|
391 | elName = oForm.elements[i].name;
|
---|
392 | elValue = oForm.elements[i].value;
|
---|
393 | switch (oElement.type)
|
---|
394 | {
|
---|
395 | case 'select-multiple':
|
---|
396 | for(var j=0; j<oElement.options.length; j++){
|
---|
397 | if(oElement.options[j].selected){
|
---|
398 | this._sFormData += encodeURIComponent(elName) + '=' + encodeURIComponent(oElement.options[j].value) + '&';
|
---|
399 | }
|
---|
400 | }
|
---|
401 | break;
|
---|
402 | case 'radio':
|
---|
403 | case 'checkbox':
|
---|
404 | if(oElement.checked){
|
---|
405 | this._sFormData += encodeURIComponent(elName) + '=' + encodeURIComponent(elValue) + '&';
|
---|
406 | }
|
---|
407 | break;
|
---|
408 | case 'file':
|
---|
409 | // stub case as XMLHttpRequest will only send the file path as a string.
|
---|
410 | break;
|
---|
411 | case undefined:
|
---|
412 | // stub case for fieldset element which returns undefined.
|
---|
413 | break;
|
---|
414 | default:
|
---|
415 | this._sFormData += encodeURIComponent(elName) + '=' + encodeURIComponent(elValue) + '&';
|
---|
416 | break;
|
---|
417 | }
|
---|
418 | }
|
---|
419 | this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
|
---|
420 | this._isFormPost = true;
|
---|
421 | this.initHeader('Content-Type','application/x-www-form-urlencoded');
|
---|
422 | },
|
---|
423 |
|
---|
424 | /**
|
---|
425 | * Public method to terminate a transaction, if it has not reached readyState 4.
|
---|
426 | * @public
|
---|
427 | * @param {object} o The connection object returned by asyncRequest.
|
---|
428 | * @return void
|
---|
429 | */
|
---|
430 | abort:function(o)
|
---|
431 | {
|
---|
432 | if(this.isCallInProgress(o)){
|
---|
433 | o.conn.abort();
|
---|
434 | this.releaseObject(o);
|
---|
435 | }
|
---|
436 | },
|
---|
437 |
|
---|
438 | /**
|
---|
439 | * Accessor to check if the transaction associated with the connection object
|
---|
440 | * is still being processed.
|
---|
441 | * @public
|
---|
442 | * @param {object} o The connection object returned by asyncRequest
|
---|
443 | * @return boolean
|
---|
444 | */
|
---|
445 | isCallInProgress:function(o)
|
---|
446 | {
|
---|
447 | if(o){
|
---|
448 | return o.conn.readyState != 4 && o.conn.readyState != 0;
|
---|
449 | }
|
---|
450 | },
|
---|
451 |
|
---|
452 | /**
|
---|
453 | * Dereference the XHR instance and the connection object after the transaction is completed.
|
---|
454 | * @private
|
---|
455 | * @param {object} o The connection object
|
---|
456 | * @return void
|
---|
457 | */
|
---|
458 | releaseObject:function(o)
|
---|
459 | {
|
---|
460 | //dereference the XHR instance.
|
---|
461 | o.conn = null;
|
---|
462 | //dereference the connection object.
|
---|
463 | o = null;
|
---|
464 | }
|
---|
465 | } |
---|