2 Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
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
20 * The Connection Manager singleton provides methods for creating and managing
21 * asynchronous transactions.
29 * @description Array of MSFT ActiveX ids for XMLHttpRequest.
30 * @property _msxml_progid
42 * @description Object literal of HTTP header(s)
43 * @property _http_header
51 * @description Determines if HTTP headers are set.
52 * @property _has_http_headers
57 _has_http_headers:false,
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
64 * @property _use_default_post_header
69 _use_default_post_header:true,
72 * @description The default header used for POST transactions.
73 * @property _default_post_header
78 _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',
81 * @description The default header used for transactions involving the
83 * @property _default_form_header
88 _default_form_header:'application/x-www-form-urlencoded',
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
99 _use_default_xhr_header:true,
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
111 _default_xhr_header:'XMLHttpRequest',
114 * @description Determines if custom, default headers
115 * are set for each transaction.
116 * @property _has_default_header
121 _has_default_headers:true,
124 * @description Determines if custom, default headers
125 * are set for each transaction.
126 * @property _has_default_header
134 * @description Property modified by setForm() to determine if the data
135 * should be submitted as an HTML form.
136 * @property _isFormSubmit
144 * @description Property modified by setForm() to determine if a file(s)
145 * upload is expected.
146 * @property _isFileUpload
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
164 * @description Property modified by setForm() to set the HTML form data
165 * for each transaction.
166 * @property _sFormData
174 * @description Collection of polling references to the polling mechanism in handleReadyState.
183 * @description Queue of timeout values for each transaction callback with a defined timeout value.
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
200 _polling_interval:50,
203 * @description A transaction counter that increments the transaction id for each transaction.
204 * @property _transaction_id
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
219 _submitElementValue:null,
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
230 _hasSubmitListener:(function()
232 if(YAHOO.util.Event){
233 YAHOO.util.Event.addListener(
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);
248 * @description Custom event that fires at the start of a transaction
249 * @property startEvent
254 startEvent: new YAHOO.util.CustomEvent('start'),
257 * @description Custom event that fires when a transaction response has completed.
258 * @property completeEvent
263 completeEvent: new YAHOO.util.CustomEvent('complete'),
266 * @description Custom event that fires when handleTransactionResponse() determines a
267 * response in the HTTP 2xx range.
268 * @property successEvent
273 successEvent: new YAHOO.util.CustomEvent('success'),
276 * @description Custom event that fires when handleTransactionResponse() determines a
277 * response in the HTTP 4xx/5xx range.
278 * @property failureEvent
283 failureEvent: new YAHOO.util.CustomEvent('failure'),
286 * @description Custom event that fires when handleTransactionResponse() determines a
287 * response in the HTTP 4xx/5xx range.
288 * @property failureEvent
293 uploadEvent: new YAHOO.util.CustomEvent('upload'),
296 * @description Custom event that fires when a transaction is successfully aborted.
297 * @property abortEvent
302 abortEvent: new YAHOO.util.CustomEvent('abort'),
305 * @description A reference table that maps callback custom events members to its specific
307 * @property _customEvents
314 onStart:['startEvent', 'start'],
315 onComplete:['completeEvent', 'complete'],
316 onSuccess:['successEvent', 'success'],
317 onFailure:['failureEvent', 'failure'],
318 onUpload:['uploadEvent', 'upload'],
319 onAbort:['abortEvent', 'abort']
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.
329 * @param {string} id The ActiveX id to be added to initialize the XHR object.
332 setProgId:function(id)
334 this._msxml_progid.unshift(id);
338 * @description Member to override the default POST header.
339 * @method setDefaultPostHeader
342 * @param {boolean} b Set and use default header - true or false .
345 setDefaultPostHeader:function(b)
347 if(typeof b == 'string'){
348 this._default_post_header = b;
350 else if(typeof b == 'boolean'){
351 this._use_default_post_header = b;
356 * @description Member to override the default transaction header..
357 * @method setDefaultXhrHeader
360 * @param {boolean} b Set and use default header - true or false .
363 setDefaultXhrHeader:function(b)
365 if(typeof b == 'string'){
366 this._default_xhr_header = b;
369 this._use_default_xhr_header = b;
374 * @description Member to modify the default polling interval.
375 * @method setPollingInterval
378 * @param {int} i The polling interval in milliseconds.
381 setPollingInterval:function(i)
383 if(typeof i == 'number' && isFinite(i)){
384 this._polling_interval = i;
389 * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
390 * the XMLHttpRequest instance and the transaction id.
391 * @method createXhrObject
394 * @param {int} transactionId Property containing the transaction id for this transaction.
397 createXhrObject:function(transactionId)
402 // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
403 http = new XMLHttpRequest();
404 // Object literal with http and tId properties
405 obj = { conn:http, tId:transactionId };
409 for(var i=0; i<this._msxml_progid.length; ++i){
412 // Instantiates XMLHttpRequest for IE and assign to http
413 http = new ActiveXObject(this._msxml_progid[i]);
414 // Object literal with conn and tId properties
415 obj = { conn:http, tId:transactionId };
428 * @description This method is called by asyncRequest to create a
429 * valid connection object for the transaction. It also passes a
430 * transaction id and increments the transaction id counter.
431 * @method getConnectionObject
436 getConnectionObject:function(isFileUpload)
439 var tId = this._transaction_id;
444 o = this.createXhrObject(tId);
453 this._transaction_id++;
464 * @description Method for initiating an asynchronous request via the XHR object.
465 * @method asyncRequest
468 * @param {string} method HTTP transaction method
469 * @param {string} uri Fully qualified path of resource
470 * @param {callback} callback User-defined callback function or object
471 * @param {string} postData POST body
472 * @return {object} Returns the connection object
474 asyncRequest:function(method, uri, callback, postData)
476 var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
477 var args = (callback && callback.argument)?callback.argument:null;
484 // Intialize any transaction-specific custom events, if provided.
485 if(callback && callback.customevents){
486 this.initCustomEvents(o, callback);
489 if(this._isFormSubmit){
490 if(this._isFileUpload){
491 this.uploadFile(o, callback, uri, postData);
495 // If the specified HTTP method is GET, setForm() will return an
496 // encoded string that is concatenated to the uri to
497 // create a querystring.
498 if(method.toUpperCase() == 'GET'){
499 if(this._sFormData.length !== 0){
500 // If the URI already contains a querystring, append an ampersand
501 // and then concatenate _sFormData to the URI.
502 uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
505 else if(method.toUpperCase() == 'POST'){
506 // If POST data exist in addition to the HTML form data,
507 // it will be concatenated to the form data.
508 postData = postData?this._sFormData + "&" + postData:this._sFormData;
512 if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
513 // If callback.cache is defined and set to false, a
514 // timestamp value will be added to the querystring.
515 uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
518 o.conn.open(method, uri, true);
520 // Each transaction will automatically include a custom header of
521 // "X-Requested-With: XMLHttpRequest" to identify the request as
522 // having originated from Connection Manager.
523 if(this._use_default_xhr_header){
524 if(!this._default_headers['X-Requested-With']){
525 this.initHeader('X-Requested-With', this._default_xhr_header, true);
529 //If the transaction method is POST and the POST header value is set to true
530 //or a custom value, initalize the Content-Type header to this value.
531 if((method.toUpperCase() == 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
532 this.initHeader('Content-Type', this._default_post_header);
535 //Initialize all default and custom HTTP headers,
536 if(this._has_default_headers || this._has_http_headers){
540 this.handleReadyState(o, callback);
541 o.conn.send(postData || '');
543 // Reset the HTML form data and state properties as
544 // soon as the data are submitted.
545 if(this._isFormSubmit === true){
546 this.resetFormState();
549 // Fire global custom event -- startEvent
550 this.startEvent.fire(o, args);
553 // Fire transaction custom event -- startEvent
554 o.startEvent.fire(o, args);
562 * @description This method creates and subscribes custom events,
563 * specific to each transaction
564 * @method initCustomEvents
567 * @param {object} o The connection object
568 * @param {callback} callback The user-defined callback object
571 initCustomEvents:function(o, callback)
573 // Enumerate through callback.customevents members and bind/subscribe
574 // events that match in the _customEvents table.
575 for(var prop in callback.customevents){
576 if(this._customEvents[prop][0]){
577 // Create the custom event
578 o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
580 // Subscribe the custom event
581 o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
587 * @description This method serves as a timer that polls the XHR object's readyState
588 * property during a transaction, instead of binding a callback to the
589 * onreadystatechange event. Upon readyState 4, handleTransactionResponse
590 * will process the response, and the timer will be cleared.
591 * @method handleReadyState
594 * @param {object} o The connection object
595 * @param {callback} callback The user-defined callback object
599 handleReadyState:function(o, callback)
603 var args = (callback && callback.argument)?callback.argument:null;
605 if(callback && callback.timeout){
606 this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
609 this._poll[o.tId] = window.setInterval(
611 if(o.conn && o.conn.readyState === 4){
613 // Clear the polling interval for the transaction
614 // and remove the reference from _poll.
615 window.clearInterval(oConn._poll[o.tId]);
616 delete oConn._poll[o.tId];
618 if(callback && callback.timeout){
619 window.clearTimeout(oConn._timeOut[o.tId]);
620 delete oConn._timeOut[o.tId];
623 // Fire global custom event -- completeEvent
624 oConn.completeEvent.fire(o, args);
627 // Fire transaction custom event -- completeEvent
628 o.completeEvent.fire(o, args);
631 oConn.handleTransactionResponse(o, callback);
634 ,this._polling_interval);
638 * @description This method attempts to interpret the server response and
639 * determine whether the transaction was successful, or if an error or
640 * exception was encountered.
641 * @method handleTransactionResponse
644 * @param {object} o The connection object
645 * @param {object} callback The user-defined callback object
646 * @param {boolean} isAbort Determines if the transaction was terminated via abort().
649 handleTransactionResponse:function(o, callback, isAbort)
651 var httpStatus, responseObject;
652 var args = (callback && callback.argument)?callback.argument:null;
656 if(o.conn.status !== undefined && o.conn.status !== 0){
657 httpStatus = o.conn.status;
665 // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
666 // when the XHR object's status and statusText properties are
667 // unavailable, and a query attempt throws an exception.
671 if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
672 responseObject = this.createResponseObject(o, args);
673 if(callback && callback.success){
675 callback.success(responseObject);
678 // If a scope property is defined, the callback will be fired from
679 // the context of the object.
680 callback.success.apply(callback.scope, [responseObject]);
684 // Fire global custom event -- successEvent
685 this.successEvent.fire(responseObject);
688 // Fire transaction custom event -- successEvent
689 o.successEvent.fire(responseObject);
694 // The following cases are wininet.dll error codes that may be encountered.
695 case 12002: // Server timeout
696 case 12029: // 12029 to 12031 correspond to dropped connections.
699 case 12152: // Connection closed by server.
700 case 13030: // See above comments for variable status.
701 responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
702 if(callback && callback.failure){
704 callback.failure(responseObject);
707 callback.failure.apply(callback.scope, [responseObject]);
713 responseObject = this.createResponseObject(o, args);
714 if(callback && callback.failure){
716 callback.failure(responseObject);
719 callback.failure.apply(callback.scope, [responseObject]);
724 // Fire global custom event -- failureEvent
725 this.failureEvent.fire(responseObject);
728 // Fire transaction custom event -- failureEvent
729 o.failureEvent.fire(responseObject);
734 this.releaseObject(o);
735 responseObject = null;
739 * @description This method evaluates the server response, creates and returns the results via
740 * its properties. Success and failure cases will differ in the response
741 * object's property values.
742 * @method createResponseObject
745 * @param {object} o The connection object
746 * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
749 createResponseObject:function(o, callbackArg)
756 var headerStr = o.conn.getAllResponseHeaders();
757 var header = headerStr.split('\n');
758 for(var i=0; i<header.length; i++){
759 var delimitPos = header[i].indexOf(':');
760 if(delimitPos != -1){
761 headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
768 // Normalize IE's response to HTTP 204 when Win error 1223.
769 obj.status = (o.conn.status == 1223)?204:o.conn.status;
770 // Normalize IE's statusText to "No Content" instead of "Unknown".
771 obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
772 obj.getResponseHeader = headerObj;
773 obj.getAllResponseHeaders = headerStr;
774 obj.responseText = o.conn.responseText;
775 obj.responseXML = o.conn.responseXML;
778 obj.argument = callbackArg;
785 * @description If a transaction cannot be completed due to dropped or closed connections,
786 * there may be not be enough information to build a full response object.
787 * The failure callback will be fired and this specific condition can be identified
788 * by a status property value of 0.
790 * If an abort was successful, the status property will report a value of -1.
792 * @method createExceptionObject
795 * @param {int} tId The Transaction Id
796 * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
797 * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
800 createExceptionObject:function(tId, callbackArg, isAbort)
803 var COMM_ERROR = 'communication failure';
805 var ABORT_ERROR = 'transaction aborted';
811 obj.status = ABORT_CODE;
812 obj.statusText = ABORT_ERROR;
815 obj.status = COMM_CODE;
816 obj.statusText = COMM_ERROR;
820 obj.argument = callbackArg;
827 * @description Method that initializes the custom HTTP headers for the each transaction.
831 * @param {string} label The HTTP header label
832 * @param {string} value The HTTP header value
833 * @param {string} isDefault Determines if the specific header is a default header
834 * automatically sent with each transaction.
837 initHeader:function(label, value, isDefault)
839 var headerObj = (isDefault)?this._default_headers:this._http_headers;
840 headerObj[label] = value;
843 this._has_default_headers = true;
846 this._has_http_headers = true;
852 * @description Accessor that sets the HTTP headers for each transaction.
856 * @param {object} o The connection object for the transaction.
859 setHeader:function(o)
861 if(this._has_default_headers){
862 for(var prop in this._default_headers){
863 if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
864 o.conn.setRequestHeader(prop, this._default_headers[prop]);
869 if(this._has_http_headers){
870 for(var prop in this._http_headers){
871 if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
872 o.conn.setRequestHeader(prop, this._http_headers[prop]);
875 delete this._http_headers;
877 this._http_headers = {};
878 this._has_http_headers = false;
883 * @description Resets the default HTTP headers object
884 * @method resetDefaultHeaders
889 resetDefaultHeaders:function(){
890 delete this._default_headers;
891 this._default_headers = {};
892 this._has_default_headers = false;
896 * @description This method assembles the form label and value pairs and
897 * constructs an encoded string.
898 * asyncRequest() will automatically initialize the transaction with a
899 * a HTTP header Content-Type of application/x-www-form-urlencoded.
903 * @param {string || object} form id or name attribute, or form object.
904 * @param {boolean} optional enable file upload.
905 * @param {boolean} optional enable file upload over SSL in IE only.
906 * @return {string} string of the HTML form field name and value pairs..
908 setForm:function(formId, isUpload, secureUri)
910 // reset the HTML form data and state properties
911 this.resetFormState();
914 if(typeof formId == 'string'){
915 // Determine if the argument is a form id or a form name.
916 // Note form name usage is deprecated, but supported
917 // here for backward compatibility.
918 oForm = (document.getElementById(formId) || document.forms[formId]);
920 else if(typeof formId == 'object'){
921 // Treat argument as an HTML form object.
928 // If the isUpload argument is true, setForm will call createFrame to initialize
929 // an iframe as the form target.
931 // The argument secureURI is also required by IE in SSL environments
932 // where the secureURI string is a fully qualified HTTP path, used to set the source
933 // of the iframe, to a stub resource in the same domain.
936 // Create iframe in preparation for file upload.
937 var io = this.createFrame((window.location.href.toLowerCase().indexOf("https") === 0 || secureUri)?true:false);
938 // Set form reference and file upload properties to true.
939 this._isFormSubmit = true;
940 this._isFileUpload = true;
941 this._formNode = oForm;
947 var oElement, oName, oValue, oDisabled;
948 var hasSubmit = false;
950 // Iterate over the form elements collection to construct the
951 // label-value pairs.
952 for (var i=0; i<oForm.elements.length; i++){
953 oElement = oForm.elements[i];
954 oDisabled = oElement.disabled;
955 oName = oElement.name;
956 oValue = oElement.value;
958 // Do not submit fields that are disabled or
959 // do not have a name attribute value.
960 if(!oDisabled && oName)
962 switch(oElement.type)
965 case 'select-multiple':
966 for(var j=0; j<oElement.options.length; j++){
967 if(oElement.options[j].selected){
968 if(window.ActiveXObject){
969 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
972 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
979 if(oElement.checked){
980 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
984 // stub case as XMLHttpRequest will only send the file path as a string.
986 // stub case for fieldset element which returns undefined.
988 // stub case for input type reset button.
990 // stub case for input type button elements.
993 if(hasSubmit === false){
994 if(this._hasSubmitListener && this._submitElementValue){
995 this._sFormData += this._submitElementValue + '&';
998 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1005 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1010 this._isFormSubmit = true;
1011 this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
1013 this.initHeader('Content-Type', this._default_form_header);
1015 return this._sFormData;
1019 * @description Resets HTML form properties when an HTML form or HTML form
1020 * with file upload transaction is sent.
1021 * @method resetFormState
1026 resetFormState:function(){
1027 this._isFormSubmit = false;
1028 this._isFileUpload = false;
1029 this._formNode = null;
1030 this._sFormData = "";
1034 * @description Creates an iframe to be used for form file uploads. It is remove from the
1035 * document upon completion of the upload transaction.
1036 * @method createFrame
1039 * @param {string} optional qualified path of iframe resource for SSL in IE.
1042 createFrame:function(secureUri){
1044 // IE does not allow the setting of id and name attributes as object
1045 // properties via createElement(). A different iframe creation
1046 // pattern is required for IE.
1047 var frameId = 'yuiIO' + this._transaction_id;
1049 if(window.ActiveXObject){
1050 io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
1052 // IE will throw a security exception in an SSL environment if the
1053 // iframe source is undefined.
1054 if(typeof secureUri == 'boolean'){
1055 io.src = 'javascript:false';
1059 io = document.createElement('iframe');
1064 io.style.position = 'absolute';
1065 io.style.top = '-1000px';
1066 io.style.left = '-1000px';
1068 document.body.appendChild(io);
1072 * @description Parses the POST data and creates hidden form elements
1073 * for each key-value, and appends them to the HTML form object.
1074 * @method appendPostData
1077 * @param {string} postData The HTTP POST data
1078 * @return {array} formElements Collection of hidden fields.
1080 appendPostData:function(postData)
1082 var formElements = [];
1083 var postMessage = postData.split('&');
1084 for(var i=0; i < postMessage.length; i++){
1085 var delimitPos = postMessage[i].indexOf('=');
1086 if(delimitPos != -1){
1087 formElements[i] = document.createElement('input');
1088 formElements[i].type = 'hidden';
1089 formElements[i].name = postMessage[i].substring(0,delimitPos);
1090 formElements[i].value = postMessage[i].substring(delimitPos+1);
1091 this._formNode.appendChild(formElements[i]);
1095 return formElements;
1099 * @description Uploads HTML form, inclusive of files/attachments, using the
1100 * iframe created in createFrame to facilitate the transaction.
1101 * @method uploadFile
1104 * @param {int} id The transaction id.
1105 * @param {object} callback User-defined callback object.
1106 * @param {string} uri Fully qualified path of resource.
1107 * @param {string} postData POST data to be submitted in addition to HTML form.
1110 uploadFile:function(o, callback, uri, postData){
1112 // Each iframe has an id prefix of "yuiIO" followed
1113 // by the unique transaction id.
1115 var frameId = 'yuiIO' + o.tId;
1116 var uploadEncoding = 'multipart/form-data';
1117 var io = document.getElementById(frameId);
1118 var args = (callback && callback.argument)?callback.argument:null;
1120 // Track original HTML form attribute values.
1121 var rawFormAttributes =
1123 action:this._formNode.getAttribute('action'),
1124 method:this._formNode.getAttribute('method'),
1125 target:this._formNode.getAttribute('target')
1128 // Initialize the HTML form properties in case they are
1129 // not defined in the HTML form.
1130 this._formNode.setAttribute('action', uri);
1131 this._formNode.setAttribute('method', 'POST');
1132 this._formNode.setAttribute('target', frameId);
1134 if(YAHOO.env.ua.ie){
1135 // IE does not respect property enctype for HTML forms.
1136 // Instead it uses the property - "encoding".
1137 this._formNode.setAttribute('encoding', uploadEncoding);
1140 this._formNode.setAttribute('enctype', uploadEncoding);
1144 var oElements = this.appendPostData(postData);
1147 // Start file upload.
1148 this._formNode.submit();
1150 // Fire global custom event -- startEvent
1151 this.startEvent.fire(o, args);
1154 // Fire transaction custom event -- startEvent
1155 o.startEvent.fire(o, args);
1158 // Start polling if a callback is present and the timeout
1159 // property has been defined.
1160 if(callback && callback.timeout){
1161 this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
1164 // Remove HTML elements created by appendPostData
1165 if(oElements && oElements.length > 0){
1166 for(var i=0; i < oElements.length; i++){
1167 this._formNode.removeChild(oElements[i]);
1171 // Restore HTML form attributes to their original
1172 // values prior to file upload.
1173 for(var prop in rawFormAttributes){
1174 if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
1175 if(rawFormAttributes[prop]){
1176 this._formNode.setAttribute(prop, rawFormAttributes[prop]);
1179 this._formNode.removeAttribute(prop);
1184 // Reset HTML form state properties.
1185 this.resetFormState();
1187 // Create the upload callback handler that fires when the iframe
1188 // receives the load event. Subsequently, the event handler is detached
1189 // and the iframe removed from the document.
1190 var uploadCallback = function()
1192 if(callback && callback.timeout){
1193 window.clearTimeout(oConn._timeOut[o.tId]);
1194 delete oConn._timeOut[o.tId];
1197 // Fire global custom event -- completeEvent
1198 oConn.completeEvent.fire(o, args);
1200 if(o.completeEvent){
1201 // Fire transaction custom event -- completeEvent
1202 o.completeEvent.fire(o, args);
1207 obj.argument = callback.argument;
1211 // responseText and responseXML will be populated with the same data from the iframe.
1212 // Since the HTTP headers cannot be read from the iframe
1213 obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
1214 obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
1218 if(callback && callback.upload){
1219 if(!callback.scope){
1220 callback.upload(obj);
1223 callback.upload.apply(callback.scope, [obj]);
1227 // Fire global custom event -- uploadEvent
1228 oConn.uploadEvent.fire(obj);
1231 // Fire transaction custom event -- uploadEvent
1232 o.uploadEvent.fire(obj);
1235 YAHOO.util.Event.removeListener(io, "load", uploadCallback);
1239 document.body.removeChild(io);
1240 oConn.releaseObject(o);
1244 // Bind the onload handler to the iframe to detect the file upload response.
1245 YAHOO.util.Event.addListener(io, "load", uploadCallback);
1249 * @description Method to terminate a transaction, if it has not reached readyState 4.
1253 * @param {object} o The connection object returned by asyncRequest.
1254 * @param {object} callback User-defined callback object.
1255 * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
1258 abort:function(o, callback, isTimeout)
1261 var args = (callback && callback.argument)?callback.argument:null;
1265 if(this.isCallInProgress(o)){
1266 // Issue abort request
1269 window.clearInterval(this._poll[o.tId]);
1270 delete this._poll[o.tId];
1273 window.clearTimeout(this._timeOut[o.tId]);
1274 delete this._timeOut[o.tId];
1280 else if(o && o.isUpload === true){
1281 var frameId = 'yuiIO' + o.tId;
1282 var io = document.getElementById(frameId);
1285 // Remove all listeners on the iframe prior to
1287 YAHOO.util.Event.removeListener(io, "load");
1288 // Destroy the iframe facilitating the transaction.
1289 document.body.removeChild(io);
1292 window.clearTimeout(this._timeOut[o.tId]);
1293 delete this._timeOut[o.tId];
1300 abortStatus = false;
1303 if(abortStatus === true){
1304 // Fire global custom event -- abortEvent
1305 this.abortEvent.fire(o, args);
1308 // Fire transaction custom event -- abortEvent
1309 o.abortEvent.fire(o, args);
1312 this.handleTransactionResponse(o, callback, true);
1319 * @description Determines if the transaction is still being processed.
1320 * @method isCallInProgress
1323 * @param {object} o The connection object returned by asyncRequest
1326 isCallInProgress:function(o)
1328 // if the XHR object assigned to the transaction has not been dereferenced,
1329 // then check its readyState status. Otherwise, return false.
1331 return o.conn.readyState !== 4 && o.conn.readyState !== 0;
1333 else if(o && o.isUpload === true){
1334 var frameId = 'yuiIO' + o.tId;
1335 return document.getElementById(frameId)?true:false;
1343 * @description Dereference the XHR instance and the connection object after the transaction is completed.
1344 * @method releaseObject
1347 * @param {object} o The connection object
1350 releaseObject:function(o)
1353 //dereference the XHR instance.
1356 //dereference the connection object.
1362 YAHOO.register("connection", YAHOO.util.Connect, {version: "2.5.2", build: "1076"});