# Yahoo! UI Library

### connection 2.8.2r1

Yahoo! UI Library > connection > connectionx.js (source view)
Search:
Filters Show Private Show Protected Show Deprecated

    /**
      * @for Connect
      */
    (function(){
    	var YCM = YAHOO.util.Connect,
    		YE = YAHOO.util.Event;
       /**
    	* @description Property modified by setForm() to determine if the data
    	* should be submitted as an HTML form.
    	* @property _isFormSubmit
    	* @private
    	* @static
    	* @type boolean
    	*/
    	YCM._isFormSubmit = false;

       /**
    	* @description Property modified by setForm() to determine if a file(s)
    	* upload is expected.
    	* @property _isFileUpload
    	* @private
    	* @static
    	* @type boolean
    	*/
    	YCM._isFileUpload = false;

       /**
    	* @description Property modified by setForm() to set a reference to the HTML
    	* form node if the desired action is file upload.
    	* @property _formNode
    	* @private
    	* @static
    	* @type object
    	*/
    	YCM._formNode = null;

       /**
    	* @description Property modified by setForm() to set the HTML form data
    	* for each transaction.
    	* @property _sFormData
    	* @private
    	* @static
    	* @type string
    	*/
    	YCM._sFormData = null;

       /**
    	* @description Tracks the name-value pair of the "clicked" submit button if multiple submit
    	* buttons are present in an HTML form; and, if YAHOO.util.Event is available.
    	* @property _submitElementValue
    	* @private
    	* @static
    	* @type string
    	*/
    	YCM._submitElementValue = null;

       /**
        * @description Custom event that fires when handleTransactionResponse() determines a
        * response in the HTTP 4xx/5xx range.
        * @property failureEvent
        * @private
        * @static
        * @type CustomEvent
        */
    	YCM.uploadEvent = new YAHOO.util.CustomEvent('upload'),

       /**
    	* @description Determines whether YAHOO.util.Event is available and returns true or false.
    	* If true, an event listener is bound at the document level to trap click events that
    	* resolve to a target type of "Submit".  This listener will enable setForm() to determine
    	* the clicked "Submit" value in a multi-Submit button, HTML form.
    	* @property _hasSubmitListener
    	* @private
    	* @static
    	*/
    	YCM._hasSubmitListener = function() {
    		if(YE){
    			YE.addListener(
    				document,
    				'click',
    				function(e){
    					var obj = YE.getTarget(e),
    						name = obj.nodeName.toLowerCase();

    					if((name === 'input' || name === 'button') && (obj.type && obj.type.toLowerCase() == 'submit')){
    						YCM._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
    					}
    				});
    			return true;
    		}
    		return false;
    	}();

      /**
       * @description This method assembles the form label and value pairs and
       * constructs an encoded string.
       * asyncRequest() will automatically initialize the transaction with a
       * a HTTP header Content-Type of application/x-www-form-urlencoded.
       * @method setForm
       * @public
       * @static
       * @param {string || object} form id or name attribute, or form object.
       * @param {boolean} optional enable file upload.
       * @param {boolean} optional enable file upload over SSL in IE only.
       * @return {string} string of the HTML form field name and value pairs..
       */
    	function _setForm(formId, isUpload, secureUri)
    	{
    		var oForm, oElement, oName, oValue, oDisabled,
    			hasSubmit = false,
    			data = [], item = 0,
    			i,len,j,jlen,opt;

    		this.resetFormState();

    		if(typeof formId == 'string'){
    			// Determine if the argument is a form id or a form name.
    			// Note form name usage is deprecated by supported
    			// here for legacy reasons.
    			oForm = (document.getElementById(formId) || document.forms[formId]);
    		}
    		else if(typeof formId == 'object'){
    			// Treat argument as an HTML form object.
    			oForm = formId;
    		}
    		else{
    			YAHOO.log('Unable to create form object ' + formId, 'warn', 'Connection');
    			return;
    		}

    		// If the isUpload argument is true, setForm will call createFrame to initialize
    		// an iframe as the form target.
    		//
    		// The argument secureURI is also required by IE in SSL environments
    		// where the secureURI string is a fully qualified HTTP path, used to set the source
    		// of the iframe, to a stub resource in the same domain.
    		if(isUpload){

    			// Create iframe in preparation for file upload.
    			this.createFrame(secureUri?secureUri:null);

    			// Set form reference and file upload properties to true.
    			this._isFormSubmit = true;
    			this._isFileUpload = true;
    			this._formNode = oForm;

    			return;
    		}

    		// Iterate over the form elements collection to construct the
    		// label-value pairs.
    		for (i=0,len=oForm.elements.length; i<len; ++i){
    			oElement  = oForm.elements[i];
    			oDisabled = oElement.disabled;
    			oName     = oElement.name;

    			// Do not submit fields that are disabled or
    			// do not have a name attribute value.
    			if(!oDisabled && oName)
    			{
    				oName  = encodeURIComponent(oName)+'=';
    				oValue = encodeURIComponent(oElement.value);

    				switch(oElement.type)
    				{
    					// Safari, Opera, FF all default opt.value from .text if
    					// value attribute not specified in markup
    					case 'select-one':
    						if (oElement.selectedIndex > -1) {
    							opt = oElement.options[oElement.selectedIndex];
    							data[item++] = oName + encodeURIComponent(
    								(opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
    						}
    						break;
    					case 'select-multiple':
    						if (oElement.selectedIndex > -1) {
    							for(j=oElement.selectedIndex, jlen=oElement.options.length; j<jlen; ++j){
    								opt = oElement.options[j];
    								if (opt.selected) {
    									data[item++] = oName + encodeURIComponent(
    										(opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
    								}
    							}
    						}
    						break;
    					case 'radio':
    					case 'checkbox':
    						if(oElement.checked){
    							data[item++] = oName + oValue;
    						}
    						break;
    					case 'file':
    						// stub case as XMLHttpRequest will only send the file path as a string.
    					case undefined:
    						// stub case for fieldset element which returns undefined.
    					case 'reset':
    						// stub case for input type reset button.
    					case 'button':
    						// stub case for input type button elements.
    						break;
    					case 'submit':
    						if(hasSubmit === false){
    							if(this._hasSubmitListener && this._submitElementValue){
    								data[item++] = this._submitElementValue;
    							}
    							hasSubmit = true;
    						}
    						break;
    					default:
    						data[item++] = oName + oValue;
    				}
    			}
    		}

    		this._isFormSubmit = true;
    		this._sFormData = data.join('&');

    		YAHOO.log('Form initialized for transaction. HTML form POST message is: ' + this._sFormData, 'info', 'Connection');

    		this.initHeader('Content-Type', this._default_form_header);
    		YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded for setForm() transaction.', 'info', 'Connection');

    		return this._sFormData;
    	}

       /**
        * @description Resets HTML form properties when an HTML form or HTML form
        * with file upload transaction is sent.
        * @method resetFormState
        * @private
        * @static
        * @return {void}
        */
    	function _resetFormState(){
    		this._isFormSubmit = false;
    		this._isFileUpload = false;
    		this._formNode = null;
    		this._sFormData = "";
    	}

       /**
        * @description Creates an iframe to be used for form file uploads.  It is remove from the
        * document upon completion of the upload transaction.
        * @method createFrame
        * @private
        * @static
        * @param {string} optional qualified path of iframe resource for SSL in IE.
        * @return {void}
        */
    	function _createFrame(secureUri){

    		// IE does not allow the setting of id and name attributes as object
    		// properties via createElement().  A different iframe creation
    		// pattern is required for IE.
    		var frameId = 'yuiIO' + this._transaction_id,
    			io;
    		if(YAHOO.env.ua.ie){
    			io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');

    			// IE will throw a security exception in an SSL environment if the
    			// iframe source is undefined.
    			if(typeof secureUri == 'boolean'){
    				io.src = 'javascript:false';
    			}
    		}
    		else{
    			io = document.createElement('iframe');
    			io.id = frameId;
    			io.name = frameId;
    		}

    		io.style.position = 'absolute';
    		io.style.top = '-1000px';
    		io.style.left = '-1000px';

    		document.body.appendChild(io);
    		YAHOO.log('File upload iframe created. Id is:' + frameId, 'info', 'Connection');
    	}

       /**
        * @description Parses the POST data and creates hidden form elements
        * for each key-value, and appends them to the HTML form object.
        * @method appendPostData
        * @private
        * @static
        * @param {string} postData The HTTP POST data
        * @return {array} formElements Collection of hidden fields.
        */
    	function _appendPostData(postData){
    		var formElements = [],
    			postMessage = postData.split('&'),
    			i, delimitPos;

    		for(i=0; i < postMessage.length; i++){
    			delimitPos = postMessage[i].indexOf('=');
    			if(delimitPos != -1){
    				formElements[i] = document.createElement('input');
    				formElements[i].type = 'hidden';
    				formElements[i].name = decodeURIComponent(postMessage[i].substring(0,delimitPos));
    				formElements[i].value = decodeURIComponent(postMessage[i].substring(delimitPos+1));
    				this._formNode.appendChild(formElements[i]);
    			}
    		}

    		return formElements;
    	}

       /**
        * @description Uploads HTML form, inclusive of files/attachments, using the
        * iframe created in createFrame to facilitate the transaction.
        * @method uploadFile
        * @private
        * @static
        * @param {int} id The transaction id.
        * @param {object} callback User-defined callback object.
        * @param {string} uri Fully qualified path of resource.
        * @param {string} postData POST data to be submitted in addition to HTML form.
        * @return {void}
        */
    	function _uploadFile(o, callback, uri, postData){
    		// Each iframe has an id prefix of "yuiIO" followed
    		// by the unique transaction id.
    		var frameId = 'yuiIO' + o.tId,
    		    uploadEncoding = 'multipart/form-data',
    		    io = document.getElementById(frameId),
    		    ie8 = (document.documentMode && document.documentMode === 8) ? true : false,
    		    oConn = this,
    			args = (callback && callback.argument)?callback.argument:null,
                oElements,i,prop,obj, rawFormAttributes, uploadCallback;

    		// Track original HTML form attribute values.
    		rawFormAttributes = {
    			action:this._formNode.getAttribute('action'),
    			method:this._formNode.getAttribute('method'),
    			target:this._formNode.getAttribute('target')
    		};

    		// Initialize the HTML form properties in case they are
    		// not defined in the HTML form.
    		this._formNode.setAttribute('action', uri);
    		this._formNode.setAttribute('method', 'POST');
    		this._formNode.setAttribute('target', frameId);

    		if(YAHOO.env.ua.ie && !ie8){
    			// IE does not respect property enctype for HTML forms.
    			// Instead it uses the property - "encoding".
    			this._formNode.setAttribute('encoding', uploadEncoding);
    		}
    		else{
    			this._formNode.setAttribute('enctype', uploadEncoding);
    		}

    		if(postData){
    			oElements = this.appendPostData(postData);
    		}

    		// Start file upload.
    		this._formNode.submit();

    		// Fire global custom event -- startEvent
    		this.startEvent.fire(o, args);

    		if(o.startEvent){
    			// Fire transaction custom event -- startEvent
    			o.startEvent.fire(o, args);
    		}

    		// Start polling if a callback is present and the timeout
    		// property has been defined.
    		if(callback && callback.timeout){
    			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
    		}

    		// Remove HTML elements created by appendPostData
    		if(oElements && oElements.length > 0){
    			for(i=0; i < oElements.length; i++){
    				this._formNode.removeChild(oElements[i]);
    			}
    		}

    		// Restore HTML form attributes to their original
    		// values prior to file upload.
    		for(prop in rawFormAttributes){
    			if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
    				if(rawFormAttributes[prop]){
    					this._formNode.setAttribute(prop, rawFormAttributes[prop]);
    				}
    				else{
    					this._formNode.removeAttribute(prop);
    				}
    			}
    		}

    		// Reset HTML form state properties.
    		this.resetFormState();

    		// Create the upload callback handler that fires when the iframe
    		// receives the load event.  Subsequently, the event handler is detached
    		// and the iframe removed from the document.
    		uploadCallback = function() {
    			if(callback && callback.timeout){
    				window.clearTimeout(oConn._timeOut[o.tId]);
    				delete oConn._timeOut[o.tId];
    			}

    			// Fire global custom event -- completeEvent
    			oConn.completeEvent.fire(o, args);

    			if(o.completeEvent){
    				// Fire transaction custom event -- completeEvent
    				o.completeEvent.fire(o, args);
    			}

    			obj = {
    			    tId : o.tId,
    			    argument : callback.argument
                };

    			try
    			{
    				// responseText and responseXML will be populated with the same data from the iframe.
    				// Since the HTTP headers cannot be read from the iframe
    				obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
    				obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
    			}
    			catch(e){}

    			if(callback && callback.upload){
    				if(!callback.scope){
    					callback.upload(obj);
    					YAHOO.log('Upload callback.', 'info', 'Connection');
    				}
    				else{
    					callback.upload.apply(callback.scope, [obj]);
    					YAHOO.log('Upload callback with scope.', 'info', 'Connection');
    				}
    			}

    			// Fire global custom event -- uploadEvent
    			oConn.uploadEvent.fire(obj);

    			if(o.uploadEvent){
    				// Fire transaction custom event -- uploadEvent
    				o.uploadEvent.fire(obj);
    			}

    			YE.removeListener(io, "load", uploadCallback);

    			setTimeout(
    				function(){
    					document.body.removeChild(io);
    					oConn.releaseObject(o);
    					YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
    				}, 100);
    		};

    		// Bind the onload handler to the iframe to detect the file upload response.
    		YE.addListener(io, "load", uploadCallback);
    	}

    	YCM.setForm = _setForm;
    	YCM.resetFormState = _resetFormState;
    	YCM.createFrame = _createFrame;
    	YCM.appendPostData = _appendPostData;
    	YCM.uploadFile = _uploadFile;
    })();

Copyright © 2010 Yahoo! Inc. All rights reserved.