/* Copyright (c) 2006, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 0.12.2 */ /** * The Connection Manager provides a simplified interface to the XMLHttpRequest * object. It handles cross-browser instantiantion of XMLHttpRequest, negotiates the * interactive states and server response, returning the results to a pre-defined * callback you create. * * @namespace YAHOO.util * @module connection * @requires yahoo */ /** * The Connection Manager singleton provides methods for creating and managing * asynchronous transactions. * * @class Connect */ YAHOO.util.Connect = { /** * @description Array of MSFT ActiveX ids for XMLHttpRequest. * @property _msxml_progid * @private * @static * @type array */ _msxml_progid:[ 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP' ], /** * @description Object literal of HTTP header(s) * @property _http_header * @private * @static * @type object */ _http_header:{}, /** * @description Determines if HTTP headers are set. * @property _has_http_headers * @private * @static * @type boolean */ _has_http_headers:false, /** * @description Determines if a default header of * Content-Type of 'application/x-www-form-urlencoded' * will be added to any client HTTP headers sent for POST * transactions. * @property _use_default_post_header * @private * @static * @type boolean */ _use_default_post_header:true, /** * @description Determines if a default header of * Content-Type of 'application/x-www-form-urlencoded' * will be added to any client HTTP headers sent for POST * transactions. * @property _default_post_header * @private * @static * @type boolean */ _default_post_header:'application/x-www-form-urlencoded', /** * @description Property modified by setForm() to determine if the data * should be submitted as an HTML form. * @property _isFormSubmit * @private * @static * @type boolean */ _isFormSubmit:false, /** * @description Property modified by setForm() to determine if a file(s) * upload is expected. * @property _isFileUpload * @private * @static * @type boolean */ _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 */ _formNode:null, /** * @description Property modified by setForm() to set the HTML form data * for each transaction. * @property _sFormData * @private * @static * @type string */ _sFormData:null, /** * @description Collection of polling references to the polling mechanism in handleReadyState. * @property _poll * @private * @static * @type object */ _poll:{}, /** * @description Queue of timeout values for each transaction callback with a defined timeout value. * @property _timeOut * @private * @static * @type object */ _timeOut:{}, /** * @description The polling frequency, in milliseconds, for HandleReadyState. * when attempting to determine a transaction's XHR readyState. * The default is 50 milliseconds. * @property _polling_interval * @private * @static * @type int */ _polling_interval:50, /** * @description A transaction counter that increments the transaction id for each transaction. * @property _transaction_id * @private * @static * @type int */ _transaction_id:0, /** * @description Member to add an ActiveX id to the existing xml_progid array. * In the event(unlikely) a new ActiveX id is introduced, it can be added * without internal code modifications. * @method setProgId * @public * @static * @param {string} id The ActiveX id to be added to initialize the XHR object. * @return void */ setProgId:function(id) { this._msxml_progid.unshift(id); }, /** * @description Member to enable or disable the default POST header. * @method setDefaultPostHeader * @public * @static * @param {boolean} b Set and use default header - true or false . * @return void */ setDefaultPostHeader:function(b) { this._use_default_post_header = b; }, /** * @description Member to modify the default polling interval. * @method setPollingInterval * @public * @static * @param {int} i The polling interval in milliseconds. * @return void */ setPollingInterval:function(i) { if(typeof i == 'number' && isFinite(i)){ this._polling_interval = i; } }, /** * @description Instantiates a XMLHttpRequest object and returns an object with two properties: * the XMLHttpRequest instance and the transaction id. * @method createXhrObject * @private * @static * @param {int} transactionId Property containing the transaction id for this transaction. * @return object */ createXhrObject:function(transactionId) { var obj,http; try { // Instantiates XMLHttpRequest in non-IE browsers and assigns to http. http = new XMLHttpRequest(); // Object literal with http and tId properties obj = { conn:http, tId:transactionId }; } catch(e) { for(var i=0; i= 200 && httpStatus < 300){ try { responseObject = this.createResponseObject(o, callback.argument); if(callback.success){ if(!callback.scope){ callback.success(responseObject); } else{ // If a scope property is defined, the callback will be fired from // the context of the object. callback.success.apply(callback.scope, [responseObject]); } } } catch(e){} } else{ try { switch(httpStatus){ // The following cases are wininet.dll error codes that may be encountered. case 12002: // Server timeout case 12029: // 12029 to 12031 correspond to dropped connections. case 12030: case 12031: case 12152: // Connection closed by server. case 13030: // See above comments for variable status. responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort?isAbort:false)); if(callback.failure){ if(!callback.scope){ callback.failure(responseObject); } else{ callback.failure.apply(callback.scope, [responseObject]); } } break; default: responseObject = this.createResponseObject(o, callback.argument); if(callback.failure){ if(!callback.scope){ callback.failure(responseObject); } else{ callback.failure.apply(callback.scope, [responseObject]); } } } } catch(e){} } this.releaseObject(o); responseObject = null; }, /** * @description This method evaluates the server response, creates and returns the results via * its properties. Success and failure cases will differ in the response * object's property values. * @method createResponseObject * @private * @static * @param {object} o The connection object * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback * @return {object} */ createResponseObject:function(o, callbackArg) { var obj = {}; var headerObj = {}; try { var headerStr = o.conn.getAllResponseHeaders(); var header = headerStr.split('\n'); for(var i=0; i'); // 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 if(typeof secureURI == 'string'){ // Deprecated io.src = secureUri; } } else{ var 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); }, /** * @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. */ appendPostData:function(postData) { var formElements = []; var postMessage = postData.split('&'); for(var i=0; i < postMessage.length; i++){ var delimitPos = postMessage[i].indexOf('='); if(delimitPos != -1){ formElements[i] = document.createElement('input'); formElements[i].type = 'hidden'; formElements[i].name = postMessage[i].substring(0,delimitPos); formElements[i].value = postMessage[i].substring(delimitPos+1); this._formNode.appendChild(formElements[i]); } } return formElements; }, /** * @description Uploads HTML form, including files/attachments, to the * iframe created in createFrame. * @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. * @return {void} */ uploadFile:function(id, callback, uri, postData){ // Each iframe has an id prefix of "yuiIO" followed // by the unique transaction id. var frameId = 'yuiIO' + id; var io = document.getElementById(frameId); // Initialize the HTML form properties in case they are // not defined in the HTML form. this._formNode.action = uri; this._formNode.method = 'POST'; this._formNode.target = frameId; if(this._formNode.encoding){ // IE does not respect property enctype for HTML forms. // Instead use property encoding. this._formNode.encoding = 'multipart/form-data'; } else{ this._formNode.enctype = 'multipart/form-data'; } if(postData){ var oElements = this.appendPostData(postData); } this._formNode.submit(); if(oElements && oElements.length > 0){ try { for(var i=0; i < oElements.length; i++){ this._formNode.removeChild(oElements[i]); } } catch(e){} } // Reset HTML form status 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. var uploadCallback = function() { var obj = {}; obj.tId = id; obj.argument = callback.argument; try { obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null; obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document; } catch(e){} if(callback.upload){ if(!callback.scope){ callback.upload(obj); } else{ callback.upload.apply(callback.scope, [obj]); } } if(YAHOO.util.Event){ YAHOO.util.Event.removeListener(io, "load", uploadCallback); } else if(window.detachEvent){ io.detachEvent('onload', uploadCallback); } else{ io.removeEventListener('load', uploadCallback, false); } setTimeout(function(){ document.body.removeChild(io); }, 100); }; // Bind the onload handler to the iframe to detect the file upload response. if(YAHOO.util.Event){ YAHOO.util.Event.addListener(io, "load", uploadCallback); } else if(window.attachEvent){ io.attachEvent('onload', uploadCallback); } else{ io.addEventListener('load', uploadCallback, false); } }, /** * @description Method to terminate a transaction, if it has not reached readyState 4. * @method abort * @public * @static * @param {object} o The connection object returned by asyncRequest. * @param {object} callback User-defined callback object. * @param {string} isTimeout boolean to indicate if abort was a timeout. * @return {boolean} */ abort:function(o, callback, isTimeout) { if(this.isCallInProgress(o)){ o.conn.abort(); window.clearInterval(this._poll[o.tId]); delete this._poll[o.tId]; if(isTimeout){ delete this._timeOut[o.tId]; } this.handleTransactionResponse(o, callback, true); return true; } else{ return false; } }, /** * Public method to check if the transaction is still being processed. * * @method isCallInProgress * @public * @static * @param {object} o The connection object returned by asyncRequest * @return {boolean} */ isCallInProgress:function(o) { // if the XHR object assigned to the transaction has not been dereferenced, // then check its readyState status. Otherwise, return false. if(o.conn){ return o.conn.readyState != 4 && o.conn.readyState != 0; } else{ //The XHR object has been destroyed. return false; } }, /** * @description Dereference the XHR instance and the connection object after the transaction is completed. * @method releaseObject * @private * @static * @param {object} o The connection object * @return {void} */ releaseObject:function(o) { //dereference the XHR instance. o.conn = null; //dereference the connection object. o = null; } };