Added LinuxChix theme
[moodle-linuxchix.git] / lib / yui / connection / connection-debug.js
blob08e0f80893aab45610c50ab294d2627b5a30db21
1 /*
2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.3.0
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.
13 * @namespace YAHOO.util
14 * @module connection
15 * @requires yahoo
16 * @requires event
19 /**
20 * The Connection Manager singleton provides methods for creating and managing
21 * asynchronous transactions.
23 * @class Connect
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
35 _msxml_progid:[
36 'MSXML2.XMLHTTP.3.0',
37 'MSXML2.XMLHTTP',
38 'Microsoft.XMLHTTP'
41 /**
42 * @description Object literal of HTTP header(s)
43 * @property _http_header
44 * @private
45 * @static
46 * @type object
48 _http_headers:{},
50 /**
51 * @description Determines if HTTP headers are set.
52 * @property _has_http_headers
53 * @private
54 * @static
55 * @type boolean
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
69 _use_default_post_header:true,
71 /**
72 * @description Determines if a default header of
73 * Content-Type of 'application/x-www-form-urlencoded'
74 * will be added to client HTTP headers sent for POST
75 * transactions.
76 * @property _default_post_header
77 * @private
78 * @static
79 * @type boolean
81 _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',
83 /**
84 * @description Determines if a default header of
85 * 'X-Requested-With: XMLHttpRequest'
86 * will be added to each transaction.
87 * @property _use_default_xhr_header
88 * @private
89 * @static
90 * @type boolean
92 _use_default_xhr_header:true,
94 /**
95 * @description The default header value for the label
96 * "X-Requested-With". This is sent with each
97 * transaction, by default, to identify the
98 * request as being made by YUI Connection Manager.
99 * @property _default_xhr_header
100 * @private
101 * @static
102 * @type boolean
104 _default_xhr_header:'XMLHttpRequest',
107 * @description Determines if custom, default headers
108 * are set for each transaction.
109 * @property _has_default_header
110 * @private
111 * @static
112 * @type boolean
114 _has_default_headers:true,
117 * @description Determines if custom, default headers
118 * are set for each transaction.
119 * @property _has_default_header
120 * @private
121 * @static
122 * @type boolean
124 _default_headers:{},
127 * @description Property modified by setForm() to determine if the data
128 * should be submitted as an HTML form.
129 * @property _isFormSubmit
130 * @private
131 * @static
132 * @type boolean
134 _isFormSubmit:false,
137 * @description Property modified by setForm() to determine if a file(s)
138 * upload is expected.
139 * @property _isFileUpload
140 * @private
141 * @static
142 * @type boolean
144 _isFileUpload:false,
147 * @description Property modified by setForm() to set a reference to the HTML
148 * form node if the desired action is file upload.
149 * @property _formNode
150 * @private
151 * @static
152 * @type object
154 _formNode:null,
157 * @description Property modified by setForm() to set the HTML form data
158 * for each transaction.
159 * @property _sFormData
160 * @private
161 * @static
162 * @type string
164 _sFormData:null,
167 * @description Collection of polling references to the polling mechanism in handleReadyState.
168 * @property _poll
169 * @private
170 * @static
171 * @type object
173 _poll:{},
176 * @description Queue of timeout values for each transaction callback with a defined timeout value.
177 * @property _timeOut
178 * @private
179 * @static
180 * @type object
182 _timeOut:{},
185 * @description The polling frequency, in milliseconds, for HandleReadyState.
186 * when attempting to determine a transaction's XHR readyState.
187 * The default is 50 milliseconds.
188 * @property _polling_interval
189 * @private
190 * @static
191 * @type int
193 _polling_interval:50,
196 * @description A transaction counter that increments the transaction id for each transaction.
197 * @property _transaction_id
198 * @private
199 * @static
200 * @type int
202 _transaction_id:0,
205 * @description Tracks the name-value pair of the "clicked" submit button if multiple submit
206 * buttons are present in an HTML form; and, if YAHOO.util.Event is available.
207 * @property _submitElementValue
208 * @private
209 * @static
210 * @type string
212 _submitElementValue:null,
215 * @description Determines whether YAHOO.util.Event is available and returns true or false.
216 * If true, an event listener is bound at the document level to trap click events that
217 * resolve to a target type of "Submit". This listener will enable setForm() to determine
218 * the clicked "Submit" value in a multi-Submit button, HTML form.
219 * @property _hasSubmitListener
220 * @private
221 * @static
223 _hasSubmitListener:(function()
225 if(YAHOO.util.Event){
226 YAHOO.util.Event.addListener(
227 document,
228 'click',
229 function(e){
230 var obj = YAHOO.util.Event.getTarget(e);
231 if(obj.type == 'submit'){
232 YAHOO.util.Connect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
235 return true;
237 return false;
238 })(),
241 * @description Custom event that fires at the start of a transaction
242 * @property startEvent
243 * @private
244 * @static
245 * @type CustomEvent
247 startEvent: new YAHOO.util.CustomEvent('start'),
250 * @description Custom event that fires when a transaction response has completed.
251 * @property completeEvent
252 * @private
253 * @static
254 * @type CustomEvent
256 completeEvent: new YAHOO.util.CustomEvent('complete'),
259 * @description Custom event that fires when handleTransactionResponse() determines a
260 * response in the HTTP 2xx range.
261 * @property successEvent
262 * @private
263 * @static
264 * @type CustomEvent
266 successEvent: new YAHOO.util.CustomEvent('success'),
269 * @description Custom event that fires when handleTransactionResponse() determines a
270 * response in the HTTP 4xx/5xx range.
271 * @property failureEvent
272 * @private
273 * @static
274 * @type CustomEvent
276 failureEvent: new YAHOO.util.CustomEvent('failure'),
279 * @description Custom event that fires when handleTransactionResponse() determines a
280 * response in the HTTP 4xx/5xx range.
281 * @property failureEvent
282 * @private
283 * @static
284 * @type CustomEvent
286 uploadEvent: new YAHOO.util.CustomEvent('upload'),
289 * @description Custom event that fires when a transaction is successfully aborted.
290 * @property abortEvent
291 * @private
292 * @static
293 * @type CustomEvent
295 abortEvent: new YAHOO.util.CustomEvent('abort'),
298 * @description A reference table that maps callback custom events members to its specific
299 * event name.
300 * @property _customEvents
301 * @private
302 * @static
303 * @type object
305 _customEvents:
307 onStart:['startEvent', 'start'],
308 onComplete:['completeEvent', 'complete'],
309 onSuccess:['successEvent', 'success'],
310 onFailure:['failureEvent', 'failure'],
311 onUpload:['uploadEvent', 'upload'],
312 onAbort:['abortEvent', 'abort']
316 * @description Member to add an ActiveX id to the existing xml_progid array.
317 * In the event(unlikely) a new ActiveX id is introduced, it can be added
318 * without internal code modifications.
319 * @method setProgId
320 * @public
321 * @static
322 * @param {string} id The ActiveX id to be added to initialize the XHR object.
323 * @return void
325 setProgId:function(id)
327 this._msxml_progid.unshift(id);
328 YAHOO.log('ActiveX Program Id ' + id + ' added to _msxml_progid.', 'info', 'Connection');
332 * @description Member to enable or disable the default POST header.
333 * @method setDefaultPostHeader
334 * @public
335 * @static
336 * @param {boolean} b Set and use default header - true or false .
337 * @return void
339 setDefaultPostHeader:function(b)
341 this._use_default_post_header = b;
342 YAHOO.log('Use default POST header set to ' + b, 'info', 'Connection');
346 * @description Member to enable or disable the default POST header.
347 * @method setDefaultXhrHeader
348 * @public
349 * @static
350 * @param {boolean} b Set and use default header - true or false .
351 * @return void
353 setDefaultXhrHeader:function(b)
355 this._use_default_xhr_header = b;
356 YAHOO.log('Use default transaction header set to ' + b, 'info', 'Connection');
360 * @description Member to modify the default polling interval.
361 * @method setPollingInterval
362 * @public
363 * @static
364 * @param {int} i The polling interval in milliseconds.
365 * @return void
367 setPollingInterval:function(i)
369 if(typeof i == 'number' && isFinite(i)){
370 this._polling_interval = i;
371 YAHOO.log('Default polling interval set to ' + i +'ms', 'info', 'Connection');
376 * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
377 * the XMLHttpRequest instance and the transaction id.
378 * @method createXhrObject
379 * @private
380 * @static
381 * @param {int} transactionId Property containing the transaction id for this transaction.
382 * @return object
384 createXhrObject:function(transactionId)
386 var obj,http;
389 // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
390 http = new XMLHttpRequest();
391 // Object literal with http and tId properties
392 obj = { conn:http, tId:transactionId };
393 YAHOO.log('XHR object created for transaction ' + transactionId, 'info', 'Connection');
395 catch(e)
397 for(var i=0; i<this._msxml_progid.length; ++i){
400 // Instantiates XMLHttpRequest for IE and assign to http.
401 http = new ActiveXObject(this._msxml_progid[i]);
402 // Object literal with conn and tId properties
403 obj = { conn:http, tId:transactionId };
404 YAHOO.log('ActiveX XHR object created for transaction ' + transactionId, 'info', 'Connection');
405 break;
407 catch(e){}
410 finally
412 return obj;
417 * @description This method is called by asyncRequest to create a
418 * valid connection object for the transaction. It also passes a
419 * transaction id and increments the transaction id counter.
420 * @method getConnectionObject
421 * @private
422 * @static
423 * @return {object}
425 getConnectionObject:function(isFileUpload)
427 var o;
428 var tId = this._transaction_id;
432 if(!isFileUpload){
433 o = this.createXhrObject(tId);
435 else{
436 o = {};
437 o.tId = tId;
438 o.isUpload = true;
441 if(o){
442 this._transaction_id++;
445 catch(e){}
446 finally
448 return o;
453 * @description Method for initiating an asynchronous request via the XHR object.
454 * @method asyncRequest
455 * @public
456 * @static
457 * @param {string} method HTTP transaction method
458 * @param {string} uri Fully qualified path of resource
459 * @param {callback} callback User-defined callback function or object
460 * @param {string} postData POST body
461 * @return {object} Returns the connection object
463 asyncRequest:function(method, uri, callback, postData)
465 var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
467 if(!o){
468 YAHOO.log('Unable to create connection object.', 'error', 'Connection');
469 return null;
471 else{
473 // Intialize any transaction-specific custom events, if provided.
474 if(callback && callback.customevents){
475 this.initCustomEvents(o, callback);
478 if(this._isFormSubmit){
479 if(this._isFileUpload){
480 this.uploadFile(o, callback, uri, postData);
481 return o;
484 // If the specified HTTP method is GET, setForm() will return an
485 // encoded string that is concatenated to the uri to
486 // create a querystring.
487 if(method.toUpperCase() == 'GET'){
488 if(this._sFormData.length !== 0){
489 // If the URI already contains a querystring, append an ampersand
490 // and then concatenate _sFormData to the URI.
491 uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
493 else{
494 uri += "?" + this._sFormData;
497 else if(method.toUpperCase() == 'POST'){
498 // If POST data exist in addition to the HTML form data,
499 // it will be concatenated to the form data.
500 postData = postData?this._sFormData + "&" + postData:this._sFormData;
504 o.conn.open(method, uri, true);
505 //this.processTransactionHeaders(o);
507 // Each transaction will automatically include a custom header of
508 // "X-Requested-With: XMLHttpRequest" to identify the request as
509 // having originated from Connection Manager.
510 if(this._use_default_xhr_header){
511 if(!this._default_headers['X-Requested-With']){
512 this.initHeader('X-Requested-With', this._default_xhr_header, true);
513 YAHOO.log('Initialize transaction header X-Request-Header to XMLHttpRequest.', 'info', 'Connection');
517 if(this._isFormSubmit || (postData && this._use_default_post_header)){
518 this.initHeader('Content-Type', this._default_post_header);
519 YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded for POST transaction.', 'info', 'Connection');
520 if(this._isFormSubmit){
521 this.resetFormState();
525 if(this._has_default_headers || this._has_http_headers){
526 this.setHeader(o);
529 this.handleReadyState(o, callback);
530 o.conn.send(postData || null);
532 // Fire global custom event -- startEvent
533 this.startEvent.fire(o);
535 if(o.startEvent){
536 // Fire transaction custom event -- startEvent
537 o.startEvent.fire(o);
540 return o;
545 * @description This method creates and subscribes custom events,
546 * specific to each transaction
547 * @method initCustomEvents
548 * @private
549 * @static
550 * @param {object} o The connection object
551 * @param {callback} callback The user-defined callback object
552 * @return {void}
554 initCustomEvents:function(o, callback)
556 // Enumerate through callback.customevents members and bind/subscribe
557 // events that match in the _customEvents table.
558 for(var prop in callback.customevents){
559 if(this._customEvents[prop][0]){
560 // Create the custom event
561 o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
562 YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' created.', 'info', 'Connection');
564 // Subscribe the custom event
565 o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
566 YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' subscribed.', 'info', 'Connection');
572 * @description This method serves as a timer that polls the XHR object's readyState
573 * property during a transaction, instead of binding a callback to the
574 * onreadystatechange event. Upon readyState 4, handleTransactionResponse
575 * will process the response, and the timer will be cleared.
576 * @method handleReadyState
577 * @private
578 * @static
579 * @param {object} o The connection object
580 * @param {callback} callback The user-defined callback object
581 * @return {void}
584 handleReadyState:function(o, callback)
587 var oConn = this;
589 if(callback && callback.timeout){
590 this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
593 this._poll[o.tId] = window.setInterval(
594 function(){
595 if(o.conn && o.conn.readyState === 4){
597 // Clear the polling interval for the transaction
598 // and remove the reference from _poll.
599 window.clearInterval(oConn._poll[o.tId]);
600 delete oConn._poll[o.tId];
602 if(callback && callback.timeout){
603 window.clearTimeout(oConn._timeOut[o.tId]);
604 delete oConn._timeOut[o.tId];
607 // Fire global custom event -- completeEvent
608 oConn.completeEvent.fire(o);
610 if(o.completeEvent){
611 // Fire transaction custom event -- completeEvent
612 o.completeEvent.fire(o);
615 oConn.handleTransactionResponse(o, callback);
618 ,this._polling_interval);
622 * @description This method attempts to interpret the server response and
623 * determine whether the transaction was successful, or if an error or
624 * exception was encountered.
625 * @method handleTransactionResponse
626 * @private
627 * @static
628 * @param {object} o The connection object
629 * @param {object} callback The user-defined callback object
630 * @param {boolean} isAbort Determines if the transaction was terminated via abort().
631 * @return {void}
633 handleTransactionResponse:function(o, callback, isAbort)
635 // If no valid callback is provided, then do not process any callback handling.
636 if(!callback){
637 this.releaseObject(o);
638 YAHOO.log('No callback object to process. Transaction complete.', 'info', 'Connection');
639 return;
642 var httpStatus, responseObject;
646 if(o.conn.status !== undefined && o.conn.status !== 0){
647 httpStatus = o.conn.status;
649 else{
650 httpStatus = 13030;
653 catch(e){
655 // 13030 is the custom code to indicate the condition -- in Mozilla/FF --
656 // when the o object's status and statusText properties are
657 // unavailable, and a query attempt throws an exception.
658 httpStatus = 13030;
661 if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
662 responseObject = this.createResponseObject(o, callback.argument);
663 if(callback.success){
664 if(!callback.scope){
665 callback.success(responseObject);
666 YAHOO.log('Success callback. HTTP code is ' + httpStatus, 'info', 'Connection');
668 else{
669 // If a scope property is defined, the callback will be fired from
670 // the context of the object.
671 callback.success.apply(callback.scope, [responseObject]);
672 YAHOO.log('Success callback with scope. HTTP code is ' + httpStatus, 'info', 'Connection');
676 // Fire global custom event -- successEvent
677 this.successEvent.fire(responseObject);
679 if(o.successEvent){
680 // Fire transaction custom event -- successEvent
681 o.successEvent.fire(responseObject);
684 else{
685 switch(httpStatus){
686 // The following cases are wininet.dll error codes that may be encountered.
687 case 12002: // Server timeout
688 case 12029: // 12029 to 12031 correspond to dropped connections.
689 case 12030:
690 case 12031:
691 case 12152: // Connection closed by server.
692 case 13030: // See above comments for variable status.
693 responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort?isAbort:false));
694 if(callback.failure){
695 if(!callback.scope){
696 callback.failure(responseObject);
697 YAHOO.log('Failure callback. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection');
699 else{
700 callback.failure.apply(callback.scope, [responseObject]);
701 YAHOO.log('Failure callback with scope. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection');
704 break;
705 default:
706 responseObject = this.createResponseObject(o, callback.argument);
707 if(callback.failure){
708 if(!callback.scope){
709 callback.failure(responseObject);
710 YAHOO.log('Failure callback. HTTP status code is ' + httpStatus, 'warn', 'Connection');
712 else{
713 callback.failure.apply(callback.scope, [responseObject]);
714 YAHOO.log('Failure callback with scope. HTTP status code is ' + httpStatus, 'warn', 'Connection');
719 // Fire global custom event -- failureEvent
720 this.failureEvent.fire(responseObject);
722 if(o.failureEvent){
723 // Fire transaction custom event -- failureEvent
724 o.failureEvent.fire(responseObject);
729 this.releaseObject(o);
730 responseObject = null;
734 * @description This method evaluates the server response, creates and returns the results via
735 * its properties. Success and failure cases will differ in the response
736 * object's property values.
737 * @method createResponseObject
738 * @private
739 * @static
740 * @param {object} o The connection object
741 * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
742 * @return {object}
744 createResponseObject:function(o, callbackArg)
746 var obj = {};
747 var headerObj = {};
751 var headerStr = o.conn.getAllResponseHeaders();
752 var header = headerStr.split('\n');
753 for(var i=0; i<header.length; i++){
754 var delimitPos = header[i].indexOf(':');
755 if(delimitPos != -1){
756 headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
760 catch(e){}
762 obj.tId = o.tId;
763 // Normalize IE's response to HTTP 204 when Win error 1223.
764 obj.status = (o.conn.status == 1223)?204:o.conn.status;
765 // Normalize IE's statusText to "No Content" instead of "Unknown".
766 obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
767 obj.getResponseHeader = headerObj;
768 obj.getAllResponseHeaders = headerStr;
769 obj.responseText = o.conn.responseText;
770 obj.responseXML = o.conn.responseXML;
772 if(typeof callbackArg !== undefined){
773 obj.argument = callbackArg;
776 return obj;
780 * @description If a transaction cannot be completed due to dropped or closed connections,
781 * there may be not be enough information to build a full response object.
782 * The failure callback will be fired and this specific condition can be identified
783 * by a status property value of 0.
785 * If an abort was successful, the status property will report a value of -1.
787 * @method createExceptionObject
788 * @private
789 * @static
790 * @param {int} tId The Transaction Id
791 * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
792 * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
793 * @return {object}
795 createExceptionObject:function(tId, callbackArg, isAbort)
797 var COMM_CODE = 0;
798 var COMM_ERROR = 'communication failure';
799 var ABORT_CODE = -1;
800 var ABORT_ERROR = 'transaction aborted';
802 var obj = {};
804 obj.tId = tId;
805 if(isAbort){
806 obj.status = ABORT_CODE;
807 obj.statusText = ABORT_ERROR;
809 else{
810 obj.status = COMM_CODE;
811 obj.statusText = COMM_ERROR;
814 if(callbackArg){
815 obj.argument = callbackArg;
818 return obj;
822 * @description Method that initializes the custom HTTP headers for the each transaction.
823 * @method initHeader
824 * @public
825 * @static
826 * @param {string} label The HTTP header label
827 * @param {string} value The HTTP header value
828 * @param {string} isDefault Determines if the specific header is a default header
829 * automatically sent with each transaction.
830 * @return {void}
832 initHeader:function(label,value,isDefault)
834 var headerObj = (isDefault)?this._default_headers:this._http_headers;
836 if(headerObj[label] === undefined){
837 headerObj[label] = value;
839 else{
840 // Concatenate multiple values, comma-delimited,
841 // for the same header label,
842 headerObj[label] = value + "," + headerObj[label];
845 if(isDefault){
846 this._has_default_headers = true;
848 else{
849 this._has_http_headers = true;
855 * @description Accessor that sets the HTTP headers for each transaction.
856 * @method setHeader
857 * @private
858 * @static
859 * @param {object} o The connection object for the transaction.
860 * @return {void}
862 setHeader:function(o)
864 if(this._has_default_headers){
865 for(var prop in this._default_headers){
866 if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
867 o.conn.setRequestHeader(prop, this._default_headers[prop]);
868 YAHOO.log('Default HTTP header ' + prop + ' set with value of ' + this._default_headers[prop], 'info', 'Connection');
873 if(this._has_http_headers){
874 for(var prop in this._http_headers){
875 if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
876 o.conn.setRequestHeader(prop, this._http_headers[prop]);
877 YAHOO.log('HTTP header ' + prop + ' set with value of ' + this._http_headers[prop], 'info', 'Connection');
880 delete this._http_headers;
882 this._http_headers = {};
883 this._has_http_headers = false;
888 * @description Resets the default HTTP headers object
889 * @method resetDefaultHeaders
890 * @public
891 * @static
892 * @return {void}
894 resetDefaultHeaders:function(){
895 delete this._default_headers;
896 this._default_headers = {};
897 this._has_default_headers = false;
901 * @description This method assembles the form label and value pairs and
902 * constructs an encoded string.
903 * asyncRequest() will automatically initialize the transaction with a
904 * a HTTP header Content-Type of application/x-www-form-urlencoded.
905 * @method setForm
906 * @public
907 * @static
908 * @param {string || object} form id or name attribute, or form object.
909 * @param {boolean} optional enable file upload.
910 * @param {boolean} optional enable file upload over SSL in IE only.
911 * @return {string} string of the HTML form field name and value pairs..
913 setForm:function(formId, isUpload, secureUri)
915 this.resetFormState();
916 var oForm;
917 if(typeof formId == 'string'){
918 // Determine if the argument is a form id or a form name.
919 // Note form name usage is deprecated by supported
920 // here for legacy reasons.
921 oForm = (document.getElementById(formId) || document.forms[formId]);
923 else if(typeof formId == 'object'){
924 // Treat argument as an HTML form object.
925 oForm = formId;
927 else{
928 YAHOO.log('Unable to create form object ' + formId, 'warn', 'Connection');
929 return;
932 // If the isUpload argument is true, setForm will call createFrame to initialize
933 // an iframe as the form target.
935 // The argument secureURI is also required by IE in SSL environments
936 // where the secureURI string is a fully qualified HTTP path, used to set the source
937 // of the iframe, to a stub resource in the same domain.
938 if(isUpload){
940 // Create iframe in preparation for file upload.
941 var io = this.createFrame(secureUri?secureUri:null);
942 // Set form reference and file upload properties to true.
943 this._isFormSubmit = true;
944 this._isFileUpload = true;
945 this._formNode = oForm;
947 return;
951 var oElement, oName, oValue, oDisabled;
952 var hasSubmit = false;
954 // Iterate over the form elements collection to construct the
955 // label-value pairs.
956 for (var i=0; i<oForm.elements.length; i++){
957 oElement = oForm.elements[i];
958 oDisabled = oForm.elements[i].disabled;
959 oName = oForm.elements[i].name;
960 oValue = oForm.elements[i].value;
962 // Do not submit fields that are disabled or
963 // do not have a name attribute value.
964 if(!oDisabled && oName)
966 switch(oElement.type)
968 case 'select-one':
969 case 'select-multiple':
970 for(var j=0; j<oElement.options.length; j++){
971 if(oElement.options[j].selected){
972 if(window.ActiveXObject){
973 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
975 else{
976 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
980 break;
981 case 'radio':
982 case 'checkbox':
983 if(oElement.checked){
984 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
986 break;
987 case 'file':
988 // stub case as XMLHttpRequest will only send the file path as a string.
989 case undefined:
990 // stub case for fieldset element which returns undefined.
991 case 'reset':
992 // stub case for input type reset button.
993 case 'button':
994 // stub case for input type button elements.
995 break;
996 case 'submit':
997 if(hasSubmit === false){
998 if(this._hasSubmitListener && this._submitElementValue){
999 this._sFormData += this._submitElementValue + '&';
1001 else{
1002 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1005 hasSubmit = true;
1007 break;
1008 default:
1009 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1014 this._isFormSubmit = true;
1015 this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
1017 YAHOO.log('Form initialized for transaction. HTML form POST message is: ' + this._sFormData, 'info', 'Connection');
1019 return this._sFormData;
1023 * @description Resets HTML form properties when an HTML form or HTML form
1024 * with file upload transaction is sent.
1025 * @method resetFormState
1026 * @private
1027 * @static
1028 * @return {void}
1030 resetFormState:function(){
1031 this._isFormSubmit = false;
1032 this._isFileUpload = false;
1033 this._formNode = null;
1034 this._sFormData = "";
1038 * @description Creates an iframe to be used for form file uploads. It is remove from the
1039 * document upon completion of the upload transaction.
1040 * @method createFrame
1041 * @private
1042 * @static
1043 * @param {string} optional qualified path of iframe resource for SSL in IE.
1044 * @return {void}
1046 createFrame:function(secureUri){
1048 // IE does not allow the setting of id and name attributes as object
1049 // properties via createElement(). A different iframe creation
1050 // pattern is required for IE.
1051 var frameId = 'yuiIO' + this._transaction_id;
1052 var io;
1053 if(window.ActiveXObject){
1054 io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
1056 // IE will throw a security exception in an SSL environment if the
1057 // iframe source is undefined.
1058 if(typeof secureUri == 'boolean'){
1059 io.src = 'javascript:false';
1061 else if(typeof secureURI == 'string'){
1062 // Deprecated
1063 io.src = secureUri;
1066 else{
1067 io = document.createElement('iframe');
1068 io.id = frameId;
1069 io.name = frameId;
1072 io.style.position = 'absolute';
1073 io.style.top = '-1000px';
1074 io.style.left = '-1000px';
1076 document.body.appendChild(io);
1077 YAHOO.log('File upload iframe created. Id is:' + frameId, 'info', 'Connection');
1081 * @description Parses the POST data and creates hidden form elements
1082 * for each key-value, and appends them to the HTML form object.
1083 * @method appendPostData
1084 * @private
1085 * @static
1086 * @param {string} postData The HTTP POST data
1087 * @return {array} formElements Collection of hidden fields.
1089 appendPostData:function(postData)
1091 var formElements = [];
1092 var postMessage = postData.split('&');
1093 for(var i=0; i < postMessage.length; i++){
1094 var delimitPos = postMessage[i].indexOf('=');
1095 if(delimitPos != -1){
1096 formElements[i] = document.createElement('input');
1097 formElements[i].type = 'hidden';
1098 formElements[i].name = postMessage[i].substring(0,delimitPos);
1099 formElements[i].value = postMessage[i].substring(delimitPos+1);
1100 this._formNode.appendChild(formElements[i]);
1104 return formElements;
1108 * @description Uploads HTML form, inclusive of files/attachments, using the
1109 * iframe created in createFrame to facilitate the transaction.
1110 * @method uploadFile
1111 * @private
1112 * @static
1113 * @param {int} id The transaction id.
1114 * @param {object} callback User-defined callback object.
1115 * @param {string} uri Fully qualified path of resource.
1116 * @param {string} postData POST data to be submitted in addition to HTML form.
1117 * @return {void}
1119 uploadFile:function(o, callback, uri, postData){
1121 // Each iframe has an id prefix of "yuiIO" followed
1122 // by the unique transaction id.
1123 var frameId = 'yuiIO' + o.tId;
1124 var uploadEncoding = 'multipart/form-data';
1125 var io = document.getElementById(frameId);
1126 var oConn = this;
1128 // Track original HTML form attribute values.
1129 var rawFormAttributes =
1131 action:this._formNode.getAttribute('action'),
1132 method:this._formNode.getAttribute('method'),
1133 target:this._formNode.getAttribute('target')
1136 // Initialize the HTML form properties in case they are
1137 // not defined in the HTML form.
1138 this._formNode.setAttribute('action', uri);
1139 this._formNode.setAttribute('method', 'POST');
1140 this._formNode.setAttribute('target', frameId);
1142 if(this._formNode.encoding){
1143 // IE does not respect property enctype for HTML forms.
1144 // Instead it uses the property - "encoding".
1145 this._formNode.setAttribute('encoding', uploadEncoding);
1147 else{
1148 this._formNode.setAttribute('enctype', uploadEncoding);
1151 if(postData){
1152 var oElements = this.appendPostData(postData);
1155 // Start file upload.
1156 this._formNode.submit();
1158 // Fire global custom event -- startEvent
1159 this.startEvent.fire(o);
1161 if(o.startEvent){
1162 // Fire transaction custom event -- startEvent
1163 o.startEvent.fire(o);
1166 // Start polling if a callback is present and the timeout
1167 // property has been defined.
1168 if(callback && callback.timeout){
1169 this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
1172 // Remove HTML elements created by appendPostData
1173 if(oElements && oElements.length > 0){
1174 for(var i=0; i < oElements.length; i++){
1175 this._formNode.removeChild(oElements[i]);
1179 // Restore HTML form attributes to their original
1180 // values prior to file upload.
1181 for(var prop in rawFormAttributes){
1182 if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
1183 if(rawFormAttributes[prop]){
1184 this._formNode.setAttribute(prop, rawFormAttributes[prop]);
1186 else{
1187 this._formNode.removeAttribute(prop);
1192 // Reset HTML form state properties.
1193 this.resetFormState();
1195 // Create the upload callback handler that fires when the iframe
1196 // receives the load event. Subsequently, the event handler is detached
1197 // and the iframe removed from the document.
1198 var uploadCallback = function()
1200 if(callback && callback.timeout){
1201 window.clearTimeout(oConn._timeOut[o.tId]);
1202 delete oConn._timeOut[o.tId];
1205 // Fire global custom event -- completeEvent
1206 oConn.completeEvent.fire(o);
1208 if(o.completeEvent){
1209 // Fire transaction custom event -- completeEvent
1210 o.completeEvent.fire(o);
1213 var obj = {};
1214 obj.tId = o.tId;
1215 obj.argument = callback.argument;
1219 // responseText and responseXML will be populated with the same data from the iframe.
1220 // Since the HTTP headers cannot be read from the iframe
1221 obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
1222 obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
1224 catch(e){}
1226 if(callback && callback.upload){
1227 if(!callback.scope){
1228 callback.upload(obj);
1229 YAHOO.log('Upload callback.', 'info', 'Connection');
1231 else{
1232 callback.upload.apply(callback.scope, [obj]);
1233 YAHOO.log('Upload callback with scope.', 'info', 'Connection');
1237 // Fire global custom event -- completeEvent
1238 oConn.uploadEvent.fire(obj);
1240 if(o.uploadEvent){
1241 // Fire transaction custom event -- completeEvent
1242 o.uploadEvent.fire(obj);
1245 if(YAHOO.util.Event){
1246 YAHOO.util.Event.removeListener(io, "load", uploadCallback);
1248 else if(window.detachEvent){
1249 io.detachEvent('onload', uploadCallback);
1251 else{
1252 io.removeEventListener('load', uploadCallback, false);
1254 setTimeout(
1255 function(){
1256 document.body.removeChild(io);
1257 oConn.releaseObject(o);
1258 YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
1259 }, 100);
1262 // Bind the onload handler to the iframe to detect the file upload response.
1263 if(YAHOO.util.Event){
1264 YAHOO.util.Event.addListener(io, "load", uploadCallback);
1266 else if(window.attachEvent){
1267 io.attachEvent('onload', uploadCallback);
1269 else{
1270 io.addEventListener('load', uploadCallback, false);
1275 * @description Method to terminate a transaction, if it has not reached readyState 4.
1276 * @method abort
1277 * @public
1278 * @static
1279 * @param {object} o The connection object returned by asyncRequest.
1280 * @param {object} callback User-defined callback object.
1281 * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
1282 * @return {boolean}
1284 abort:function(o, callback, isTimeout)
1286 var abortStatus;
1288 if(o.conn){
1289 if(this.isCallInProgress(o)){
1290 // Issue abort request
1291 o.conn.abort();
1293 window.clearInterval(this._poll[o.tId]);
1294 delete this._poll[o.tId];
1296 if(isTimeout){
1297 window.clearTimeout(this._timeOut[o.tId]);
1298 delete this._timeOut[o.tId];
1301 abortStatus = true;
1304 else if(o.isUpload === true){
1305 var frameId = 'yuiIO' + o.tId;
1306 var io = document.getElementById(frameId);
1308 if(io){
1309 // Destroy the iframe facilitating the transaction.
1310 document.body.removeChild(io);
1311 YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
1313 if(isTimeout){
1314 window.clearTimeout(this._timeOut[o.tId]);
1315 delete this._timeOut[o.tId];
1318 abortStatus = true;
1321 else{
1322 abortStatus = false;
1325 if(abortStatus === true){
1326 // Fire global custom event -- abortEvent
1327 this.abortEvent.fire(o);
1329 if(o.abortEvent){
1330 // Fire transaction custom event -- abortEvent
1331 o.abortEvent.fire(o);
1334 this.handleTransactionResponse(o, callback, true);
1335 YAHOO.log('Transaction ' + o.tId + ' aborted.', 'info', 'Connection');
1337 else{
1338 YAHOO.log('Transaction ' + o.tId + ' abort call failed. Connection object no longer exists.', 'warn', 'Connection');
1341 return abortStatus;
1345 * Public method to check if the transaction is still being processed.
1347 * @method isCallInProgress
1348 * @public
1349 * @static
1350 * @param {object} o The connection object returned by asyncRequest
1351 * @return {boolean}
1353 isCallInProgress:function(o)
1355 // if the XHR object assigned to the transaction has not been dereferenced,
1356 // then check its readyState status. Otherwise, return false.
1357 if(o && o.conn){
1358 return o.conn.readyState !== 4 && o.conn.readyState !== 0;
1360 else if(o && o.isUpload === true){
1361 var frameId = 'yuiIO' + o.tId;
1362 return document.getElementById(frameId)?true:false;
1364 else{
1365 return false;
1370 * @description Dereference the XHR instance and the connection object after the transaction is completed.
1371 * @method releaseObject
1372 * @private
1373 * @static
1374 * @param {object} o The connection object
1375 * @return {void}
1377 releaseObject:function(o)
1379 //dereference the XHR instance.
1380 if(o.conn){
1381 o.conn = null;
1383 YAHOO.log('Connection object for transaction ' + o.tId + ' destroyed.', 'info', 'Connection');
1384 //dereference the connection object.
1385 o = null;
1389 YAHOO.register("connection", YAHOO.util.Connect, {version: "2.3.0", build: "442"});