PageRenderTime 56ms CodeModel.GetById 12ms app.highlight 35ms RepoModel.GetById 2ms app.codeStats 0ms

/hippo/src/main/webapp/yui/connection/connection.js

http://hdbc.googlecode.com/
JavaScript | 1376 lines | 650 code | 156 blank | 570 comment | 154 complexity | 6000c27aea72e3c980ce882a2b66e0bb MD5 | raw file
   1/*
   2Copyright (c) 2009, Yahoo! Inc. All rights reserved.
   3Code licensed under the BSD License:
   4http://developer.yahoo.net/yui/license.txt
   5version: 2.7.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.
  12 *
  13 * @namespace YAHOO.util
  14 * @module connection
  15 * @requires yahoo
  16 * @requires event
  17 */
  18
  19/**
  20 * The Connection Manager singleton provides methods for creating and managing
  21 * asynchronous transactions.
  22 *
  23 * @class Connect
  24 */
  25
  26YAHOO.util.Connect =
  27{
  28  /**
  29   * @description Array of MSFT ActiveX ids for XMLHttpRequest.
  30   * @property _msxml_progid
  31   * @private
  32   * @static
  33   * @type array
  34   */
  35	_msxml_progid:[
  36		'Microsoft.XMLHTTP',
  37		'MSXML2.XMLHTTP.3.0',
  38		'MSXML2.XMLHTTP'
  39		],
  40
  41  /**
  42   * @description Object literal of HTTP header(s)
  43   * @property _http_header
  44   * @private
  45   * @static
  46   * @type object
  47   */
  48	_http_headers:{},
  49
  50  /**
  51   * @description Determines if HTTP headers are set.
  52   * @property _has_http_headers
  53   * @private
  54   * @static
  55   * @type boolean
  56   */
  57	_has_http_headers:false,
  58
  59 /**
  60  * @description Determines if a default header of
  61  * Content-Type of 'application/x-www-form-urlencoded'
  62  * will be added to any client HTTP headers sent for POST
  63  * transactions.
  64  * @property _use_default_post_header
  65  * @private
  66  * @static
  67  * @type boolean
  68  */
  69    _use_default_post_header:true,
  70
  71 /**
  72  * @description The default header used for POST transactions.
  73  * @property _default_post_header
  74  * @private
  75  * @static
  76  * @type boolean
  77  */
  78    _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',
  79
  80 /**
  81  * @description The default header used for transactions involving the
  82  * use of HTML forms.
  83  * @property _default_form_header
  84  * @private
  85  * @static
  86  * @type boolean
  87  */
  88    _default_form_header:'application/x-www-form-urlencoded',
  89
  90 /**
  91  * @description Determines if a default header of
  92  * 'X-Requested-With: XMLHttpRequest'
  93  * will be added to each transaction.
  94  * @property _use_default_xhr_header
  95  * @private
  96  * @static
  97  * @type boolean
  98  */
  99    _use_default_xhr_header:true,
 100
 101 /**
 102  * @description The default header value for the label
 103  * "X-Requested-With".  This is sent with each
 104  * transaction, by default, to identify the
 105  * request as being made by YUI Connection Manager.
 106  * @property _default_xhr_header
 107  * @private
 108  * @static
 109  * @type boolean
 110  */
 111    _default_xhr_header:'XMLHttpRequest',
 112
 113 /**
 114  * @description Determines if custom, default headers
 115  * are set for each transaction.
 116  * @property _has_default_header
 117  * @private
 118  * @static
 119  * @type boolean
 120  */
 121    _has_default_headers:true,
 122
 123 /**
 124  * @description Determines if custom, default headers
 125  * are set for each transaction.
 126  * @property _has_default_header
 127  * @private
 128  * @static
 129  * @type boolean
 130  */
 131    _default_headers:{},
 132
 133 /**
 134  * @description Property modified by setForm() to determine if the data
 135  * should be submitted as an HTML form.
 136  * @property _isFormSubmit
 137  * @private
 138  * @static
 139  * @type boolean
 140  */
 141    _isFormSubmit:false,
 142
 143 /**
 144  * @description Property modified by setForm() to determine if a file(s)
 145  * upload is expected.
 146  * @property _isFileUpload
 147  * @private
 148  * @static
 149  * @type boolean
 150  */
 151    _isFileUpload:false,
 152
 153 /**
 154  * @description Property modified by setForm() to set a reference to the HTML
 155  * form node if the desired action is file upload.
 156  * @property _formNode
 157  * @private
 158  * @static
 159  * @type object
 160  */
 161    _formNode:null,
 162
 163 /**
 164  * @description Property modified by setForm() to set the HTML form data
 165  * for each transaction.
 166  * @property _sFormData
 167  * @private
 168  * @static
 169  * @type string
 170  */
 171    _sFormData:null,
 172
 173 /**
 174  * @description Collection of polling references to the polling mechanism in handleReadyState.
 175  * @property _poll
 176  * @private
 177  * @static
 178  * @type object
 179  */
 180    _poll:{},
 181
 182 /**
 183  * @description Queue of timeout values for each transaction callback with a defined timeout value.
 184  * @property _timeOut
 185  * @private
 186  * @static
 187  * @type object
 188  */
 189    _timeOut:{},
 190
 191  /**
 192   * @description The polling frequency, in milliseconds, for HandleReadyState.
 193   * when attempting to determine a transaction's XHR readyState.
 194   * The default is 50 milliseconds.
 195   * @property _polling_interval
 196   * @private
 197   * @static
 198   * @type int
 199   */
 200     _polling_interval:50,
 201
 202  /**
 203   * @description A transaction counter that increments the transaction id for each transaction.
 204   * @property _transaction_id
 205   * @private
 206   * @static
 207   * @type int
 208   */
 209     _transaction_id:0,
 210
 211  /**
 212   * @description Tracks the name-value pair of the "clicked" submit button if multiple submit
 213   * buttons are present in an HTML form; and, if YAHOO.util.Event is available.
 214   * @property _submitElementValue
 215   * @private
 216   * @static
 217   * @type string
 218   */
 219	 _submitElementValue:null,
 220
 221  /**
 222   * @description Determines whether YAHOO.util.Event is available and returns true or false.
 223   * If true, an event listener is bound at the document level to trap click events that
 224   * resolve to a target type of "Submit".  This listener will enable setForm() to determine
 225   * the clicked "Submit" value in a multi-Submit button, HTML form.
 226   * @property _hasSubmitListener
 227   * @private
 228   * @static
 229   */
 230	 _hasSubmitListener:(function()
 231	 {
 232		if(YAHOO.util.Event){
 233			YAHOO.util.Event.addListener(
 234				document,
 235				'click',
 236				function(e){
 237					var obj = YAHOO.util.Event.getTarget(e),
 238						name = obj.nodeName.toLowerCase();
 239					if((name === 'input' || name === 'button') && (obj.type && obj.type.toLowerCase() == 'submit')){
 240						YAHOO.util.Connect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
 241					}
 242				});
 243			return true;
 244	    }
 245	    return false;
 246	 })(),
 247
 248  /**
 249   * @description Custom event that fires at the start of a transaction
 250   * @property startEvent
 251   * @private
 252   * @static
 253   * @type CustomEvent
 254   */
 255	startEvent: new YAHOO.util.CustomEvent('start'),
 256
 257  /**
 258   * @description Custom event that fires when a transaction response has completed.
 259   * @property completeEvent
 260   * @private
 261   * @static
 262   * @type CustomEvent
 263   */
 264	completeEvent: new YAHOO.util.CustomEvent('complete'),
 265
 266  /**
 267   * @description Custom event that fires when handleTransactionResponse() determines a
 268   * response in the HTTP 2xx range.
 269   * @property successEvent
 270   * @private
 271   * @static
 272   * @type CustomEvent
 273   */
 274	successEvent: new YAHOO.util.CustomEvent('success'),
 275
 276  /**
 277   * @description Custom event that fires when handleTransactionResponse() determines a
 278   * response in the HTTP 4xx/5xx range.
 279   * @property failureEvent
 280   * @private
 281   * @static
 282   * @type CustomEvent
 283   */
 284	failureEvent: new YAHOO.util.CustomEvent('failure'),
 285
 286  /**
 287   * @description Custom event that fires when handleTransactionResponse() determines a
 288   * response in the HTTP 4xx/5xx range.
 289   * @property failureEvent
 290   * @private
 291   * @static
 292   * @type CustomEvent
 293   */
 294	uploadEvent: new YAHOO.util.CustomEvent('upload'),
 295
 296  /**
 297   * @description Custom event that fires when a transaction is successfully aborted.
 298   * @property abortEvent
 299   * @private
 300   * @static
 301   * @type CustomEvent
 302   */
 303	abortEvent: new YAHOO.util.CustomEvent('abort'),
 304
 305  /**
 306   * @description A reference table that maps callback custom events members to its specific
 307   * event name.
 308   * @property _customEvents
 309   * @private
 310   * @static
 311   * @type object
 312   */
 313	_customEvents:
 314	{
 315		onStart:['startEvent', 'start'],
 316		onComplete:['completeEvent', 'complete'],
 317		onSuccess:['successEvent', 'success'],
 318		onFailure:['failureEvent', 'failure'],
 319		onUpload:['uploadEvent', 'upload'],
 320		onAbort:['abortEvent', 'abort']
 321	},
 322
 323  /**
 324   * @description Member to add an ActiveX id to the existing xml_progid array.
 325   * In the event(unlikely) a new ActiveX id is introduced, it can be added
 326   * without internal code modifications.
 327   * @method setProgId
 328   * @public
 329   * @static
 330   * @param {string} id The ActiveX id to be added to initialize the XHR object.
 331   * @return void
 332   */
 333	setProgId:function(id)
 334	{
 335		this._msxml_progid.unshift(id);
 336	},
 337
 338  /**
 339   * @description Member to override the default POST header.
 340   * @method setDefaultPostHeader
 341   * @public
 342   * @static
 343   * @param {boolean} b Set and use default header - true or false .
 344   * @return void
 345   */
 346	setDefaultPostHeader:function(b)
 347	{
 348		if(typeof b == 'string'){
 349			this._default_post_header = b;
 350		}
 351		else if(typeof b == 'boolean'){
 352			this._use_default_post_header = b;
 353		}
 354	},
 355
 356  /**
 357   * @description Member to override the default transaction header..
 358   * @method setDefaultXhrHeader
 359   * @public
 360   * @static
 361   * @param {boolean} b Set and use default header - true or false .
 362   * @return void
 363   */
 364	setDefaultXhrHeader:function(b)
 365	{
 366		if(typeof b == 'string'){
 367			this._default_xhr_header = b;
 368		}
 369		else{
 370			this._use_default_xhr_header = b;
 371		}
 372	},
 373
 374  /**
 375   * @description Member to modify the default polling interval.
 376   * @method setPollingInterval
 377   * @public
 378   * @static
 379   * @param {int} i The polling interval in milliseconds.
 380   * @return void
 381   */
 382	setPollingInterval:function(i)
 383	{
 384		if(typeof i == 'number' && isFinite(i)){
 385			this._polling_interval = i;
 386		}
 387	},
 388
 389  /**
 390   * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
 391   * the XMLHttpRequest instance and the transaction id.
 392   * @method createXhrObject
 393   * @private
 394   * @static
 395   * @param {int} transactionId Property containing the transaction id for this transaction.
 396   * @return object
 397   */
 398	createXhrObject:function(transactionId)
 399	{
 400		var obj,http;
 401		try
 402		{
 403			// Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
 404			http = new XMLHttpRequest();
 405			//  Object literal with http and tId properties
 406			obj = { conn:http, tId:transactionId };
 407		}
 408		catch(e)
 409		{
 410			for(var i=0; i<this._msxml_progid.length; ++i){
 411				try
 412				{
 413					// Instantiates XMLHttpRequest for IE and assign to http
 414					http = new ActiveXObject(this._msxml_progid[i]);
 415					//  Object literal with conn and tId properties
 416					obj = { conn:http, tId:transactionId };
 417					break;
 418				}
 419				catch(e2){}
 420			}
 421		}
 422		finally
 423		{
 424			return obj;
 425		}
 426	},
 427
 428  /**
 429   * @description This method is called by asyncRequest to create a
 430   * valid connection object for the transaction.  It also passes a
 431   * transaction id and increments the transaction id counter.
 432   * @method getConnectionObject
 433   * @private
 434   * @static
 435   * @return {object}
 436   */
 437	getConnectionObject:function(isFileUpload)
 438	{
 439		var o;
 440		var tId = this._transaction_id;
 441
 442		try
 443		{
 444			if(!isFileUpload){
 445				o = this.createXhrObject(tId);
 446			}
 447			else{
 448				o = {};
 449				o.tId = tId;
 450				o.isUpload = true;
 451			}
 452
 453			if(o){
 454				this._transaction_id++;
 455			}
 456		}
 457		catch(e){}
 458		finally
 459		{
 460			return o;
 461		}
 462	},
 463
 464  /**
 465   * @description Method for initiating an asynchronous request via the XHR object.
 466   * @method asyncRequest
 467   * @public
 468   * @static
 469   * @param {string} method HTTP transaction method
 470   * @param {string} uri Fully qualified path of resource
 471   * @param {callback} callback User-defined callback function or object
 472   * @param {string} postData POST body
 473   * @return {object} Returns the connection object
 474   */
 475	asyncRequest:function(method, uri, callback, postData)
 476	{
 477		var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
 478		var args = (callback && callback.argument)?callback.argument:null;
 479
 480		if(!o){
 481			return null;
 482		}
 483		else{
 484
 485			// Intialize any transaction-specific custom events, if provided.
 486			if(callback && callback.customevents){
 487				this.initCustomEvents(o, callback);
 488			}
 489
 490			if(this._isFormSubmit){
 491				if(this._isFileUpload){
 492					this.uploadFile(o, callback, uri, postData);
 493					return o;
 494				}
 495
 496				// If the specified HTTP method is GET, setForm() will return an
 497				// encoded string that is concatenated to the uri to
 498				// create a querystring.
 499				if(method.toUpperCase() == 'GET'){
 500					if(this._sFormData.length !== 0){
 501						// If the URI already contains a querystring, append an ampersand
 502						// and then concatenate _sFormData to the URI.
 503						uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
 504					}
 505				}
 506				else if(method.toUpperCase() == 'POST'){
 507					// If POST data exist in addition to the HTML form data,
 508					// it will be concatenated to the form data.
 509					postData = postData?this._sFormData + "&" + postData:this._sFormData;
 510				}
 511			}
 512
 513			if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
 514				// If callback.cache is defined and set to false, a
 515				// timestamp value will be added to the querystring.
 516				uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
 517			}
 518
 519			o.conn.open(method, uri, true);
 520
 521			// Each transaction will automatically include a custom header of
 522			// "X-Requested-With: XMLHttpRequest" to identify the request as
 523			// having originated from Connection Manager.
 524			if(this._use_default_xhr_header){
 525				if(!this._default_headers['X-Requested-With']){
 526					this.initHeader('X-Requested-With', this._default_xhr_header, true);
 527				}
 528			}
 529
 530			//If the transaction method is POST and the POST header value is set to true
 531			//or a custom value, initalize the Content-Type header to this value.
 532			if((method.toUpperCase() === 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
 533				this.initHeader('Content-Type', this._default_post_header);
 534			}
 535
 536			//Initialize all default and custom HTTP headers,
 537			if(this._has_default_headers || this._has_http_headers){
 538				this.setHeader(o);
 539			}
 540
 541			this.handleReadyState(o, callback);
 542			o.conn.send(postData || '');
 543
 544
 545			// Reset the HTML form data and state properties as
 546			// soon as the data are submitted.
 547			if(this._isFormSubmit === true){
 548				this.resetFormState();
 549			}
 550
 551			// Fire global custom event -- startEvent
 552			this.startEvent.fire(o, args);
 553
 554			if(o.startEvent){
 555				// Fire transaction custom event -- startEvent
 556				o.startEvent.fire(o, args);
 557			}
 558
 559			return o;
 560		}
 561	},
 562
 563  /**
 564   * @description This method creates and subscribes custom events,
 565   * specific to each transaction
 566   * @method initCustomEvents
 567   * @private
 568   * @static
 569   * @param {object} o The connection object
 570   * @param {callback} callback The user-defined callback object
 571   * @return {void}
 572   */
 573	initCustomEvents:function(o, callback)
 574	{
 575		var prop;
 576		// Enumerate through callback.customevents members and bind/subscribe
 577		// events that match in the _customEvents table.
 578		for(prop in callback.customevents){
 579			if(this._customEvents[prop][0]){
 580				// Create the custom event
 581				o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
 582
 583				// Subscribe the custom event
 584				o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
 585			}
 586		}
 587	},
 588
 589  /**
 590   * @description This method serves as a timer that polls the XHR object's readyState
 591   * property during a transaction, instead of binding a callback to the
 592   * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
 593   * will process the response, and the timer will be cleared.
 594   * @method handleReadyState
 595   * @private
 596   * @static
 597   * @param {object} o The connection object
 598   * @param {callback} callback The user-defined callback object
 599   * @return {void}
 600   */
 601
 602    handleReadyState:function(o, callback)
 603
 604    {
 605		var oConn = this;
 606		var args = (callback && callback.argument)?callback.argument:null;
 607
 608		if(callback && callback.timeout){
 609			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
 610		}
 611
 612		this._poll[o.tId] = window.setInterval(
 613			function(){
 614				if(o.conn && o.conn.readyState === 4){
 615
 616					// Clear the polling interval for the transaction
 617					// and remove the reference from _poll.
 618					window.clearInterval(oConn._poll[o.tId]);
 619					delete oConn._poll[o.tId];
 620
 621					if(callback && callback.timeout){
 622						window.clearTimeout(oConn._timeOut[o.tId]);
 623						delete oConn._timeOut[o.tId];
 624					}
 625
 626					// Fire global custom event -- completeEvent
 627					oConn.completeEvent.fire(o, args);
 628
 629					if(o.completeEvent){
 630						// Fire transaction custom event -- completeEvent
 631						o.completeEvent.fire(o, args);
 632					}
 633
 634					oConn.handleTransactionResponse(o, callback);
 635				}
 636			}
 637		,this._polling_interval);
 638    },
 639
 640  /**
 641   * @description This method attempts to interpret the server response and
 642   * determine whether the transaction was successful, or if an error or
 643   * exception was encountered.
 644   * @method handleTransactionResponse
 645   * @private
 646   * @static
 647   * @param {object} o The connection object
 648   * @param {object} callback The user-defined callback object
 649   * @param {boolean} isAbort Determines if the transaction was terminated via abort().
 650   * @return {void}
 651   */
 652    handleTransactionResponse:function(o, callback, isAbort)
 653    {
 654		var httpStatus, responseObject;
 655		var args = (callback && callback.argument)?callback.argument:null;
 656
 657		try
 658		{
 659			if(o.conn.status !== undefined && o.conn.status !== 0){
 660				httpStatus = o.conn.status;
 661			}
 662			else{
 663				httpStatus = 13030;
 664			}
 665		}
 666		catch(e){
 667
 668			 // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
 669			 // when the XHR object's status and statusText properties are
 670			 // unavailable, and a query attempt throws an exception.
 671			httpStatus = 13030;
 672		}
 673
 674		if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
 675			responseObject = this.createResponseObject(o, args);
 676			if(callback && callback.success){
 677				if(!callback.scope){
 678					callback.success(responseObject);
 679				}
 680				else{
 681					// If a scope property is defined, the callback will be fired from
 682					// the context of the object.
 683					callback.success.apply(callback.scope, [responseObject]);
 684				}
 685			}
 686
 687			// Fire global custom event -- successEvent
 688			this.successEvent.fire(responseObject);
 689
 690			if(o.successEvent){
 691				// Fire transaction custom event -- successEvent
 692				o.successEvent.fire(responseObject);
 693			}
 694		}
 695		else{
 696			switch(httpStatus){
 697				// The following cases are wininet.dll error codes that may be encountered.
 698				case 12002: // Server timeout
 699				case 12029: // 12029 to 12031 correspond to dropped connections.
 700				case 12030:
 701				case 12031:
 702				case 12152: // Connection closed by server.
 703				case 13030: // See above comments for variable status.
 704					responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
 705					if(callback && callback.failure){
 706						if(!callback.scope){
 707							callback.failure(responseObject);
 708						}
 709						else{
 710							callback.failure.apply(callback.scope, [responseObject]);
 711						}
 712					}
 713
 714					break;
 715				default:
 716					responseObject = this.createResponseObject(o, args);
 717					if(callback && callback.failure){
 718						if(!callback.scope){
 719							callback.failure(responseObject);
 720						}
 721						else{
 722							callback.failure.apply(callback.scope, [responseObject]);
 723						}
 724					}
 725			}
 726
 727			// Fire global custom event -- failureEvent
 728			this.failureEvent.fire(responseObject);
 729
 730			if(o.failureEvent){
 731				// Fire transaction custom event -- failureEvent
 732				o.failureEvent.fire(responseObject);
 733			}
 734
 735		}
 736
 737		this.releaseObject(o);
 738		responseObject = null;
 739    },
 740
 741  /**
 742   * @description This method evaluates the server response, creates and returns the results via
 743   * its properties.  Success and failure cases will differ in the response
 744   * object's property values.
 745   * @method createResponseObject
 746   * @private
 747   * @static
 748   * @param {object} o The connection object
 749   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
 750   * @return {object}
 751   */
 752    createResponseObject:function(o, callbackArg)
 753    {
 754		var obj = {};
 755		var headerObj = {};
 756
 757		try
 758		{
 759			var headerStr = o.conn.getAllResponseHeaders();
 760			var header = headerStr.split('\n');
 761			for(var i=0; i<header.length; i++){
 762				var delimitPos = header[i].indexOf(':');
 763				if(delimitPos != -1){
 764					headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
 765				}
 766			}
 767		}
 768		catch(e){}
 769
 770		obj.tId = o.tId;
 771		// Normalize IE's response to HTTP 204 when Win error 1223.
 772		obj.status = (o.conn.status == 1223)?204:o.conn.status;
 773		// Normalize IE's statusText to "No Content" instead of "Unknown".
 774		obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
 775		obj.getResponseHeader = headerObj;
 776		obj.getAllResponseHeaders = headerStr;
 777		obj.responseText = o.conn.responseText;
 778		obj.responseXML = o.conn.responseXML;
 779
 780		if(callbackArg){
 781			obj.argument = callbackArg;
 782		}
 783
 784		return obj;
 785    },
 786
 787  /**
 788   * @description If a transaction cannot be completed due to dropped or closed connections,
 789   * there may be not be enough information to build a full response object.
 790   * The failure callback will be fired and this specific condition can be identified
 791   * by a status property value of 0.
 792   *
 793   * If an abort was successful, the status property will report a value of -1.
 794   *
 795   * @method createExceptionObject
 796   * @private
 797   * @static
 798   * @param {int} tId The Transaction Id
 799   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
 800   * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
 801   * @return {object}
 802   */
 803    createExceptionObject:function(tId, callbackArg, isAbort)
 804    {
 805		var COMM_CODE = 0;
 806		var COMM_ERROR = 'communication failure';
 807		var ABORT_CODE = -1;
 808		var ABORT_ERROR = 'transaction aborted';
 809
 810		var obj = {};
 811
 812		obj.tId = tId;
 813		if(isAbort){
 814			obj.status = ABORT_CODE;
 815			obj.statusText = ABORT_ERROR;
 816		}
 817		else{
 818			obj.status = COMM_CODE;
 819			obj.statusText = COMM_ERROR;
 820		}
 821
 822		if(callbackArg){
 823			obj.argument = callbackArg;
 824		}
 825
 826		return obj;
 827    },
 828
 829  /**
 830   * @description Method that initializes the custom HTTP headers for the each transaction.
 831   * @method initHeader
 832   * @public
 833   * @static
 834   * @param {string} label The HTTP header label
 835   * @param {string} value The HTTP header value
 836   * @param {string} isDefault Determines if the specific header is a default header
 837   * automatically sent with each transaction.
 838   * @return {void}
 839   */
 840	initHeader:function(label, value, isDefault)
 841	{
 842		var headerObj = (isDefault)?this._default_headers:this._http_headers;
 843		headerObj[label] = value;
 844
 845		if(isDefault){
 846			this._has_default_headers = true;
 847		}
 848		else{
 849			this._has_http_headers = true;
 850		}
 851	},
 852
 853
 854  /**
 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}
 861   */
 862	setHeader:function(o)
 863	{
 864		var prop;
 865		if(this._has_default_headers){
 866			for(prop in this._default_headers){
 867				if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
 868					o.conn.setRequestHeader(prop, this._default_headers[prop]);
 869				}
 870			}
 871		}
 872
 873		if(this._has_http_headers){
 874			for(prop in this._http_headers){
 875				if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
 876					o.conn.setRequestHeader(prop, this._http_headers[prop]);
 877				}
 878			}
 879			delete this._http_headers;
 880
 881			this._http_headers = {};
 882			this._has_http_headers = false;
 883		}
 884	},
 885
 886  /**
 887   * @description Resets the default HTTP headers object
 888   * @method resetDefaultHeaders
 889   * @public
 890   * @static
 891   * @return {void}
 892   */
 893	resetDefaultHeaders:function(){
 894		delete this._default_headers;
 895		this._default_headers = {};
 896		this._has_default_headers = false;
 897	},
 898
 899  /**
 900   * @description This method assembles the form label and value pairs and
 901   * constructs an encoded string.
 902   * asyncRequest() will automatically initialize the transaction with a
 903   * a HTTP header Content-Type of application/x-www-form-urlencoded.
 904   * @method setForm
 905   * @public
 906   * @static
 907   * @param {string || object} form id or name attribute, or form object.
 908   * @param {boolean} optional enable file upload.
 909   * @param {boolean} optional enable file upload over SSL in IE only.
 910   * @return {string} string of the HTML form field name and value pairs..
 911   */
 912	setForm:function(formId, isUpload, secureUri)
 913	{
 914        var oForm, oElement, oName, oValue, oDisabled,
 915            hasSubmit = false,
 916            data = [], item = 0,
 917            i,len,j,jlen,opt;
 918
 919		this.resetFormState();
 920
 921		if(typeof formId == 'string'){
 922			// Determine if the argument is a form id or a form name.
 923			// Note form name usage is deprecated by supported
 924			// here for legacy reasons.
 925			oForm = (document.getElementById(formId) || document.forms[formId]);
 926		}
 927		else if(typeof formId == 'object'){
 928			// Treat argument as an HTML form object.
 929			oForm = formId;
 930		}
 931		else{
 932			return;
 933		}
 934
 935		// If the isUpload argument is true, setForm will call createFrame to initialize
 936		// an iframe as the form target.
 937		//
 938		// The argument secureURI is also required by IE in SSL environments
 939		// where the secureURI string is a fully qualified HTTP path, used to set the source
 940		// of the iframe, to a stub resource in the same domain.
 941		if(isUpload){
 942
 943			// Create iframe in preparation for file upload.
 944			this.createFrame(secureUri?secureUri:null);
 945
 946			// Set form reference and file upload properties to true.
 947			this._isFormSubmit = true;
 948			this._isFileUpload = true;
 949			this._formNode = oForm;
 950
 951			return;
 952
 953		}
 954
 955		// Iterate over the form elements collection to construct the
 956		// label-value pairs.
 957		for (i=0,len=oForm.elements.length; i<len; ++i){
 958			oElement  = oForm.elements[i];
 959			oDisabled = oElement.disabled;
 960            oName     = oElement.name;
 961
 962			// Do not submit fields that are disabled or
 963			// do not have a name attribute value.
 964			if(!oDisabled && oName)
 965			{
 966                oName  = encodeURIComponent(oName)+'=';
 967                oValue = encodeURIComponent(oElement.value);
 968
 969				switch(oElement.type)
 970				{
 971                    // Safari, Opera, FF all default opt.value from .text if
 972                    // value attribute not specified in markup
 973					case 'select-one':
 974                        if (oElement.selectedIndex > -1) {
 975                            opt = oElement.options[oElement.selectedIndex];
 976                            data[item++] = oName + encodeURIComponent(
 977                                (opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
 978                        }
 979                        break;
 980					case 'select-multiple':
 981                        if (oElement.selectedIndex > -1) {
 982                            for(j=oElement.selectedIndex, jlen=oElement.options.length; j<jlen; ++j){
 983                                opt = oElement.options[j];
 984                                if (opt.selected) {
 985                                    data[item++] = oName + encodeURIComponent(
 986                                        (opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
 987                                }
 988                            }
 989                        }
 990						break;
 991					case 'radio':
 992					case 'checkbox':
 993						if(oElement.checked){
 994                            data[item++] = oName + oValue;
 995						}
 996						break;
 997					case 'file':
 998						// stub case as XMLHttpRequest will only send the file path as a string.
 999					case undefined:
1000						// stub case for fieldset element which returns undefined.
1001					case 'reset':
1002						// stub case for input type reset button.
1003					case 'button':
1004						// stub case for input type button elements.
1005						break;
1006					case 'submit':
1007						if(hasSubmit === false){
1008							if(this._hasSubmitListener && this._submitElementValue){
1009                                data[item++] = this._submitElementValue;
1010							}
1011							hasSubmit = true;
1012						}
1013						break;
1014					default:
1015                        data[item++] = oName + oValue;
1016				}
1017			}
1018		}
1019
1020		this._isFormSubmit = true;
1021		this._sFormData = data.join('&');
1022
1023
1024		this.initHeader('Content-Type', this._default_form_header);
1025
1026		return this._sFormData;
1027	},
1028
1029  /**
1030   * @description Resets HTML form properties when an HTML form or HTML form
1031   * with file upload transaction is sent.
1032   * @method resetFormState
1033   * @private
1034   * @static
1035   * @return {void}
1036   */
1037	resetFormState:function(){
1038		this._isFormSubmit = false;
1039		this._isFileUpload = false;
1040		this._formNode = null;
1041		this._sFormData = "";
1042	},
1043
1044  /**
1045   * @description Creates an iframe to be used for form file uploads.  It is remove from the
1046   * document upon completion of the upload transaction.
1047   * @method createFrame
1048   * @private
1049   * @static
1050   * @param {string} optional qualified path of iframe resource for SSL in IE.
1051   * @return {void}
1052   */
1053	createFrame:function(secureUri){
1054
1055		// IE does not allow the setting of id and name attributes as object
1056		// properties via createElement().  A different iframe creation
1057		// pattern is required for IE.
1058		var frameId = 'yuiIO' + this._transaction_id;
1059		var io;
1060		if(YAHOO.env.ua.ie){
1061			io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
1062
1063			// IE will throw a security exception in an SSL environment if the
1064			// iframe source is undefined.
1065			if(typeof secureUri == 'boolean'){
1066				io.src = 'javascript:false';
1067			}
1068		}
1069		else{
1070			io = document.createElement('iframe');
1071			io.id = frameId;
1072			io.name = frameId;
1073		}
1074
1075		io.style.position = 'absolute';
1076		io.style.top = '-1000px';
1077		io.style.left = '-1000px';
1078
1079		document.body.appendChild(io);
1080	},
1081
1082  /**
1083   * @description Parses the POST data and creates hidden form elements
1084   * for each key-value, and appends them to the HTML form object.
1085   * @method appendPostData
1086   * @private
1087   * @static
1088   * @param {string} postData The HTTP POST data
1089   * @return {array} formElements Collection of hidden fields.
1090   */
1091	appendPostData:function(postData)
1092	{
1093		var formElements = [],
1094			postMessage = postData.split('&'),
1095			i, delimitPos;
1096		for(i=0; i < postMessage.length; i++){
1097			delimitPos = postMessage[i].indexOf('=');
1098			if(delimitPos != -1){
1099				formElements[i] = document.createElement('input');
1100				formElements[i].type = 'hidden';
1101				formElements[i].name = decodeURIComponent(postMessage[i].substring(0,delimitPos));
1102				formElements[i].value = decodeURIComponent(postMessage[i].substring(delimitPos+1));
1103				this._formNode.appendChild(formElements[i]);
1104			}
1105		}
1106
1107		return formElements;
1108	},
1109
1110  /**
1111   * @description Uploads HTML form, inclusive of files/attachments, using the
1112   * iframe created in createFrame to facilitate the transaction.
1113   * @method uploadFile
1114   * @private
1115   * @static
1116   * @param {int} id The transaction id.
1117   * @param {object} callback User-defined callback object.
1118   * @param {string} uri Fully qualified path of resource.
1119   * @param {string} postData POST data to be submitted in addition to HTML form.
1120   * @return {void}
1121   */
1122	uploadFile:function(o, callback, uri, postData){
1123
1124		// Each iframe has an id prefix of "yuiIO" followed
1125		// by the unique transaction id.
1126		var frameId = 'yuiIO' + o.tId,
1127		    uploadEncoding = 'multipart/form-data',
1128		    io = document.getElementById(frameId),
1129		    oConn = this,
1130			args = (callback && callback.argument)?callback.argument:null,
1131            oElements,i,prop,obj;
1132
1133		// Track original HTML form attribute values.
1134		var rawFormAttributes =
1135		{
1136			action:this._formNode.getAttribute('action'),
1137			method:this._formNode.getAttribute('method'),
1138			target:this._formNode.getAttribute('target')
1139		};
1140
1141		// Initialize the HTML form properties in case they are
1142		// not defined in the HTML form.
1143		this._formNode.setAttribute('action', uri);
1144		this._formNode.setAttribute('method', 'POST');
1145		this._formNode.setAttribute('target', frameId);
1146
1147		if(YAHOO.env.ua.ie){
1148			// IE does not respect property enctype for HTML forms.
1149			// Instead it uses the property - "encoding".
1150			this._formNode.setAttribute('encoding', uploadEncoding);
1151		}
1152		else{
1153			this._formNode.setAttribute('enctype', uploadEncoding);
1154		}
1155
1156		if(postData){
1157			oElements = this.appendPostData(postData);
1158		}
1159
1160		// Start file upload.
1161		this._formNode.submit();
1162
1163		// Fire global custom event -- startEvent
1164		this.startEvent.fire(o, args);
1165
1166		if(o.startEvent){
1167			// Fire transaction custom event -- startEvent
1168			o.startEvent.fire(o, args);
1169		}
1170
1171		// Start polling if a callback is present and the timeout
1172		// property has been defined.
1173		if(callback && callback.timeout){
1174			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
1175		}
1176
1177		// Remove HTML elements created by appendPostData
1178		if(oElements && oElements.length > 0){
1179			for(i=0; i < oElements.length; i++){
1180				this._formNode.removeChild(oElements[i]);
1181			}
1182		}
1183
1184		// Restore HTML form attributes to their original
1185		// values prior to file upload.
1186		for(prop in rawFormAttributes){
1187			if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
1188				if(rawFormAttributes[prop]){
1189					this._formNode.setAttribute(prop, rawFormAttributes[prop]);
1190				}
1191				else{
1192					this._formNode.removeAttribute(prop);
1193				}
1194			}
1195		}
1196
1197		// Reset HTML form state properties.
1198		this.resetFormState();
1199
1200		// Create the upload callback handler that fires when the iframe
1201		// receives the load event.  Subsequently, the event handler is detached
1202		// and the iframe removed from the document.
1203		var uploadCallback = function()
1204		{
1205			if(callback && callback.timeout){
1206				window.clearTimeout(oConn._timeOut[o.tId]);
1207				delete oConn._timeOut[o.tId];
1208			}
1209
1210			// Fire global custom event -- completeEvent
1211			oConn.completeEvent.fire(o, args);
1212
1213			if(o.completeEvent){
1214				// Fire transaction custom event -- completeEvent
1215				o.completeEvent.fire(o, args);
1216			}
1217
1218			obj = {
1219			    tId : o.tId,
1220			    argument : callback.argument
1221            };
1222
1223			try
1224			{
1225				// responseText and responseXML will be populated with the same data from the iframe.
1226				// Since the HTTP headers cannot be read from the iframe
1227				obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
1228				obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
1229			}
1230			catch(e){}
1231
1232			if(callback && callback.upload){
1233				if(!callback.scope){
1234					callback.upload(obj);
1235				}
1236				else{
1237					callback.upload.apply(callback.scope, [obj]);
1238				}
1239			}
1240
1241			// Fire global custom event -- uploadEvent
1242			oConn.uploadEvent.fire(obj);
1243
1244			if(o.uploadEvent){
1245				// Fire transaction custom event -- uploadEvent
1246				o.uploadEvent.fire(obj);
1247			}
1248
1249			YAHOO.util.Event.removeListener(io, "load", uploadCallback);
1250
1251			setTimeout(
1252				function(){
1253					document.body.removeChild(io);
1254					oConn.releaseObject(o);
1255				}, 100);
1256		};
1257
1258		// Bind the onload handler to the iframe to detect the file upload response.
1259		YAHOO.util.Event.addListener(io, "load", uploadCallback);
1260	},
1261
1262  /**
1263   * @description Method to terminate a transaction, if it has not reached readyState 4.
1264   * @method abort
1265   * @public
1266   * @static
1267   * @param {object} o The connection object returned by asyncRequest.
1268   * @param {object} callback  User-defined callback object.
1269   * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
1270   * @return {boolean}
1271   */
1272	abort:function(o, callback, isTimeout)
1273	{
1274		var abortStatus;
1275		var args = (callback && callback.argument)?callback.argument:null;
1276
1277
1278		if(o && o.conn){
1279			if(this.isCallInProgress(o)){
1280				// Issue abort request
1281				o.conn.abort();
1282
1283				window.clearInterval(this._poll[o.tId]);
1284				delete this._poll[o.tId];
1285
1286				if(isTimeout){
1287					window.clearTimeout(this._timeOut[o.tId]);
1288					delete this._timeOut[o.tId];
1289				}
1290
1291				abortStatus = true;
1292			}
1293		}
1294		else if(o && o.isUpload === true){
1295			var frameId = 'yuiIO' + o.tId;
1296			var io = document.getElementById(frameId);
1297
1298			if(io){
1299				// Remove all listeners on the iframe prior to
1300				// its destruction.
1301				YAHOO.util.Event.removeListener(io, "load");
1302				// Destroy the iframe facilitating the transaction.
1303				document.body.removeChild(io);
1304
1305				if(isTimeout){
1306					window.clearTimeout(this._timeOut[o.tId]);
1307					delete this._timeOut[o.tId];
1308				}
1309
1310				abortStatus = true;
1311			}
1312		}
1313		else{
1314			abortStatus = false;
1315		}
1316
1317		if(abortStatus === true){
1318			// Fire global custom event -- abortEvent
1319			this.abortEvent.fire(o, args);
1320
1321			if(o.abortEvent){
1322				// Fire transaction custom event -- abortEvent
1323				o.abortEvent.fire(o, args);
1324			}
1325
1326			this.handleTransactionResponse(o, callback, true);
1327		}
1328
1329		return abortStatus;
1330	},
1331
1332  /**
1333   * @description Determines if the transaction is still being processed.
1334   * @method isCallInProgress
1335   * @public
1336   * @static
1337   * @param {object} o The connection object returned by asyncRequest
1338   * @return {boolean}
1339   */
1340	isCallInProgress:function(o)
1341	{
1342		// if the XHR object assigned to the transaction has not been dereferenced,
1343		// then check its readyState status.  Otherwise, return false.
1344		if(o && o.conn){
1345			return o.conn.readyState !== 4 && o.conn.readyState !== 0;
1346		}
1347		else if(o && o.isUpload === true){
1348			var frameId = 'yuiIO' + o.tId;
1349			return document.getElementById(frameId)?true:false;
1350		}
1351		else{
1352			return false;
1353		}
1354	},
1355
1356  /**
1357   * @description Dereference the XHR instance and the connection object after the transaction is completed.
1358   * @method releaseObject
1359   * @private
1360   * @static
1361   * @param {object} o The connection object
1362   * @return {void}
1363   */
1364	releaseObject:function(o)
1365	{
1366		if(o && o.conn){
1367			//dereference the XHR instance.
1368			o.conn = null;
1369
1370
1371			//dereference the connection object.
1372			o = null;
1373		}
1374	}
1375};
1376YAHOO.register("connection", YAHOO.util.Connect, {version: "2.7.0", build: "1799"});