Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / lib / yui / connection / connection.js
blobd3a917e77e504b814357fe04b04e4b20cfdb6db2
1 /*
2 Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.5.2
6 */
7 /**
8  * The Connection Manager provides a simplified interface to the XMLHttpRequest
9  * object.  It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
10  * interactive states and server response, returning the results to a pre-defined
11  * callback you create.
12  *
13  * @namespace YAHOO.util
14  * @module connection
15  * @requires yahoo
16  * @requires event
17  */
19 /**
20  * The Connection Manager singleton provides methods for creating and managing
21  * asynchronous transactions.
22  *
23  * @class Connect
24  */
26 YAHOO.util.Connect =
28   /**
29    * @description Array of MSFT ActiveX ids for XMLHttpRequest.
30    * @property _msxml_progid
31    * @private
32    * @static
33    * @type array
34    */
35         _msxml_progid:[
36                 'Microsoft.XMLHTTP',
37                 'MSXML2.XMLHTTP.3.0',
38                 'MSXML2.XMLHTTP'
39                 ],
41   /**
42    * @description Object literal of HTTP header(s)
43    * @property _http_header
44    * @private
45    * @static
46    * @type object
47    */
48         _http_headers:{},
50   /**
51    * @description Determines if HTTP headers are set.
52    * @property _has_http_headers
53    * @private
54    * @static
55    * @type boolean
56    */
57         _has_http_headers:false,
59  /**
60   * @description Determines if a default header of
61   * Content-Type of 'application/x-www-form-urlencoded'
62   * will be added to any client HTTP headers sent for POST
63   * transactions.
64   * @property _use_default_post_header
65   * @private
66   * @static
67   * @type boolean
68   */
69     _use_default_post_header:true,
71  /**
72   * @description The default header used for POST transactions.
73   * @property _default_post_header
74   * @private
75   * @static
76   * @type boolean
77   */
78     _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',
80  /**
81   * @description The default header used for transactions involving the
82   * use of HTML forms.
83   * @property _default_form_header
84   * @private
85   * @static
86   * @type boolean
87   */
88     _default_form_header:'application/x-www-form-urlencoded',
90  /**
91   * @description Determines if a default header of
92   * 'X-Requested-With: XMLHttpRequest'
93   * will be added to each transaction.
94   * @property _use_default_xhr_header
95   * @private
96   * @static
97   * @type boolean
98   */
99     _use_default_xhr_header:true,
101  /**
102   * @description The default header value for the label
103   * "X-Requested-With".  This is sent with each
104   * transaction, by default, to identify the
105   * request as being made by YUI Connection Manager.
106   * @property _default_xhr_header
107   * @private
108   * @static
109   * @type boolean
110   */
111     _default_xhr_header:'XMLHttpRequest',
113  /**
114   * @description Determines if custom, default headers
115   * are set for each transaction.
116   * @property _has_default_header
117   * @private
118   * @static
119   * @type boolean
120   */
121     _has_default_headers:true,
123  /**
124   * @description Determines if custom, default headers
125   * are set for each transaction.
126   * @property _has_default_header
127   * @private
128   * @static
129   * @type boolean
130   */
131     _default_headers:{},
133  /**
134   * @description Property modified by setForm() to determine if the data
135   * should be submitted as an HTML form.
136   * @property _isFormSubmit
137   * @private
138   * @static
139   * @type boolean
140   */
141     _isFormSubmit:false,
143  /**
144   * @description Property modified by setForm() to determine if a file(s)
145   * upload is expected.
146   * @property _isFileUpload
147   * @private
148   * @static
149   * @type boolean
150   */
151     _isFileUpload:false,
153  /**
154   * @description Property modified by setForm() to set a reference to the HTML
155   * form node if the desired action is file upload.
156   * @property _formNode
157   * @private
158   * @static
159   * @type object
160   */
161     _formNode:null,
163  /**
164   * @description Property modified by setForm() to set the HTML form data
165   * for each transaction.
166   * @property _sFormData
167   * @private
168   * @static
169   * @type string
170   */
171     _sFormData:null,
173  /**
174   * @description Collection of polling references to the polling mechanism in handleReadyState.
175   * @property _poll
176   * @private
177   * @static
178   * @type object
179   */
180     _poll:{},
182  /**
183   * @description Queue of timeout values for each transaction callback with a defined timeout value.
184   * @property _timeOut
185   * @private
186   * @static
187   * @type object
188   */
189     _timeOut:{},
191   /**
192    * @description The polling frequency, in milliseconds, for HandleReadyState.
193    * when attempting to determine a transaction's XHR readyState.
194    * The default is 50 milliseconds.
195    * @property _polling_interval
196    * @private
197    * @static
198    * @type int
199    */
200      _polling_interval:50,
202   /**
203    * @description A transaction counter that increments the transaction id for each transaction.
204    * @property _transaction_id
205    * @private
206    * @static
207    * @type int
208    */
209      _transaction_id:0,
211   /**
212    * @description Tracks the name-value pair of the "clicked" submit button if multiple submit
213    * buttons are present in an HTML form; and, if YAHOO.util.Event is available.
214    * @property _submitElementValue
215    * @private
216    * @static
217    * @type string
218    */
219          _submitElementValue:null,
221   /**
222    * @description Determines whether YAHOO.util.Event is available and returns true or false.
223    * If true, an event listener is bound at the document level to trap click events that
224    * resolve to a target type of "Submit".  This listener will enable setForm() to determine
225    * the clicked "Submit" value in a multi-Submit button, HTML form.
226    * @property _hasSubmitListener
227    * @private
228    * @static
229    */
230          _hasSubmitListener:(function()
231          {
232                 if(YAHOO.util.Event){
233                         YAHOO.util.Event.addListener(
234                                 document,
235                                 'click',
236                                 function(e){
237                                         var obj = YAHOO.util.Event.getTarget(e);
238                                         if(obj.nodeName.toLowerCase() == 'input' && (obj.type && obj.type.toLowerCase() == 'submit')){
239                                                 YAHOO.util.Connect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
240                                         }
241                                 });
242                         return true;
243             }
244             return false;
245          })(),
247   /**
248    * @description Custom event that fires at the start of a transaction
249    * @property startEvent
250    * @private
251    * @static
252    * @type CustomEvent
253    */
254         startEvent: new YAHOO.util.CustomEvent('start'),
256   /**
257    * @description Custom event that fires when a transaction response has completed.
258    * @property completeEvent
259    * @private
260    * @static
261    * @type CustomEvent
262    */
263         completeEvent: new YAHOO.util.CustomEvent('complete'),
265   /**
266    * @description Custom event that fires when handleTransactionResponse() determines a
267    * response in the HTTP 2xx range.
268    * @property successEvent
269    * @private
270    * @static
271    * @type CustomEvent
272    */
273         successEvent: new YAHOO.util.CustomEvent('success'),
275   /**
276    * @description Custom event that fires when handleTransactionResponse() determines a
277    * response in the HTTP 4xx/5xx range.
278    * @property failureEvent
279    * @private
280    * @static
281    * @type CustomEvent
282    */
283         failureEvent: new YAHOO.util.CustomEvent('failure'),
285   /**
286    * @description Custom event that fires when handleTransactionResponse() determines a
287    * response in the HTTP 4xx/5xx range.
288    * @property failureEvent
289    * @private
290    * @static
291    * @type CustomEvent
292    */
293         uploadEvent: new YAHOO.util.CustomEvent('upload'),
295   /**
296    * @description Custom event that fires when a transaction is successfully aborted.
297    * @property abortEvent
298    * @private
299    * @static
300    * @type CustomEvent
301    */
302         abortEvent: new YAHOO.util.CustomEvent('abort'),
304   /**
305    * @description A reference table that maps callback custom events members to its specific
306    * event name.
307    * @property _customEvents
308    * @private
309    * @static
310    * @type object
311    */
312         _customEvents:
313         {
314                 onStart:['startEvent', 'start'],
315                 onComplete:['completeEvent', 'complete'],
316                 onSuccess:['successEvent', 'success'],
317                 onFailure:['failureEvent', 'failure'],
318                 onUpload:['uploadEvent', 'upload'],
319                 onAbort:['abortEvent', 'abort']
320         },
322   /**
323    * @description Member to add an ActiveX id to the existing xml_progid array.
324    * In the event(unlikely) a new ActiveX id is introduced, it can be added
325    * without internal code modifications.
326    * @method setProgId
327    * @public
328    * @static
329    * @param {string} id The ActiveX id to be added to initialize the XHR object.
330    * @return void
331    */
332         setProgId:function(id)
333         {
334                 this._msxml_progid.unshift(id);
335         },
337   /**
338    * @description Member to override the default POST header.
339    * @method setDefaultPostHeader
340    * @public
341    * @static
342    * @param {boolean} b Set and use default header - true or false .
343    * @return void
344    */
345         setDefaultPostHeader:function(b)
346         {
347                 if(typeof b == 'string'){
348                         this._default_post_header = b;
349                 }
350                 else if(typeof b == 'boolean'){
351                         this._use_default_post_header = b;
352                 }
353         },
355   /**
356    * @description Member to override the default transaction header..
357    * @method setDefaultXhrHeader
358    * @public
359    * @static
360    * @param {boolean} b Set and use default header - true or false .
361    * @return void
362    */
363         setDefaultXhrHeader:function(b)
364         {
365                 if(typeof b == 'string'){
366                         this._default_xhr_header = b;
367                 }
368                 else{
369                         this._use_default_xhr_header = b;
370                 }
371         },
373   /**
374    * @description Member to modify the default polling interval.
375    * @method setPollingInterval
376    * @public
377    * @static
378    * @param {int} i The polling interval in milliseconds.
379    * @return void
380    */
381         setPollingInterval:function(i)
382         {
383                 if(typeof i == 'number' && isFinite(i)){
384                         this._polling_interval = i;
385                 }
386         },
388   /**
389    * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
390    * the XMLHttpRequest instance and the transaction id.
391    * @method createXhrObject
392    * @private
393    * @static
394    * @param {int} transactionId Property containing the transaction id for this transaction.
395    * @return object
396    */
397         createXhrObject:function(transactionId)
398         {
399                 var obj,http;
400                 try
401                 {
402                         // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
403                         http = new XMLHttpRequest();
404                         //  Object literal with http and tId properties
405                         obj = { conn:http, tId:transactionId };
406                 }
407                 catch(e)
408                 {
409                         for(var i=0; i<this._msxml_progid.length; ++i){
410                                 try
411                                 {
412                                         // Instantiates XMLHttpRequest for IE and assign to http
413                                         http = new ActiveXObject(this._msxml_progid[i]);
414                                         //  Object literal with conn and tId properties
415                                         obj = { conn:http, tId:transactionId };
416                                         break;
417                                 }
418                                 catch(e){}
419                         }
420                 }
421                 finally
422                 {
423                         return obj;
424                 }
425         },
427   /**
428    * @description This method is called by asyncRequest to create a
429    * valid connection object for the transaction.  It also passes a
430    * transaction id and increments the transaction id counter.
431    * @method getConnectionObject
432    * @private
433    * @static
434    * @return {object}
435    */
436         getConnectionObject:function(isFileUpload)
437         {
438                 var o;
439                 var tId = this._transaction_id;
441                 try
442                 {
443                         if(!isFileUpload){
444                                 o = this.createXhrObject(tId);
445                         }
446                         else{
447                                 o = {};
448                                 o.tId = tId;
449                                 o.isUpload = true;
450                         }
452                         if(o){
453                                 this._transaction_id++;
454                         }
455                 }
456                 catch(e){}
457                 finally
458                 {
459                         return o;
460                 }
461         },
463   /**
464    * @description Method for initiating an asynchronous request via the XHR object.
465    * @method asyncRequest
466    * @public
467    * @static
468    * @param {string} method HTTP transaction method
469    * @param {string} uri Fully qualified path of resource
470    * @param {callback} callback User-defined callback function or object
471    * @param {string} postData POST body
472    * @return {object} Returns the connection object
473    */
474         asyncRequest:function(method, uri, callback, postData)
475         {
476                 var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
477                 var args = (callback && callback.argument)?callback.argument:null;
479                 if(!o){
480                         return null;
481                 }
482                 else{
484                         // Intialize any transaction-specific custom events, if provided.
485                         if(callback && callback.customevents){
486                                 this.initCustomEvents(o, callback);
487                         }
489                         if(this._isFormSubmit){
490                                 if(this._isFileUpload){
491                                         this.uploadFile(o, callback, uri, postData);
492                                         return o;
493                                 }
495                                 // If the specified HTTP method is GET, setForm() will return an
496                                 // encoded string that is concatenated to the uri to
497                                 // create a querystring.
498                                 if(method.toUpperCase() == 'GET'){
499                                         if(this._sFormData.length !== 0){
500                                                 // If the URI already contains a querystring, append an ampersand
501                                                 // and then concatenate _sFormData to the URI.
502                                                 uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
503                                         }
504                                 }
505                                 else if(method.toUpperCase() == 'POST'){
506                                         // If POST data exist in addition to the HTML form data,
507                                         // it will be concatenated to the form data.
508                                         postData = postData?this._sFormData + "&" + postData:this._sFormData;
509                                 }
510                         }
512                         if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
513                                 // If callback.cache is defined and set to false, a
514                                 // timestamp value will be added to the querystring.
515                                 uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
516                         }
518                         o.conn.open(method, uri, true);
520                         // Each transaction will automatically include a custom header of
521                         // "X-Requested-With: XMLHttpRequest" to identify the request as
522                         // having originated from Connection Manager.
523                         if(this._use_default_xhr_header){
524                                 if(!this._default_headers['X-Requested-With']){
525                                         this.initHeader('X-Requested-With', this._default_xhr_header, true);
526                                 }
527                         }
529                         //If the transaction method is POST and the POST header value is set to true
530                         //or a custom value, initalize the Content-Type header to this value.
531                         if((method.toUpperCase() == 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
532                                 this.initHeader('Content-Type', this._default_post_header);
533                         }
535                         //Initialize all default and custom HTTP headers,
536                         if(this._has_default_headers || this._has_http_headers){
537                                 this.setHeader(o);
538                         }
540                         this.handleReadyState(o, callback);
541                         o.conn.send(postData || '');
543                         // Reset the HTML form data and state properties as
544                         // soon as the data are submitted.
545                         if(this._isFormSubmit === true){
546                                 this.resetFormState();
547                         }
549                         // Fire global custom event -- startEvent
550                         this.startEvent.fire(o, args);
552                         if(o.startEvent){
553                                 // Fire transaction custom event -- startEvent
554                                 o.startEvent.fire(o, args);
555                         }
557                         return o;
558                 }
559         },
561   /**
562    * @description This method creates and subscribes custom events,
563    * specific to each transaction
564    * @method initCustomEvents
565    * @private
566    * @static
567    * @param {object} o The connection object
568    * @param {callback} callback The user-defined callback object
569    * @return {void}
570    */
571         initCustomEvents:function(o, callback)
572         {
573                 // Enumerate through callback.customevents members and bind/subscribe
574                 // events that match in the _customEvents table.
575                 for(var prop in callback.customevents){
576                         if(this._customEvents[prop][0]){
577                                 // Create the custom event
578                                 o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
580                                 // Subscribe the custom event
581                                 o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
582                         }
583                 }
584         },
586   /**
587    * @description This method serves as a timer that polls the XHR object's readyState
588    * property during a transaction, instead of binding a callback to the
589    * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
590    * will process the response, and the timer will be cleared.
591    * @method handleReadyState
592    * @private
593    * @static
594    * @param {object} o The connection object
595    * @param {callback} callback The user-defined callback object
596    * @return {void}
597    */
599     handleReadyState:function(o, callback)
601     {
602                 var oConn = this;
603                 var args = (callback && callback.argument)?callback.argument:null;
605                 if(callback && callback.timeout){
606                         this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
607                 }
609                 this._poll[o.tId] = window.setInterval(
610                         function(){
611                                 if(o.conn && o.conn.readyState === 4){
613                                         // Clear the polling interval for the transaction
614                                         // and remove the reference from _poll.
615                                         window.clearInterval(oConn._poll[o.tId]);
616                                         delete oConn._poll[o.tId];
618                                         if(callback && callback.timeout){
619                                                 window.clearTimeout(oConn._timeOut[o.tId]);
620                                                 delete oConn._timeOut[o.tId];
621                                         }
623                                         // Fire global custom event -- completeEvent
624                                         oConn.completeEvent.fire(o, args);
626                                         if(o.completeEvent){
627                                                 // Fire transaction custom event -- completeEvent
628                                                 o.completeEvent.fire(o, args);
629                                         }
631                                         oConn.handleTransactionResponse(o, callback);
632                                 }
633                         }
634                 ,this._polling_interval);
635     },
637   /**
638    * @description This method attempts to interpret the server response and
639    * determine whether the transaction was successful, or if an error or
640    * exception was encountered.
641    * @method handleTransactionResponse
642    * @private
643    * @static
644    * @param {object} o The connection object
645    * @param {object} callback The user-defined callback object
646    * @param {boolean} isAbort Determines if the transaction was terminated via abort().
647    * @return {void}
648    */
649     handleTransactionResponse:function(o, callback, isAbort)
650     {
651                 var httpStatus, responseObject;
652                 var args = (callback && callback.argument)?callback.argument:null;
654                 try
655                 {
656                         if(o.conn.status !== undefined && o.conn.status !== 0){
657                                 httpStatus = o.conn.status;
658                         }
659                         else{
660                                 httpStatus = 13030;
661                         }
662                 }
663                 catch(e){
665                          // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
666                          // when the XHR object's status and statusText properties are
667                          // unavailable, and a query attempt throws an exception.
668                         httpStatus = 13030;
669                 }
671                 if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
672                         responseObject = this.createResponseObject(o, args);
673                         if(callback && callback.success){
674                                 if(!callback.scope){
675                                         callback.success(responseObject);
676                                 }
677                                 else{
678                                         // If a scope property is defined, the callback will be fired from
679                                         // the context of the object.
680                                         callback.success.apply(callback.scope, [responseObject]);
681                                 }
682                         }
684                         // Fire global custom event -- successEvent
685                         this.successEvent.fire(responseObject);
687                         if(o.successEvent){
688                                 // Fire transaction custom event -- successEvent
689                                 o.successEvent.fire(responseObject);
690                         }
691                 }
692                 else{
693                         switch(httpStatus){
694                                 // The following cases are wininet.dll error codes that may be encountered.
695                                 case 12002: // Server timeout
696                                 case 12029: // 12029 to 12031 correspond to dropped connections.
697                                 case 12030:
698                                 case 12031:
699                                 case 12152: // Connection closed by server.
700                                 case 13030: // See above comments for variable status.
701                                         responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
702                                         if(callback && callback.failure){
703                                                 if(!callback.scope){
704                                                         callback.failure(responseObject);
705                                                 }
706                                                 else{
707                                                         callback.failure.apply(callback.scope, [responseObject]);
708                                                 }
709                                         }
711                                         break;
712                                 default:
713                                         responseObject = this.createResponseObject(o, args);
714                                         if(callback && callback.failure){
715                                                 if(!callback.scope){
716                                                         callback.failure(responseObject);
717                                                 }
718                                                 else{
719                                                         callback.failure.apply(callback.scope, [responseObject]);
720                                                 }
721                                         }
722                         }
724                         // Fire global custom event -- failureEvent
725                         this.failureEvent.fire(responseObject);
727                         if(o.failureEvent){
728                                 // Fire transaction custom event -- failureEvent
729                                 o.failureEvent.fire(responseObject);
730                         }
732                 }
734                 this.releaseObject(o);
735                 responseObject = null;
736     },
738   /**
739    * @description This method evaluates the server response, creates and returns the results via
740    * its properties.  Success and failure cases will differ in the response
741    * object's property values.
742    * @method createResponseObject
743    * @private
744    * @static
745    * @param {object} o The connection object
746    * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
747    * @return {object}
748    */
749     createResponseObject:function(o, callbackArg)
750     {
751                 var obj = {};
752                 var headerObj = {};
754                 try
755                 {
756                         var headerStr = o.conn.getAllResponseHeaders();
757                         var header = headerStr.split('\n');
758                         for(var i=0; i<header.length; i++){
759                                 var delimitPos = header[i].indexOf(':');
760                                 if(delimitPos != -1){
761                                         headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
762                                 }
763                         }
764                 }
765                 catch(e){}
767                 obj.tId = o.tId;
768                 // Normalize IE's response to HTTP 204 when Win error 1223.
769                 obj.status = (o.conn.status == 1223)?204:o.conn.status;
770                 // Normalize IE's statusText to "No Content" instead of "Unknown".
771                 obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
772                 obj.getResponseHeader = headerObj;
773                 obj.getAllResponseHeaders = headerStr;
774                 obj.responseText = o.conn.responseText;
775                 obj.responseXML = o.conn.responseXML;
777                 if(callbackArg){
778                         obj.argument = callbackArg;
779                 }
781                 return obj;
782     },
784   /**
785    * @description If a transaction cannot be completed due to dropped or closed connections,
786    * there may be not be enough information to build a full response object.
787    * The failure callback will be fired and this specific condition can be identified
788    * by a status property value of 0.
789    *
790    * If an abort was successful, the status property will report a value of -1.
791    *
792    * @method createExceptionObject
793    * @private
794    * @static
795    * @param {int} tId The Transaction Id
796    * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
797    * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
798    * @return {object}
799    */
800     createExceptionObject:function(tId, callbackArg, isAbort)
801     {
802                 var COMM_CODE = 0;
803                 var COMM_ERROR = 'communication failure';
804                 var ABORT_CODE = -1;
805                 var ABORT_ERROR = 'transaction aborted';
807                 var obj = {};
809                 obj.tId = tId;
810                 if(isAbort){
811                         obj.status = ABORT_CODE;
812                         obj.statusText = ABORT_ERROR;
813                 }
814                 else{
815                         obj.status = COMM_CODE;
816                         obj.statusText = COMM_ERROR;
817                 }
819                 if(callbackArg){
820                         obj.argument = callbackArg;
821                 }
823                 return obj;
824     },
826   /**
827    * @description Method that initializes the custom HTTP headers for the each transaction.
828    * @method initHeader
829    * @public
830    * @static
831    * @param {string} label The HTTP header label
832    * @param {string} value The HTTP header value
833    * @param {string} isDefault Determines if the specific header is a default header
834    * automatically sent with each transaction.
835    * @return {void}
836    */
837         initHeader:function(label, value, isDefault)
838         {
839                 var headerObj = (isDefault)?this._default_headers:this._http_headers;
840                 headerObj[label] = value;
842                 if(isDefault){
843                         this._has_default_headers = true;
844                 }
845                 else{
846                         this._has_http_headers = true;
847                 }
848         },
851   /**
852    * @description Accessor that sets the HTTP headers for each transaction.
853    * @method setHeader
854    * @private
855    * @static
856    * @param {object} o The connection object for the transaction.
857    * @return {void}
858    */
859         setHeader:function(o)
860         {
861                 if(this._has_default_headers){
862                         for(var prop in this._default_headers){
863                                 if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
864                                         o.conn.setRequestHeader(prop, this._default_headers[prop]);
865                                 }
866                         }
867                 }
869                 if(this._has_http_headers){
870                         for(var prop in this._http_headers){
871                                 if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
872                                         o.conn.setRequestHeader(prop, this._http_headers[prop]);
873                                 }
874                         }
875                         delete this._http_headers;
877                         this._http_headers = {};
878                         this._has_http_headers = false;
879                 }
880         },
882   /**
883    * @description Resets the default HTTP headers object
884    * @method resetDefaultHeaders
885    * @public
886    * @static
887    * @return {void}
888    */
889         resetDefaultHeaders:function(){
890                 delete this._default_headers;
891                 this._default_headers = {};
892                 this._has_default_headers = false;
893         },
895   /**
896    * @description This method assembles the form label and value pairs and
897    * constructs an encoded string.
898    * asyncRequest() will automatically initialize the transaction with a
899    * a HTTP header Content-Type of application/x-www-form-urlencoded.
900    * @method setForm
901    * @public
902    * @static
903    * @param {string || object} form id or name attribute, or form object.
904    * @param {boolean} optional enable file upload.
905    * @param {boolean} optional enable file upload over SSL in IE only.
906    * @return {string} string of the HTML form field name and value pairs..
907    */
908         setForm:function(formId, isUpload, secureUri)
909         {
910                 // reset the HTML form data and state properties
911                 this.resetFormState();
913                 var oForm;
914                 if(typeof formId == 'string'){
915                         // Determine if the argument is a form id or a form name.
916                         // Note form name usage is deprecated, but supported
917                         // here for backward compatibility.
918                         oForm = (document.getElementById(formId) || document.forms[formId]);
919                 }
920                 else if(typeof formId == 'object'){
921                         // Treat argument as an HTML form object.
922                         oForm = formId;
923                 }
924                 else{
925                         return;
926                 }
928                 // If the isUpload argument is true, setForm will call createFrame to initialize
929                 // an iframe as the form target.
930                 //
931                 // The argument secureURI is also required by IE in SSL environments
932                 // where the secureURI string is a fully qualified HTTP path, used to set the source
933                 // of the iframe, to a stub resource in the same domain.
934                 if(isUpload){
936                         // Create iframe in preparation for file upload.
937                         var io = this.createFrame((window.location.href.toLowerCase().indexOf("https") === 0 || secureUri)?true:false);
938                         // Set form reference and file upload properties to true.
939                         this._isFormSubmit = true;
940                         this._isFileUpload = true;
941                         this._formNode = oForm;
943                         return;
945                 }
947                 var oElement, oName, oValue, oDisabled;
948                 var hasSubmit = false;
950                 // Iterate over the form elements collection to construct the
951                 // label-value pairs.
952                 for (var i=0; i<oForm.elements.length; i++){
953                         oElement = oForm.elements[i];
954                         oDisabled = oElement.disabled;
955                         oName = oElement.name;
956                         oValue = oElement.value;
958                         // Do not submit fields that are disabled or
959                         // do not have a name attribute value.
960                         if(!oDisabled && oName)
961                         {
962                                 switch(oElement.type)
963                                 {
964                                         case 'select-one':
965                                         case 'select-multiple':
966                                                 for(var j=0; j<oElement.options.length; j++){
967                                                         if(oElement.options[j].selected){
968                                                                 if(window.ActiveXObject){
969                                                                         this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
970                                                                 }
971                                                                 else{
972                                                                         this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
973                                                                 }
974                                                         }
975                                                 }
976                                                 break;
977                                         case 'radio':
978                                         case 'checkbox':
979                                                 if(oElement.checked){
980                                                         this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
981                                                 }
982                                                 break;
983                                         case 'file':
984                                                 // stub case as XMLHttpRequest will only send the file path as a string.
985                                         case undefined:
986                                                 // stub case for fieldset element which returns undefined.
987                                         case 'reset':
988                                                 // stub case for input type reset button.
989                                         case 'button':
990                                                 // stub case for input type button elements.
991                                                 break;
992                                         case 'submit':
993                                                 if(hasSubmit === false){
994                                                         if(this._hasSubmitListener && this._submitElementValue){
995                                                                 this._sFormData += this._submitElementValue + '&';
996                                                         }
997                                                         else{
998                                                                 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
999                                                         }
1001                                                         hasSubmit = true;
1002                                                 }
1003                                                 break;
1004                                         default:
1005                                                 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1006                                 }
1007                         }
1008                 }
1010                 this._isFormSubmit = true;
1011                 this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
1013                 this.initHeader('Content-Type', this._default_form_header);
1015                 return this._sFormData;
1016         },
1018   /**
1019    * @description Resets HTML form properties when an HTML form or HTML form
1020    * with file upload transaction is sent.
1021    * @method resetFormState
1022    * @private
1023    * @static
1024    * @return {void}
1025    */
1026         resetFormState:function(){
1027                 this._isFormSubmit = false;
1028                 this._isFileUpload = false;
1029                 this._formNode = null;
1030                 this._sFormData = "";
1031         },
1033   /**
1034    * @description Creates an iframe to be used for form file uploads.  It is remove from the
1035    * document upon completion of the upload transaction.
1036    * @method createFrame
1037    * @private
1038    * @static
1039    * @param {string} optional qualified path of iframe resource for SSL in IE.
1040    * @return {void}
1041    */
1042         createFrame:function(secureUri){
1044                 // IE does not allow the setting of id and name attributes as object
1045                 // properties via createElement().  A different iframe creation
1046                 // pattern is required for IE.
1047                 var frameId = 'yuiIO' + this._transaction_id;
1048                 var io;
1049                 if(window.ActiveXObject){
1050                         io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
1052                         // IE will throw a security exception in an SSL environment if the
1053                         // iframe source is undefined.
1054                         if(typeof secureUri == 'boolean'){
1055                                 io.src = 'javascript:false';
1056                         }
1057                 }
1058                 else{
1059                         io = document.createElement('iframe');
1060                         io.id = frameId;
1061                         io.name = frameId;
1062                 }
1064                 io.style.position = 'absolute';
1065                 io.style.top = '-1000px';
1066                 io.style.left = '-1000px';
1068                 document.body.appendChild(io);
1069         },
1071   /**
1072    * @description Parses the POST data and creates hidden form elements
1073    * for each key-value, and appends them to the HTML form object.
1074    * @method appendPostData
1075    * @private
1076    * @static
1077    * @param {string} postData The HTTP POST data
1078    * @return {array} formElements Collection of hidden fields.
1079    */
1080         appendPostData:function(postData)
1081         {
1082                 var formElements = [];
1083                 var postMessage = postData.split('&');
1084                 for(var i=0; i < postMessage.length; i++){
1085                         var delimitPos = postMessage[i].indexOf('=');
1086                         if(delimitPos != -1){
1087                                 formElements[i] = document.createElement('input');
1088                                 formElements[i].type = 'hidden';
1089                                 formElements[i].name = postMessage[i].substring(0,delimitPos);
1090                                 formElements[i].value = postMessage[i].substring(delimitPos+1);
1091                                 this._formNode.appendChild(formElements[i]);
1092                         }
1093                 }
1095                 return formElements;
1096         },
1098   /**
1099    * @description Uploads HTML form, inclusive of files/attachments, using the
1100    * iframe created in createFrame to facilitate the transaction.
1101    * @method uploadFile
1102    * @private
1103    * @static
1104    * @param {int} id The transaction id.
1105    * @param {object} callback User-defined callback object.
1106    * @param {string} uri Fully qualified path of resource.
1107    * @param {string} postData POST data to be submitted in addition to HTML form.
1108    * @return {void}
1109    */
1110         uploadFile:function(o, callback, uri, postData){
1112                 // Each iframe has an id prefix of "yuiIO" followed
1113                 // by the unique transaction id.
1114                 var oConn = this;
1115                 var frameId = 'yuiIO' + o.tId;
1116                 var uploadEncoding = 'multipart/form-data';
1117                 var io = document.getElementById(frameId);
1118                 var args = (callback && callback.argument)?callback.argument:null;
1120                 // Track original HTML form attribute values.
1121                 var rawFormAttributes =
1122                 {
1123                         action:this._formNode.getAttribute('action'),
1124                         method:this._formNode.getAttribute('method'),
1125                         target:this._formNode.getAttribute('target')
1126                 };
1128                 // Initialize the HTML form properties in case they are
1129                 // not defined in the HTML form.
1130                 this._formNode.setAttribute('action', uri);
1131                 this._formNode.setAttribute('method', 'POST');
1132                 this._formNode.setAttribute('target', frameId);
1134                 if(YAHOO.env.ua.ie){
1135                         // IE does not respect property enctype for HTML forms.
1136                         // Instead it uses the property - "encoding".
1137                         this._formNode.setAttribute('encoding', uploadEncoding);
1138                 }
1139                 else{
1140                         this._formNode.setAttribute('enctype', uploadEncoding);
1141                 }
1143                 if(postData){
1144                         var oElements = this.appendPostData(postData);
1145                 }
1147                 // Start file upload.
1148                 this._formNode.submit();
1150                 // Fire global custom event -- startEvent
1151                 this.startEvent.fire(o, args);
1153                 if(o.startEvent){
1154                         // Fire transaction custom event -- startEvent
1155                         o.startEvent.fire(o, args);
1156                 }
1158                 // Start polling if a callback is present and the timeout
1159                 // property has been defined.
1160                 if(callback && callback.timeout){
1161                         this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
1162                 }
1164                 // Remove HTML elements created by appendPostData
1165                 if(oElements && oElements.length > 0){
1166                         for(var i=0; i < oElements.length; i++){
1167                                 this._formNode.removeChild(oElements[i]);
1168                         }
1169                 }
1171                 // Restore HTML form attributes to their original
1172                 // values prior to file upload.
1173                 for(var prop in rawFormAttributes){
1174                         if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
1175                                 if(rawFormAttributes[prop]){
1176                                         this._formNode.setAttribute(prop, rawFormAttributes[prop]);
1177                                 }
1178                                 else{
1179                                         this._formNode.removeAttribute(prop);
1180                                 }
1181                         }
1182                 }
1184                 // Reset HTML form state properties.
1185                 this.resetFormState();
1187                 // Create the upload callback handler that fires when the iframe
1188                 // receives the load event.  Subsequently, the event handler is detached
1189                 // and the iframe removed from the document.
1190                 var uploadCallback = function()
1191                 {
1192                         if(callback && callback.timeout){
1193                                 window.clearTimeout(oConn._timeOut[o.tId]);
1194                                 delete oConn._timeOut[o.tId];
1195                         }
1197                         // Fire global custom event -- completeEvent
1198                         oConn.completeEvent.fire(o, args);
1200                         if(o.completeEvent){
1201                                 // Fire transaction custom event -- completeEvent
1202                                 o.completeEvent.fire(o, args);
1203                         }
1205                         var obj = {};
1206                         obj.tId = o.tId;
1207                         obj.argument = callback.argument;
1209                         try
1210                         {
1211                                 // responseText and responseXML will be populated with the same data from the iframe.
1212                                 // Since the HTTP headers cannot be read from the iframe
1213                                 obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
1214                                 obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
1215                         }
1216                         catch(e){}
1218                         if(callback && callback.upload){
1219                                 if(!callback.scope){
1220                                         callback.upload(obj);
1221                                 }
1222                                 else{
1223                                         callback.upload.apply(callback.scope, [obj]);
1224                                 }
1225                         }
1227                         // Fire global custom event -- uploadEvent
1228                         oConn.uploadEvent.fire(obj);
1230                         if(o.uploadEvent){
1231                                 // Fire transaction custom event -- uploadEvent
1232                                 o.uploadEvent.fire(obj);
1233                         }
1235                         YAHOO.util.Event.removeListener(io, "load", uploadCallback);
1237                         setTimeout(
1238                                 function(){
1239                                         document.body.removeChild(io);
1240                                         oConn.releaseObject(o);
1241                                 }, 100);
1242                 };
1244                 // Bind the onload handler to the iframe to detect the file upload response.
1245                 YAHOO.util.Event.addListener(io, "load", uploadCallback);
1246         },
1248   /**
1249    * @description Method to terminate a transaction, if it has not reached readyState 4.
1250    * @method abort
1251    * @public
1252    * @static
1253    * @param {object} o The connection object returned by asyncRequest.
1254    * @param {object} callback  User-defined callback object.
1255    * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
1256    * @return {boolean}
1257    */
1258         abort:function(o, callback, isTimeout)
1259         {
1260                 var abortStatus;
1261                 var args = (callback && callback.argument)?callback.argument:null;
1264                 if(o && o.conn){
1265                         if(this.isCallInProgress(o)){
1266                                 // Issue abort request
1267                                 o.conn.abort();
1269                                 window.clearInterval(this._poll[o.tId]);
1270                                 delete this._poll[o.tId];
1272                                 if(isTimeout){
1273                                         window.clearTimeout(this._timeOut[o.tId]);
1274                                         delete this._timeOut[o.tId];
1275                                 }
1277                                 abortStatus = true;
1278                         }
1279                 }
1280                 else if(o && o.isUpload === true){
1281                         var frameId = 'yuiIO' + o.tId;
1282                         var io = document.getElementById(frameId);
1284                         if(io){
1285                                 // Remove all listeners on the iframe prior to
1286                                 // its destruction.
1287                                 YAHOO.util.Event.removeListener(io, "load");
1288                                 // Destroy the iframe facilitating the transaction.
1289                                 document.body.removeChild(io);
1291                                 if(isTimeout){
1292                                         window.clearTimeout(this._timeOut[o.tId]);
1293                                         delete this._timeOut[o.tId];
1294                                 }
1296                                 abortStatus = true;
1297                         }
1298                 }
1299                 else{
1300                         abortStatus = false;
1301                 }
1303                 if(abortStatus === true){
1304                         // Fire global custom event -- abortEvent
1305                         this.abortEvent.fire(o, args);
1307                         if(o.abortEvent){
1308                                 // Fire transaction custom event -- abortEvent
1309                                 o.abortEvent.fire(o, args);
1310                         }
1312                         this.handleTransactionResponse(o, callback, true);
1313                 }
1315                 return abortStatus;
1316         },
1318   /**
1319    * @description Determines if the transaction is still being processed.
1320    * @method isCallInProgress
1321    * @public
1322    * @static
1323    * @param {object} o The connection object returned by asyncRequest
1324    * @return {boolean}
1325    */
1326         isCallInProgress:function(o)
1327         {
1328                 // if the XHR object assigned to the transaction has not been dereferenced,
1329                 // then check its readyState status.  Otherwise, return false.
1330                 if(o && o.conn){
1331                         return o.conn.readyState !== 4 && o.conn.readyState !== 0;
1332                 }
1333                 else if(o && o.isUpload === true){
1334                         var frameId = 'yuiIO' + o.tId;
1335                         return document.getElementById(frameId)?true:false;
1336                 }
1337                 else{
1338                         return false;
1339                 }
1340         },
1342   /**
1343    * @description Dereference the XHR instance and the connection object after the transaction is completed.
1344    * @method releaseObject
1345    * @private
1346    * @static
1347    * @param {object} o The connection object
1348    * @return {void}
1349    */
1350         releaseObject:function(o)
1351         {
1352                 if(o && o.conn){
1353                         //dereference the XHR instance.
1354                         o.conn = null;
1356                         //dereference the connection object.
1357                         o = null;
1358                 }
1359         }
1362 YAHOO.register("connection", YAHOO.util.Connect, {version: "2.5.2", build: "1076"});