Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / lib / yui / connection / connection-debug.js
blobf1ec6f52982e638380641ebc440333df226c2df6
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                 YAHOO.log('ActiveX Program Id  ' + id + ' added to _msxml_progid.', 'info', 'Connection');
336         },
338   /**
339    * @description Member to override the default POST header.
340    * @method setDefaultPostHeader
341    * @public
342    * @static
343    * @param {boolean} b Set and use default header - true or false .
344    * @return void
345    */
346         setDefaultPostHeader:function(b)
347         {
348                 if(typeof b == 'string'){
349                         this._default_post_header = b;
350                         YAHOO.log('Default POST header set to  ' + b, 'info', 'Connection');
351                 }
352                 else if(typeof b == 'boolean'){
353                         this._use_default_post_header = b;
354                 }
355         },
357   /**
358    * @description Member to override the default transaction header..
359    * @method setDefaultXhrHeader
360    * @public
361    * @static
362    * @param {boolean} b Set and use default header - true or false .
363    * @return void
364    */
365         setDefaultXhrHeader:function(b)
366         {
367                 if(typeof b == 'string'){
368                         this._default_xhr_header = b;
369                         YAHOO.log('Default XHR header set to  ' + b, 'info', 'Connection');
370                 }
371                 else{
372                         this._use_default_xhr_header = b;
373                 }
374         },
376   /**
377    * @description Member to modify the default polling interval.
378    * @method setPollingInterval
379    * @public
380    * @static
381    * @param {int} i The polling interval in milliseconds.
382    * @return void
383    */
384         setPollingInterval:function(i)
385         {
386                 if(typeof i == 'number' && isFinite(i)){
387                         this._polling_interval = i;
388                         YAHOO.log('Default polling interval set to ' + i +'ms', 'info', 'Connection');
389                 }
390         },
392   /**
393    * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
394    * the XMLHttpRequest instance and the transaction id.
395    * @method createXhrObject
396    * @private
397    * @static
398    * @param {int} transactionId Property containing the transaction id for this transaction.
399    * @return object
400    */
401         createXhrObject:function(transactionId)
402         {
403                 var obj,http;
404                 try
405                 {
406                         // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
407                         http = new XMLHttpRequest();
408                         //  Object literal with http and tId properties
409                         obj = { conn:http, tId:transactionId };
410                         YAHOO.log('XHR object created for transaction ' + transactionId, 'info', 'Connection');
411                 }
412                 catch(e)
413                 {
414                         for(var i=0; i<this._msxml_progid.length; ++i){
415                                 try
416                                 {
417                                         // Instantiates XMLHttpRequest for IE and assign to http
418                                         http = new ActiveXObject(this._msxml_progid[i]);
419                                         //  Object literal with conn and tId properties
420                                         obj = { conn:http, tId:transactionId };
421                                         YAHOO.log('ActiveX XHR object created for transaction ' + transactionId, 'info', 'Connection');
422                                         break;
423                                 }
424                                 catch(e){}
425                         }
426                 }
427                 finally
428                 {
429                         return obj;
430                 }
431         },
433   /**
434    * @description This method is called by asyncRequest to create a
435    * valid connection object for the transaction.  It also passes a
436    * transaction id and increments the transaction id counter.
437    * @method getConnectionObject
438    * @private
439    * @static
440    * @return {object}
441    */
442         getConnectionObject:function(isFileUpload)
443         {
444                 var o;
445                 var tId = this._transaction_id;
447                 try
448                 {
449                         if(!isFileUpload){
450                                 o = this.createXhrObject(tId);
451                         }
452                         else{
453                                 o = {};
454                                 o.tId = tId;
455                                 o.isUpload = true;
456                         }
458                         if(o){
459                                 this._transaction_id++;
460                         }
461                 }
462                 catch(e){}
463                 finally
464                 {
465                         return o;
466                 }
467         },
469   /**
470    * @description Method for initiating an asynchronous request via the XHR object.
471    * @method asyncRequest
472    * @public
473    * @static
474    * @param {string} method HTTP transaction method
475    * @param {string} uri Fully qualified path of resource
476    * @param {callback} callback User-defined callback function or object
477    * @param {string} postData POST body
478    * @return {object} Returns the connection object
479    */
480         asyncRequest:function(method, uri, callback, postData)
481         {
482                 var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
483                 var args = (callback && callback.argument)?callback.argument:null;
485                 if(!o){
486                         YAHOO.log('Unable to create connection object.', 'error', 'Connection');
487                         return null;
488                 }
489                 else{
491                         // Intialize any transaction-specific custom events, if provided.
492                         if(callback && callback.customevents){
493                                 this.initCustomEvents(o, callback);
494                         }
496                         if(this._isFormSubmit){
497                                 if(this._isFileUpload){
498                                         this.uploadFile(o, callback, uri, postData);
499                                         return o;
500                                 }
502                                 // If the specified HTTP method is GET, setForm() will return an
503                                 // encoded string that is concatenated to the uri to
504                                 // create a querystring.
505                                 if(method.toUpperCase() == 'GET'){
506                                         if(this._sFormData.length !== 0){
507                                                 // If the URI already contains a querystring, append an ampersand
508                                                 // and then concatenate _sFormData to the URI.
509                                                 uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
510                                         }
511                                 }
512                                 else if(method.toUpperCase() == 'POST'){
513                                         // If POST data exist in addition to the HTML form data,
514                                         // it will be concatenated to the form data.
515                                         postData = postData?this._sFormData + "&" + postData:this._sFormData;
516                                 }
517                         }
519                         if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
520                                 // If callback.cache is defined and set to false, a
521                                 // timestamp value will be added to the querystring.
522                                 uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
523                         }
525                         o.conn.open(method, uri, true);
527                         // Each transaction will automatically include a custom header of
528                         // "X-Requested-With: XMLHttpRequest" to identify the request as
529                         // having originated from Connection Manager.
530                         if(this._use_default_xhr_header){
531                                 if(!this._default_headers['X-Requested-With']){
532                                         this.initHeader('X-Requested-With', this._default_xhr_header, true);
533                                         YAHOO.log('Initialize transaction header X-Request-Header to XMLHttpRequest.', 'info', 'Connection');
534                                 }
535                         }
537                         //If the transaction method is POST and the POST header value is set to true
538                         //or a custom value, initalize the Content-Type header to this value.
539                         if((method.toUpperCase() == 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
540                                 this.initHeader('Content-Type', this._default_post_header);
541                                 YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded; UTF-8 for POST transaction.', 'info', 'Connection');
542                         }
544                         //Initialize all default and custom HTTP headers,
545                         if(this._has_default_headers || this._has_http_headers){
546                                 this.setHeader(o);
547                         }
549                         this.handleReadyState(o, callback);
550                         o.conn.send(postData || '');
551                         YAHOO.log('Transaction ' + o.tId + ' sent.', 'info', 'Connection');
554                         // Reset the HTML form data and state properties as
555                         // soon as the data are submitted.
556                         if(this._isFormSubmit === true){
557                                 this.resetFormState();
558                         }
560                         // Fire global custom event -- startEvent
561                         this.startEvent.fire(o, args);
563                         if(o.startEvent){
564                                 // Fire transaction custom event -- startEvent
565                                 o.startEvent.fire(o, args);
566                         }
568                         return o;
569                 }
570         },
572   /**
573    * @description This method creates and subscribes custom events,
574    * specific to each transaction
575    * @method initCustomEvents
576    * @private
577    * @static
578    * @param {object} o The connection object
579    * @param {callback} callback The user-defined callback object
580    * @return {void}
581    */
582         initCustomEvents:function(o, callback)
583         {
584                 // Enumerate through callback.customevents members and bind/subscribe
585                 // events that match in the _customEvents table.
586                 for(var prop in callback.customevents){
587                         if(this._customEvents[prop][0]){
588                                 // Create the custom event
589                                 o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
590                                 YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' created.', 'info', 'Connection');
592                                 // Subscribe the custom event
593                                 o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
594                                 YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' subscribed.', 'info', 'Connection');
595                         }
596                 }
597         },
599   /**
600    * @description This method serves as a timer that polls the XHR object's readyState
601    * property during a transaction, instead of binding a callback to the
602    * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
603    * will process the response, and the timer will be cleared.
604    * @method handleReadyState
605    * @private
606    * @static
607    * @param {object} o The connection object
608    * @param {callback} callback The user-defined callback object
609    * @return {void}
610    */
612     handleReadyState:function(o, callback)
614     {
615                 var oConn = this;
616                 var args = (callback && callback.argument)?callback.argument:null;
618                 if(callback && callback.timeout){
619                         this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
620                 }
622                 this._poll[o.tId] = window.setInterval(
623                         function(){
624                                 if(o.conn && o.conn.readyState === 4){
626                                         // Clear the polling interval for the transaction
627                                         // and remove the reference from _poll.
628                                         window.clearInterval(oConn._poll[o.tId]);
629                                         delete oConn._poll[o.tId];
631                                         if(callback && callback.timeout){
632                                                 window.clearTimeout(oConn._timeOut[o.tId]);
633                                                 delete oConn._timeOut[o.tId];
634                                         }
636                                         // Fire global custom event -- completeEvent
637                                         oConn.completeEvent.fire(o, args);
639                                         if(o.completeEvent){
640                                                 // Fire transaction custom event -- completeEvent
641                                                 o.completeEvent.fire(o, args);
642                                         }
644                                         oConn.handleTransactionResponse(o, callback);
645                                 }
646                         }
647                 ,this._polling_interval);
648     },
650   /**
651    * @description This method attempts to interpret the server response and
652    * determine whether the transaction was successful, or if an error or
653    * exception was encountered.
654    * @method handleTransactionResponse
655    * @private
656    * @static
657    * @param {object} o The connection object
658    * @param {object} callback The user-defined callback object
659    * @param {boolean} isAbort Determines if the transaction was terminated via abort().
660    * @return {void}
661    */
662     handleTransactionResponse:function(o, callback, isAbort)
663     {
664                 var httpStatus, responseObject;
665                 var args = (callback && callback.argument)?callback.argument:null;
667                 try
668                 {
669                         if(o.conn.status !== undefined && o.conn.status !== 0){
670                                 httpStatus = o.conn.status;
671                         }
672                         else{
673                                 httpStatus = 13030;
674                         }
675                 }
676                 catch(e){
678                          // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
679                          // when the XHR object's status and statusText properties are
680                          // unavailable, and a query attempt throws an exception.
681                         httpStatus = 13030;
682                 }
684                 if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
685                         responseObject = this.createResponseObject(o, args);
686                         if(callback && callback.success){
687                                 if(!callback.scope){
688                                         callback.success(responseObject);
689                                         YAHOO.log('Success callback. HTTP code is ' + httpStatus, 'info', 'Connection');
690                                 }
691                                 else{
692                                         // If a scope property is defined, the callback will be fired from
693                                         // the context of the object.
694                                         callback.success.apply(callback.scope, [responseObject]);
695                                         YAHOO.log('Success callback with scope. HTTP code is ' + httpStatus, 'info', 'Connection');
696                                 }
697                         }
699                         // Fire global custom event -- successEvent
700                         this.successEvent.fire(responseObject);
702                         if(o.successEvent){
703                                 // Fire transaction custom event -- successEvent
704                                 o.successEvent.fire(responseObject);
705                         }
706                 }
707                 else{
708                         switch(httpStatus){
709                                 // The following cases are wininet.dll error codes that may be encountered.
710                                 case 12002: // Server timeout
711                                 case 12029: // 12029 to 12031 correspond to dropped connections.
712                                 case 12030:
713                                 case 12031:
714                                 case 12152: // Connection closed by server.
715                                 case 13030: // See above comments for variable status.
716                                         responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
717                                         if(callback && callback.failure){
718                                                 if(!callback.scope){
719                                                         callback.failure(responseObject);
720                                                         YAHOO.log('Failure callback. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection');
721                                                 }
722                                                 else{
723                                                         callback.failure.apply(callback.scope, [responseObject]);
724                                                         YAHOO.log('Failure callback with scope. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection');
725                                                 }
726                                         }
728                                         break;
729                                 default:
730                                         responseObject = this.createResponseObject(o, args);
731                                         if(callback && callback.failure){
732                                                 if(!callback.scope){
733                                                         callback.failure(responseObject);
734                                                         YAHOO.log('Failure callback. HTTP status code is ' + httpStatus, 'warn', 'Connection');
735                                                 }
736                                                 else{
737                                                         callback.failure.apply(callback.scope, [responseObject]);
738                                                         YAHOO.log('Failure callback with scope. HTTP status code is ' + httpStatus, 'warn', 'Connection');
739                                                 }
740                                         }
741                         }
743                         // Fire global custom event -- failureEvent
744                         this.failureEvent.fire(responseObject);
746                         if(o.failureEvent){
747                                 // Fire transaction custom event -- failureEvent
748                                 o.failureEvent.fire(responseObject);
749                         }
751                 }
753                 this.releaseObject(o);
754                 responseObject = null;
755     },
757   /**
758    * @description This method evaluates the server response, creates and returns the results via
759    * its properties.  Success and failure cases will differ in the response
760    * object's property values.
761    * @method createResponseObject
762    * @private
763    * @static
764    * @param {object} o The connection object
765    * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
766    * @return {object}
767    */
768     createResponseObject:function(o, callbackArg)
769     {
770                 var obj = {};
771                 var headerObj = {};
773                 try
774                 {
775                         var headerStr = o.conn.getAllResponseHeaders();
776                         var header = headerStr.split('\n');
777                         for(var i=0; i<header.length; i++){
778                                 var delimitPos = header[i].indexOf(':');
779                                 if(delimitPos != -1){
780                                         headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
781                                 }
782                         }
783                 }
784                 catch(e){}
786                 obj.tId = o.tId;
787                 // Normalize IE's response to HTTP 204 when Win error 1223.
788                 obj.status = (o.conn.status == 1223)?204:o.conn.status;
789                 // Normalize IE's statusText to "No Content" instead of "Unknown".
790                 obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
791                 obj.getResponseHeader = headerObj;
792                 obj.getAllResponseHeaders = headerStr;
793                 obj.responseText = o.conn.responseText;
794                 obj.responseXML = o.conn.responseXML;
796                 if(callbackArg){
797                         obj.argument = callbackArg;
798                 }
800                 return obj;
801     },
803   /**
804    * @description If a transaction cannot be completed due to dropped or closed connections,
805    * there may be not be enough information to build a full response object.
806    * The failure callback will be fired and this specific condition can be identified
807    * by a status property value of 0.
808    *
809    * If an abort was successful, the status property will report a value of -1.
810    *
811    * @method createExceptionObject
812    * @private
813    * @static
814    * @param {int} tId The Transaction Id
815    * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
816    * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
817    * @return {object}
818    */
819     createExceptionObject:function(tId, callbackArg, isAbort)
820     {
821                 var COMM_CODE = 0;
822                 var COMM_ERROR = 'communication failure';
823                 var ABORT_CODE = -1;
824                 var ABORT_ERROR = 'transaction aborted';
826                 var obj = {};
828                 obj.tId = tId;
829                 if(isAbort){
830                         obj.status = ABORT_CODE;
831                         obj.statusText = ABORT_ERROR;
832                 }
833                 else{
834                         obj.status = COMM_CODE;
835                         obj.statusText = COMM_ERROR;
836                 }
838                 if(callbackArg){
839                         obj.argument = callbackArg;
840                 }
842                 return obj;
843     },
845   /**
846    * @description Method that initializes the custom HTTP headers for the each transaction.
847    * @method initHeader
848    * @public
849    * @static
850    * @param {string} label The HTTP header label
851    * @param {string} value The HTTP header value
852    * @param {string} isDefault Determines if the specific header is a default header
853    * automatically sent with each transaction.
854    * @return {void}
855    */
856         initHeader:function(label, value, isDefault)
857         {
858                 var headerObj = (isDefault)?this._default_headers:this._http_headers;
859                 headerObj[label] = value;
861                 if(isDefault){
862                         this._has_default_headers = true;
863                 }
864                 else{
865                         this._has_http_headers = true;
866                 }
867         },
870   /**
871    * @description Accessor that sets the HTTP headers for each transaction.
872    * @method setHeader
873    * @private
874    * @static
875    * @param {object} o The connection object for the transaction.
876    * @return {void}
877    */
878         setHeader:function(o)
879         {
880                 if(this._has_default_headers){
881                         for(var prop in this._default_headers){
882                                 if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
883                                         o.conn.setRequestHeader(prop, this._default_headers[prop]);
884                                         YAHOO.log('Default HTTP header ' + prop + ' set with value of ' + this._default_headers[prop], 'info', 'Connection');
885                                 }
886                         }
887                 }
889                 if(this._has_http_headers){
890                         for(var prop in this._http_headers){
891                                 if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
892                                         o.conn.setRequestHeader(prop, this._http_headers[prop]);
893                                         YAHOO.log('HTTP header ' + prop + ' set with value of ' + this._http_headers[prop], 'info', 'Connection');
894                                 }
895                         }
896                         delete this._http_headers;
898                         this._http_headers = {};
899                         this._has_http_headers = false;
900                 }
901         },
903   /**
904    * @description Resets the default HTTP headers object
905    * @method resetDefaultHeaders
906    * @public
907    * @static
908    * @return {void}
909    */
910         resetDefaultHeaders:function(){
911                 delete this._default_headers;
912                 this._default_headers = {};
913                 this._has_default_headers = false;
914         },
916   /**
917    * @description This method assembles the form label and value pairs and
918    * constructs an encoded string.
919    * asyncRequest() will automatically initialize the transaction with a
920    * a HTTP header Content-Type of application/x-www-form-urlencoded.
921    * @method setForm
922    * @public
923    * @static
924    * @param {string || object} form id or name attribute, or form object.
925    * @param {boolean} optional enable file upload.
926    * @param {boolean} optional enable file upload over SSL in IE only.
927    * @return {string} string of the HTML form field name and value pairs..
928    */
929         setForm:function(formId, isUpload, secureUri)
930         {
931                 // reset the HTML form data and state properties
932                 this.resetFormState();
934                 var oForm;
935                 if(typeof formId == 'string'){
936                         // Determine if the argument is a form id or a form name.
937                         // Note form name usage is deprecated, but supported
938                         // here for backward compatibility.
939                         oForm = (document.getElementById(formId) || document.forms[formId]);
940                 }
941                 else if(typeof formId == 'object'){
942                         // Treat argument as an HTML form object.
943                         oForm = formId;
944                 }
945                 else{
946                         YAHOO.log('Unable to create form object ' + formId, 'warn', 'Connection');
947                         return;
948                 }
950                 // If the isUpload argument is true, setForm will call createFrame to initialize
951                 // an iframe as the form target.
952                 //
953                 // The argument secureURI is also required by IE in SSL environments
954                 // where the secureURI string is a fully qualified HTTP path, used to set the source
955                 // of the iframe, to a stub resource in the same domain.
956                 if(isUpload){
958                         // Create iframe in preparation for file upload.
959                         var io = this.createFrame((window.location.href.toLowerCase().indexOf("https") === 0 || secureUri)?true:false);
960                         // Set form reference and file upload properties to true.
961                         this._isFormSubmit = true;
962                         this._isFileUpload = true;
963                         this._formNode = oForm;
965                         return;
967                 }
969                 var oElement, oName, oValue, oDisabled;
970                 var hasSubmit = false;
972                 // Iterate over the form elements collection to construct the
973                 // label-value pairs.
974                 for (var i=0; i<oForm.elements.length; i++){
975                         oElement = oForm.elements[i];
976                         oDisabled = oElement.disabled;
977                         oName = oElement.name;
978                         oValue = oElement.value;
980                         // Do not submit fields that are disabled or
981                         // do not have a name attribute value.
982                         if(!oDisabled && oName)
983                         {
984                                 switch(oElement.type)
985                                 {
986                                         case 'select-one':
987                                         case 'select-multiple':
988                                                 for(var j=0; j<oElement.options.length; j++){
989                                                         if(oElement.options[j].selected){
990                                                                 if(window.ActiveXObject){
991                                                                         this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
992                                                                 }
993                                                                 else{
994                                                                         this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
995                                                                 }
996                                                         }
997                                                 }
998                                                 break;
999                                         case 'radio':
1000                                         case 'checkbox':
1001                                                 if(oElement.checked){
1002                                                         this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1003                                                 }
1004                                                 break;
1005                                         case 'file':
1006                                                 // stub case as XMLHttpRequest will only send the file path as a string.
1007                                         case undefined:
1008                                                 // stub case for fieldset element which returns undefined.
1009                                         case 'reset':
1010                                                 // stub case for input type reset button.
1011                                         case 'button':
1012                                                 // stub case for input type button elements.
1013                                                 break;
1014                                         case 'submit':
1015                                                 if(hasSubmit === false){
1016                                                         if(this._hasSubmitListener && this._submitElementValue){
1017                                                                 this._sFormData += this._submitElementValue + '&';
1018                                                         }
1019                                                         else{
1020                                                                 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1021                                                         }
1023                                                         hasSubmit = true;
1024                                                 }
1025                                                 break;
1026                                         default:
1027                                                 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1028                                 }
1029                         }
1030                 }
1032                 this._isFormSubmit = true;
1033                 this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
1035                 YAHOO.log('Form initialized for transaction. HTML form POST message is: ' + this._sFormData, 'info', 'Connection');
1037                 this.initHeader('Content-Type', this._default_form_header);
1038                 YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded for setForm() transaction.', 'info', 'Connection');
1040                 return this._sFormData;
1041         },
1043   /**
1044    * @description Resets HTML form properties when an HTML form or HTML form
1045    * with file upload transaction is sent.
1046    * @method resetFormState
1047    * @private
1048    * @static
1049    * @return {void}
1050    */
1051         resetFormState:function(){
1052                 this._isFormSubmit = false;
1053                 this._isFileUpload = false;
1054                 this._formNode = null;
1055                 this._sFormData = "";
1056         },
1058   /**
1059    * @description Creates an iframe to be used for form file uploads.  It is remove from the
1060    * document upon completion of the upload transaction.
1061    * @method createFrame
1062    * @private
1063    * @static
1064    * @param {string} optional qualified path of iframe resource for SSL in IE.
1065    * @return {void}
1066    */
1067         createFrame:function(secureUri){
1069                 // IE does not allow the setting of id and name attributes as object
1070                 // properties via createElement().  A different iframe creation
1071                 // pattern is required for IE.
1072                 var frameId = 'yuiIO' + this._transaction_id;
1073                 var io;
1074                 if(window.ActiveXObject){
1075                         io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
1077                         // IE will throw a security exception in an SSL environment if the
1078                         // iframe source is undefined.
1079                         if(typeof secureUri == 'boolean'){
1080                                 io.src = 'javascript:false';
1081                         }
1082                 }
1083                 else{
1084                         io = document.createElement('iframe');
1085                         io.id = frameId;
1086                         io.name = frameId;
1087                 }
1089                 io.style.position = 'absolute';
1090                 io.style.top = '-1000px';
1091                 io.style.left = '-1000px';
1093                 document.body.appendChild(io);
1094                 YAHOO.log('File upload iframe created. Id is:' + frameId, 'info', 'Connection');
1095         },
1097   /**
1098    * @description Parses the POST data and creates hidden form elements
1099    * for each key-value, and appends them to the HTML form object.
1100    * @method appendPostData
1101    * @private
1102    * @static
1103    * @param {string} postData The HTTP POST data
1104    * @return {array} formElements Collection of hidden fields.
1105    */
1106         appendPostData:function(postData)
1107         {
1108                 var formElements = [];
1109                 var postMessage = postData.split('&');
1110                 for(var i=0; i < postMessage.length; i++){
1111                         var delimitPos = postMessage[i].indexOf('=');
1112                         if(delimitPos != -1){
1113                                 formElements[i] = document.createElement('input');
1114                                 formElements[i].type = 'hidden';
1115                                 formElements[i].name = postMessage[i].substring(0,delimitPos);
1116                                 formElements[i].value = postMessage[i].substring(delimitPos+1);
1117                                 this._formNode.appendChild(formElements[i]);
1118                         }
1119                 }
1121                 return formElements;
1122         },
1124   /**
1125    * @description Uploads HTML form, inclusive of files/attachments, using the
1126    * iframe created in createFrame to facilitate the transaction.
1127    * @method uploadFile
1128    * @private
1129    * @static
1130    * @param {int} id The transaction id.
1131    * @param {object} callback User-defined callback object.
1132    * @param {string} uri Fully qualified path of resource.
1133    * @param {string} postData POST data to be submitted in addition to HTML form.
1134    * @return {void}
1135    */
1136         uploadFile:function(o, callback, uri, postData){
1138                 // Each iframe has an id prefix of "yuiIO" followed
1139                 // by the unique transaction id.
1140                 var oConn = this;
1141                 var frameId = 'yuiIO' + o.tId;
1142                 var uploadEncoding = 'multipart/form-data';
1143                 var io = document.getElementById(frameId);
1144                 var args = (callback && callback.argument)?callback.argument:null;
1146                 // Track original HTML form attribute values.
1147                 var rawFormAttributes =
1148                 {
1149                         action:this._formNode.getAttribute('action'),
1150                         method:this._formNode.getAttribute('method'),
1151                         target:this._formNode.getAttribute('target')
1152                 };
1154                 // Initialize the HTML form properties in case they are
1155                 // not defined in the HTML form.
1156                 this._formNode.setAttribute('action', uri);
1157                 this._formNode.setAttribute('method', 'POST');
1158                 this._formNode.setAttribute('target', frameId);
1160                 if(YAHOO.env.ua.ie){
1161                         // IE does not respect property enctype for HTML forms.
1162                         // Instead it uses the property - "encoding".
1163                         this._formNode.setAttribute('encoding', uploadEncoding);
1164                 }
1165                 else{
1166                         this._formNode.setAttribute('enctype', uploadEncoding);
1167                 }
1169                 if(postData){
1170                         var oElements = this.appendPostData(postData);
1171                 }
1173                 // Start file upload.
1174                 this._formNode.submit();
1176                 // Fire global custom event -- startEvent
1177                 this.startEvent.fire(o, args);
1179                 if(o.startEvent){
1180                         // Fire transaction custom event -- startEvent
1181                         o.startEvent.fire(o, args);
1182                 }
1184                 // Start polling if a callback is present and the timeout
1185                 // property has been defined.
1186                 if(callback && callback.timeout){
1187                         this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
1188                 }
1190                 // Remove HTML elements created by appendPostData
1191                 if(oElements && oElements.length > 0){
1192                         for(var i=0; i < oElements.length; i++){
1193                                 this._formNode.removeChild(oElements[i]);
1194                         }
1195                 }
1197                 // Restore HTML form attributes to their original
1198                 // values prior to file upload.
1199                 for(var prop in rawFormAttributes){
1200                         if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
1201                                 if(rawFormAttributes[prop]){
1202                                         this._formNode.setAttribute(prop, rawFormAttributes[prop]);
1203                                 }
1204                                 else{
1205                                         this._formNode.removeAttribute(prop);
1206                                 }
1207                         }
1208                 }
1210                 // Reset HTML form state properties.
1211                 this.resetFormState();
1213                 // Create the upload callback handler that fires when the iframe
1214                 // receives the load event.  Subsequently, the event handler is detached
1215                 // and the iframe removed from the document.
1216                 var uploadCallback = function()
1217                 {
1218                         if(callback && callback.timeout){
1219                                 window.clearTimeout(oConn._timeOut[o.tId]);
1220                                 delete oConn._timeOut[o.tId];
1221                         }
1223                         // Fire global custom event -- completeEvent
1224                         oConn.completeEvent.fire(o, args);
1226                         if(o.completeEvent){
1227                                 // Fire transaction custom event -- completeEvent
1228                                 o.completeEvent.fire(o, args);
1229                         }
1231                         var obj = {};
1232                         obj.tId = o.tId;
1233                         obj.argument = callback.argument;
1235                         try
1236                         {
1237                                 // responseText and responseXML will be populated with the same data from the iframe.
1238                                 // Since the HTTP headers cannot be read from the iframe
1239                                 obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
1240                                 obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
1241                         }
1242                         catch(e){}
1244                         if(callback && callback.upload){
1245                                 if(!callback.scope){
1246                                         callback.upload(obj);
1247                                         YAHOO.log('Upload callback.', 'info', 'Connection');
1248                                 }
1249                                 else{
1250                                         callback.upload.apply(callback.scope, [obj]);
1251                                         YAHOO.log('Upload callback with scope.', 'info', 'Connection');
1252                                 }
1253                         }
1255                         // Fire global custom event -- uploadEvent
1256                         oConn.uploadEvent.fire(obj);
1258                         if(o.uploadEvent){
1259                                 // Fire transaction custom event -- uploadEvent
1260                                 o.uploadEvent.fire(obj);
1261                         }
1263                         YAHOO.util.Event.removeListener(io, "load", uploadCallback);
1265                         setTimeout(
1266                                 function(){
1267                                         document.body.removeChild(io);
1268                                         oConn.releaseObject(o);
1269                                         YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
1270                                 }, 100);
1271                 };
1273                 // Bind the onload handler to the iframe to detect the file upload response.
1274                 YAHOO.util.Event.addListener(io, "load", uploadCallback);
1275         },
1277   /**
1278    * @description Method to terminate a transaction, if it has not reached readyState 4.
1279    * @method abort
1280    * @public
1281    * @static
1282    * @param {object} o The connection object returned by asyncRequest.
1283    * @param {object} callback  User-defined callback object.
1284    * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
1285    * @return {boolean}
1286    */
1287         abort:function(o, callback, isTimeout)
1288         {
1289                 var abortStatus;
1290                 var args = (callback && callback.argument)?callback.argument:null;
1293                 if(o && o.conn){
1294                         if(this.isCallInProgress(o)){
1295                                 // Issue abort request
1296                                 o.conn.abort();
1298                                 window.clearInterval(this._poll[o.tId]);
1299                                 delete this._poll[o.tId];
1301                                 if(isTimeout){
1302                                         window.clearTimeout(this._timeOut[o.tId]);
1303                                         delete this._timeOut[o.tId];
1304                                 }
1306                                 abortStatus = true;
1307                         }
1308                 }
1309                 else if(o && o.isUpload === true){
1310                         var frameId = 'yuiIO' + o.tId;
1311                         var io = document.getElementById(frameId);
1313                         if(io){
1314                                 // Remove all listeners on the iframe prior to
1315                                 // its destruction.
1316                                 YAHOO.util.Event.removeListener(io, "load");
1317                                 // Destroy the iframe facilitating the transaction.
1318                                 document.body.removeChild(io);
1319                                 YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
1321                                 if(isTimeout){
1322                                         window.clearTimeout(this._timeOut[o.tId]);
1323                                         delete this._timeOut[o.tId];
1324                                 }
1326                                 abortStatus = true;
1327                         }
1328                 }
1329                 else{
1330                         abortStatus = false;
1331                 }
1333                 if(abortStatus === true){
1334                         // Fire global custom event -- abortEvent
1335                         this.abortEvent.fire(o, args);
1337                         if(o.abortEvent){
1338                                 // Fire transaction custom event -- abortEvent
1339                                 o.abortEvent.fire(o, args);
1340                         }
1342                         this.handleTransactionResponse(o, callback, true);
1343                         YAHOO.log('Transaction ' + o.tId + ' aborted.', 'info', 'Connection');
1344                 }
1346                 return abortStatus;
1347         },
1349   /**
1350    * @description Determines if the transaction is still being processed.
1351    * @method isCallInProgress
1352    * @public
1353    * @static
1354    * @param {object} o The connection object returned by asyncRequest
1355    * @return {boolean}
1356    */
1357         isCallInProgress:function(o)
1358         {
1359                 // if the XHR object assigned to the transaction has not been dereferenced,
1360                 // then check its readyState status.  Otherwise, return false.
1361                 if(o && o.conn){
1362                         return o.conn.readyState !== 4 && o.conn.readyState !== 0;
1363                 }
1364                 else if(o && o.isUpload === true){
1365                         var frameId = 'yuiIO' + o.tId;
1366                         return document.getElementById(frameId)?true:false;
1367                 }
1368                 else{
1369                         return false;
1370                 }
1371         },
1373   /**
1374    * @description Dereference the XHR instance and the connection object after the transaction is completed.
1375    * @method releaseObject
1376    * @private
1377    * @static
1378    * @param {object} o The connection object
1379    * @return {void}
1380    */
1381         releaseObject:function(o)
1382         {
1383                 if(o && o.conn){
1384                         //dereference the XHR instance.
1385                         o.conn = null;
1387                         YAHOO.log('Connection object for transaction ' + o.tId + ' destroyed.', 'info', 'Connection');
1389                         //dereference the connection object.
1390                         o = null;
1391                 }
1392         }
1395 YAHOO.register("connection", YAHOO.util.Connect, {version: "2.5.2", build: "1076"});