Project

General

Profile

1
/*
2
Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
5
version: 2.4.1
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

    
26
YAHOO.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
					if(obj.type && obj.type.toLowerCase() == 'submit'){
239
						YAHOO.util.Connect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
240
					}
241
				});
242
			return true;
243
	    }
244
	    return false;
245
	 })(),
246

    
247
  /**
248
   * @description Custom event that fires at the start of a transaction
249
   * @property startEvent
250
   * @private
251
   * @static
252
   * @type CustomEvent
253
   */
254
	startEvent: new YAHOO.util.CustomEvent('start'),
255

    
256
  /**
257
   * @description Custom event that fires when a transaction response has completed.
258
   * @property completeEvent
259
   * @private
260
   * @static
261
   * @type CustomEvent
262
   */
263
	completeEvent: new YAHOO.util.CustomEvent('complete'),
264

    
265
  /**
266
   * @description Custom event that fires when handleTransactionResponse() determines a
267
   * response in the HTTP 2xx range.
268
   * @property successEvent
269
   * @private
270
   * @static
271
   * @type CustomEvent
272
   */
273
	successEvent: new YAHOO.util.CustomEvent('success'),
274

    
275
  /**
276
   * @description Custom event that fires when handleTransactionResponse() determines a
277
   * response in the HTTP 4xx/5xx range.
278
   * @property failureEvent
279
   * @private
280
   * @static
281
   * @type CustomEvent
282
   */
283
	failureEvent: new YAHOO.util.CustomEvent('failure'),
284

    
285
  /**
286
   * @description Custom event that fires when handleTransactionResponse() determines a
287
   * response in the HTTP 4xx/5xx range.
288
   * @property failureEvent
289
   * @private
290
   * @static
291
   * @type CustomEvent
292
   */
293
	uploadEvent: new YAHOO.util.CustomEvent('upload'),
294

    
295
  /**
296
   * @description Custom event that fires when a transaction is successfully aborted.
297
   * @property abortEvent
298
   * @private
299
   * @static
300
   * @type CustomEvent
301
   */
302
	abortEvent: new YAHOO.util.CustomEvent('abort'),
303

    
304
  /**
305
   * @description A reference table that maps callback custom events members to its specific
306
   * event name.
307
   * @property _customEvents
308
   * @private
309
   * @static
310
   * @type object
311
   */
312
	_customEvents:
313
	{
314
		onStart:['startEvent', 'start'],
315
		onComplete:['completeEvent', 'complete'],
316
		onSuccess:['successEvent', 'success'],
317
		onFailure:['failureEvent', 'failure'],
318
		onUpload:['uploadEvent', 'upload'],
319
		onAbort:['abortEvent', 'abort']
320
	},
321

    
322
  /**
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.
326
   * @method setProgId
327
   * @public
328
   * @static
329
   * @param {string} id The ActiveX id to be added to initialize the XHR object.
330
   * @return void
331
   */
332
	setProgId:function(id)
333
	{
334
		this._msxml_progid.unshift(id);
335
	},
336

    
337
  /**
338
   * @description Member to override the default POST header.
339
   * @method setDefaultPostHeader
340
   * @public
341
   * @static
342
   * @param {boolean} b Set and use default header - true or false .
343
   * @return void
344
   */
345
	setDefaultPostHeader:function(b)
346
	{
347
		if(typeof b == 'string'){
348
			this._default_post_header = b;
349
		}
350
		else if(typeof b == 'boolean'){
351
			this._use_default_post_header = b;
352
		}
353
	},
354

    
355
  /**
356
   * @description Member to override the default transaction header..
357
   * @method setDefaultXhrHeader
358
   * @public
359
   * @static
360
   * @param {boolean} b Set and use default header - true or false .
361
   * @return void
362
   */
363
	setDefaultXhrHeader:function(b)
364
	{
365
		if(typeof b == 'string'){
366
			this._default_xhr_header = b;
367
		}
368
		else{
369
			this._use_default_xhr_header = b;
370
		}
371
	},
372

    
373
  /**
374
   * @description Member to modify the default polling interval.
375
   * @method setPollingInterval
376
   * @public
377
   * @static
378
   * @param {int} i The polling interval in milliseconds.
379
   * @return void
380
   */
381
	setPollingInterval:function(i)
382
	{
383
		if(typeof i == 'number' && isFinite(i)){
384
			this._polling_interval = i;
385
		}
386
	},
387

    
388
  /**
389
   * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
390
   * the XMLHttpRequest instance and the transaction id.
391
   * @method createXhrObject
392
   * @private
393
   * @static
394
   * @param {int} transactionId Property containing the transaction id for this transaction.
395
   * @return object
396
   */
397
	createXhrObject:function(transactionId)
398
	{
399
		var obj,http;
400
		try
401
		{
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 };
406
		}
407
		catch(e)
408
		{
409
			for(var i=0; i<this._msxml_progid.length; ++i){
410
				try
411
				{
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 };
416
					break;
417
				}
418
				catch(e){}
419
			}
420
		}
421
		finally
422
		{
423
			return obj;
424
		}
425
	},
426

    
427
  /**
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
432
   * @private
433
   * @static
434
   * @return {object}
435
   */
436
	getConnectionObject:function(isFileUpload)
437
	{
438
		var o;
439
		var tId = this._transaction_id;
440

    
441
		try
442
		{
443
			if(!isFileUpload){
444
				o = this.createXhrObject(tId);
445
			}
446
			else{
447
				o = {};
448
				o.tId = tId;
449
				o.isUpload = true;
450
			}
451

    
452
			if(o){
453
				this._transaction_id++;
454
			}
455
		}
456
		catch(e){}
457
		finally
458
		{
459
			return o;
460
		}
461
	},
462

    
463
  /**
464
   * @description Method for initiating an asynchronous request via the XHR object.
465
   * @method asyncRequest
466
   * @public
467
   * @static
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
473
   */
474
	asyncRequest:function(method, uri, callback, postData)
475
	{
476
		var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
477
		var args = (callback && callback.argument)?callback.argument:null;
478

    
479
		if(!o){
480
			return null;
481
		}
482
		else{
483

    
484
			// Intialize any transaction-specific custom events, if provided.
485
			if(callback && callback.customevents){
486
				this.initCustomEvents(o, callback);
487
			}
488

    
489
			if(this._isFormSubmit){
490
				if(this._isFileUpload){
491
					this.uploadFile(o, callback, uri, postData);
492
					return o;
493
				}
494

    
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;
503
					}
504
				}
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;
509
				}
510
			}
511

    
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();
516
			}
517

    
518
			o.conn.open(method, uri, true);
519

    
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);
526
				}
527
			}
528

    
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);
533
			}
534

    
535
			//Initialize all default and custom HTTP headers,
536
			if(this._has_default_headers || this._has_http_headers){
537
				this.setHeader(o);
538
			}
539

    
540
			this.handleReadyState(o, callback);
541
			o.conn.send(postData || null);
542

    
543

    
544
			// Reset the HTML form data and state properties as
545
			// soon as the data are submitted.
546
			if(this._isFormSubmit === true){
547
				this.resetFormState();
548
			}
549

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

    
553
			if(o.startEvent){
554
				// Fire transaction custom event -- startEvent
555
				o.startEvent.fire(o, args);
556
			}
557

    
558
			return o;
559
		}
560
	},
561

    
562
  /**
563
   * @description This method creates and subscribes custom events,
564
   * specific to each transaction
565
   * @method initCustomEvents
566
   * @private
567
   * @static
568
   * @param {object} o The connection object
569
   * @param {callback} callback The user-defined callback object
570
   * @return {void}
571
   */
572
	initCustomEvents:function(o, callback)
573
	{
574
		// Enumerate through callback.customevents members and bind/subscribe
575
		// events that match in the _customEvents table.
576
		for(var prop in callback.customevents){
577
			if(this._customEvents[prop][0]){
578
				// Create the custom event
579
				o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
580

    
581
				// Subscribe the custom event
582
				o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
583
			}
584
		}
585
	},
586

    
587
  /**
588
   * @description This method serves as a timer that polls the XHR object's readyState
589
   * property during a transaction, instead of binding a callback to the
590
   * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
591
   * will process the response, and the timer will be cleared.
592
   * @method handleReadyState
593
   * @private
594
   * @static
595
   * @param {object} o The connection object
596
   * @param {callback} callback The user-defined callback object
597
   * @return {void}
598
   */
599

    
600
    handleReadyState:function(o, callback)
601

    
602
    {
603
		var oConn = this;
604
		var args = (callback && callback.argument)?callback.argument:null;
605

    
606
		if(callback && callback.timeout){
607
			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
608
		}
609

    
610
		this._poll[o.tId] = window.setInterval(
611
			function(){
612
				if(o.conn && o.conn.readyState === 4){
613

    
614
					// Clear the polling interval for the transaction
615
					// and remove the reference from _poll.
616
					window.clearInterval(oConn._poll[o.tId]);
617
					delete oConn._poll[o.tId];
618

    
619
					if(callback && callback.timeout){
620
						window.clearTimeout(oConn._timeOut[o.tId]);
621
						delete oConn._timeOut[o.tId];
622
					}
623

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

    
627
					if(o.completeEvent){
628
						// Fire transaction custom event -- completeEvent
629
						o.completeEvent.fire(o, args);
630
					}
631

    
632
					oConn.handleTransactionResponse(o, callback);
633
				}
634
			}
635
		,this._polling_interval);
636
    },
637

    
638
  /**
639
   * @description This method attempts to interpret the server response and
640
   * determine whether the transaction was successful, or if an error or
641
   * exception was encountered.
642
   * @method handleTransactionResponse
643
   * @private
644
   * @static
645
   * @param {object} o The connection object
646
   * @param {object} callback The user-defined callback object
647
   * @param {boolean} isAbort Determines if the transaction was terminated via abort().
648
   * @return {void}
649
   */
650
    handleTransactionResponse:function(o, callback, isAbort)
651
    {
652
		var httpStatus, responseObject;
653
		var args = (callback && callback.argument)?callback.argument:null;
654

    
655
		try
656
		{
657
			if(o.conn.status !== undefined && o.conn.status !== 0){
658
				httpStatus = o.conn.status;
659
			}
660
			else{
661
				httpStatus = 13030;
662
			}
663
		}
664
		catch(e){
665

    
666
			 // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
667
			 // when the XHR object's status and statusText properties are
668
			 // unavailable, and a query attempt throws an exception.
669
			httpStatus = 13030;
670
		}
671

    
672
		if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
673
			responseObject = this.createResponseObject(o, args);
674
			if(callback && callback.success){
675
				if(!callback.scope){
676
					callback.success(responseObject);
677
				}
678
				else{
679
					// If a scope property is defined, the callback will be fired from
680
					// the context of the object.
681
					callback.success.apply(callback.scope, [responseObject]);
682
				}
683
			}
684

    
685
			// Fire global custom event -- successEvent
686
			this.successEvent.fire(responseObject);
687

    
688
			if(o.successEvent){
689
				// Fire transaction custom event -- successEvent
690
				o.successEvent.fire(responseObject);
691
			}
692
		}
693
		else{
694
			switch(httpStatus){
695
				// The following cases are wininet.dll error codes that may be encountered.
696
				case 12002: // Server timeout
697
				case 12029: // 12029 to 12031 correspond to dropped connections.
698
				case 12030:
699
				case 12031:
700
				case 12152: // Connection closed by server.
701
				case 13030: // See above comments for variable status.
702
					responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
703
					if(callback && callback.failure){
704
						if(!callback.scope){
705
							callback.failure(responseObject);
706
						}
707
						else{
708
							callback.failure.apply(callback.scope, [responseObject]);
709
						}
710
					}
711

    
712
					break;
713
				default:
714
					responseObject = this.createResponseObject(o, args);
715
					if(callback && callback.failure){
716
						if(!callback.scope){
717
							callback.failure(responseObject);
718
						}
719
						else{
720
							callback.failure.apply(callback.scope, [responseObject]);
721
						}
722
					}
723
			}
724

    
725
			// Fire global custom event -- failureEvent
726
			this.failureEvent.fire(responseObject);
727

    
728
			if(o.failureEvent){
729
				// Fire transaction custom event -- failureEvent
730
				o.failureEvent.fire(responseObject);
731
			}
732

    
733
		}
734

    
735
		this.releaseObject(o);
736
		responseObject = null;
737
    },
738

    
739
  /**
740
   * @description This method evaluates the server response, creates and returns the results via
741
   * its properties.  Success and failure cases will differ in the response
742
   * object's property values.
743
   * @method createResponseObject
744
   * @private
745
   * @static
746
   * @param {object} o The connection object
747
   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
748
   * @return {object}
749
   */
750
    createResponseObject:function(o, callbackArg)
751
    {
752
		var obj = {};
753
		var headerObj = {};
754

    
755
		try
756
		{
757
			var headerStr = o.conn.getAllResponseHeaders();
758
			var header = headerStr.split('\n');
759
			for(var i=0; i<header.length; i++){
760
				var delimitPos = header[i].indexOf(':');
761
				if(delimitPos != -1){
762
					headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
763
				}
764
			}
765
		}
766
		catch(e){}
767

    
768
		obj.tId = o.tId;
769
		// Normalize IE's response to HTTP 204 when Win error 1223.
770
		obj.status = (o.conn.status == 1223)?204:o.conn.status;
771
		// Normalize IE's statusText to "No Content" instead of "Unknown".
772
		obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
773
		obj.getResponseHeader = headerObj;
774
		obj.getAllResponseHeaders = headerStr;
775
		obj.responseText = o.conn.responseText;
776
		obj.responseXML = o.conn.responseXML;
777

    
778
		if(callbackArg){
779
			obj.argument = callbackArg;
780
		}
781

    
782
		return obj;
783
    },
784

    
785
  /**
786
   * @description If a transaction cannot be completed due to dropped or closed connections,
787
   * there may be not be enough information to build a full response object.
788
   * The failure callback will be fired and this specific condition can be identified
789
   * by a status property value of 0.
790
   *
791
   * If an abort was successful, the status property will report a value of -1.
792
   *
793
   * @method createExceptionObject
794
   * @private
795
   * @static
796
   * @param {int} tId The Transaction Id
797
   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
798
   * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
799
   * @return {object}
800
   */
801
    createExceptionObject:function(tId, callbackArg, isAbort)
802
    {
803
		var COMM_CODE = 0;
804
		var COMM_ERROR = 'communication failure';
805
		var ABORT_CODE = -1;
806
		var ABORT_ERROR = 'transaction aborted';
807

    
808
		var obj = {};
809

    
810
		obj.tId = tId;
811
		if(isAbort){
812
			obj.status = ABORT_CODE;
813
			obj.statusText = ABORT_ERROR;
814
		}
815
		else{
816
			obj.status = COMM_CODE;
817
			obj.statusText = COMM_ERROR;
818
		}
819

    
820
		if(callbackArg){
821
			obj.argument = callbackArg;
822
		}
823

    
824
		return obj;
825
    },
826

    
827
  /**
828
   * @description Method that initializes the custom HTTP headers for the each transaction.
829
   * @method initHeader
830
   * @public
831
   * @static
832
   * @param {string} label The HTTP header label
833
   * @param {string} value The HTTP header value
834
   * @param {string} isDefault Determines if the specific header is a default header
835
   * automatically sent with each transaction.
836
   * @return {void}
837
   */
838
	initHeader:function(label, value, isDefault)
839
	{
840
		var headerObj = (isDefault)?this._default_headers:this._http_headers;
841
		headerObj[label] = value;
842

    
843
		if(isDefault){
844
			this._has_default_headers = true;
845
		}
846
		else{
847
			this._has_http_headers = true;
848
		}
849
	},
850

    
851

    
852
  /**
853
   * @description Accessor that sets the HTTP headers for each transaction.
854
   * @method setHeader
855
   * @private
856
   * @static
857
   * @param {object} o The connection object for the transaction.
858
   * @return {void}
859
   */
860
	setHeader:function(o)
861
	{
862
		if(this._has_default_headers){
863
			for(var prop in this._default_headers){
864
				if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
865
					o.conn.setRequestHeader(prop, this._default_headers[prop]);
866
				}
867
			}
868
		}
869

    
870
		if(this._has_http_headers){
871
			for(var prop in this._http_headers){
872
				if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
873
					o.conn.setRequestHeader(prop, this._http_headers[prop]);
874
				}
875
			}
876
			delete this._http_headers;
877

    
878
			this._http_headers = {};
879
			this._has_http_headers = false;
880
		}
881
	},
882

    
883
  /**
884
   * @description Resets the default HTTP headers object
885
   * @method resetDefaultHeaders
886
   * @public
887
   * @static
888
   * @return {void}
889
   */
890
	resetDefaultHeaders:function(){
891
		delete this._default_headers;
892
		this._default_headers = {};
893
		this._has_default_headers = false;
894
	},
895

    
896
  /**
897
   * @description This method assembles the form label and value pairs and
898
   * constructs an encoded string.
899
   * asyncRequest() will automatically initialize the transaction with a
900
   * a HTTP header Content-Type of application/x-www-form-urlencoded.
901
   * @method setForm
902
   * @public
903
   * @static
904
   * @param {string || object} form id or name attribute, or form object.
905
   * @param {boolean} optional enable file upload.
906
   * @param {boolean} optional enable file upload over SSL in IE only.
907
   * @return {string} string of the HTML form field name and value pairs..
908
   */
909
	setForm:function(formId, isUpload, secureUri)
910
	{
911
		// reset the HTML form data and state properties
912
		this.resetFormState();
913

    
914
		var oForm;
915
		if(typeof formId == 'string'){
916
			// Determine if the argument is a form id or a form name.
917
			// Note form name usage is deprecated, but supported
918
			// here for backward compatibility.
919
			oForm = (document.getElementById(formId) || document.forms[formId]);
920
		}
921
		else if(typeof formId == 'object'){
922
			// Treat argument as an HTML form object.
923
			oForm = formId;
924
		}
925
		else{
926
			return;
927
		}
928

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

    
937
			// Create iframe in preparation for file upload.
938
			var io = this.createFrame(secureUri?secureUri:null);
939
			// Set form reference and file upload properties to true.
940
			this._isFormSubmit = true;
941
			this._isFileUpload = true;
942
			this._formNode = oForm;
943

    
944
			return;
945

    
946
		}
947

    
948
		var oElement, oName, oValue, oDisabled;
949
		var hasSubmit = false;
950

    
951
		// Iterate over the form elements collection to construct the
952
		// label-value pairs.
953
		for (var i=0; i<oForm.elements.length; i++){
954
			oElement = oForm.elements[i];
955
			oDisabled = oElement.disabled;
956
			oName = oElement.name;
957
			oValue = oElement.value;
958

    
959
			// Do not submit fields that are disabled or
960
			// do not have a name attribute value.
961
			if(!oDisabled && oName)
962
			{
963
				switch(oElement.type)
964
				{
965
					case 'select-one':
966
					case 'select-multiple':
967
						for(var j=0; j<oElement.options.length; j++){
968
							if(oElement.options[j].selected){
969
								if(window.ActiveXObject){
970
									this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
971
								}
972
								else{
973
									this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
974
								}
975
							}
976
						}
977
						break;
978
					case 'radio':
979
					case 'checkbox':
980
						if(oElement.checked){
981
							this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
982
						}
983
						break;
984
					case 'file':
985
						// stub case as XMLHttpRequest will only send the file path as a string.
986
					case undefined:
987
						// stub case for fieldset element which returns undefined.
988
					case 'reset':
989
						// stub case for input type reset button.
990
					case 'button':
991
						// stub case for input type button elements.
992
						break;
993
					case 'submit':
994
						if(hasSubmit === false){
995
							if(this._hasSubmitListener && this._submitElementValue){
996
								this._sFormData += this._submitElementValue + '&';
997
							}
998
							else{
999
								this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1000
							}
1001

    
1002
							hasSubmit = true;
1003
						}
1004
						break;
1005
					default:
1006
						this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
1007
				}
1008
			}
1009
		}
1010

    
1011
		this._isFormSubmit = true;
1012
		this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
1013

    
1014

    
1015
		this.initHeader('Content-Type', this._default_form_header);
1016

    
1017
		return this._sFormData;
1018
	},
1019

    
1020
  /**
1021
   * @description Resets HTML form properties when an HTML form or HTML form
1022
   * with file upload transaction is sent.
1023
   * @method resetFormState
1024
   * @private
1025
   * @static
1026
   * @return {void}
1027
   */
1028
	resetFormState:function(){
1029
		this._isFormSubmit = false;
1030
		this._isFileUpload = false;
1031
		this._formNode = null;
1032
		this._sFormData = "";
1033
	},
1034

    
1035
  /**
1036
   * @description Creates an iframe to be used for form file uploads.  It is remove from the
1037
   * document upon completion of the upload transaction.
1038
   * @method createFrame
1039
   * @private
1040
   * @static
1041
   * @param {string} optional qualified path of iframe resource for SSL in IE.
1042
   * @return {void}
1043
   */
1044
	createFrame:function(secureUri){
1045

    
1046
		// IE does not allow the setting of id and name attributes as object
1047
		// properties via createElement().  A different iframe creation
1048
		// pattern is required for IE.
1049
		var frameId = 'yuiIO' + this._transaction_id;
1050
		var io;
1051
		if(window.ActiveXObject){
1052
			io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
1053

    
1054
			// IE will throw a security exception in an SSL environment if the
1055
			// iframe source is undefined.
1056
			if(typeof secureUri == 'boolean'){
1057
				io.src = 'javascript:false';
1058
			}
1059
			else if(typeof secureURI == 'string'){
1060
				// Deprecated
1061
				io.src = secureUri;
1062
			}
1063
		}
1064
		else{
1065
			io = document.createElement('iframe');
1066
			io.id = frameId;
1067
			io.name = frameId;
1068
		}
1069

    
1070
		io.style.position = 'absolute';
1071
		io.style.top = '-1000px';
1072
		io.style.left = '-1000px';
1073

    
1074
		document.body.appendChild(io);
1075
	},
1076

    
1077
  /**
1078
   * @description Parses the POST data and creates hidden form elements
1079
   * for each key-value, and appends them to the HTML form object.
1080
   * @method appendPostData
1081
   * @private
1082
   * @static
1083
   * @param {string} postData The HTTP POST data
1084
   * @return {array} formElements Collection of hidden fields.
1085
   */
1086
	appendPostData:function(postData)
1087
	{
1088
		var formElements = [];
1089
		var postMessage = postData.split('&');
1090
		for(var i=0; i < postMessage.length; i++){
1091
			var delimitPos = postMessage[i].indexOf('=');
1092
			if(delimitPos != -1){
1093
				formElements[i] = document.createElement('input');
1094
				formElements[i].type = 'hidden';
1095
				formElements[i].name = postMessage[i].substring(0,delimitPos);
1096
				formElements[i].value = postMessage[i].substring(delimitPos+1);
1097
				this._formNode.appendChild(formElements[i]);
1098
			}
1099
		}
1100

    
1101
		return formElements;
1102
	},
1103

    
1104
  /**
1105
   * @description Uploads HTML form, inclusive of files/attachments, using the
1106
   * iframe created in createFrame to facilitate the transaction.
1107
   * @method uploadFile
1108
   * @private
1109
   * @static
1110
   * @param {int} id The transaction id.
1111
   * @param {object} callback User-defined callback object.
1112
   * @param {string} uri Fully qualified path of resource.
1113
   * @param {string} postData POST data to be submitted in addition to HTML form.
1114
   * @return {void}
1115
   */
1116
	uploadFile:function(o, callback, uri, postData){
1117

    
1118
		// Each iframe has an id prefix of "yuiIO" followed
1119
		// by the unique transaction id.
1120
		var oConn = this;
1121
		var frameId = 'yuiIO' + o.tId;
1122
		var uploadEncoding = 'multipart/form-data';
1123
		var io = document.getElementById(frameId);
1124
		var args = (callback && callback.argument)?callback.argument:null;
1125

    
1126
		// Track original HTML form attribute values.
1127
		var rawFormAttributes =
1128
		{
1129
			action:this._formNode.getAttribute('action'),
1130
			method:this._formNode.getAttribute('method'),
1131
			target:this._formNode.getAttribute('target')
1132
		};
1133

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

    
1140
		if(this._formNode.encoding){
1141
			// IE does not respect property enctype for HTML forms.
1142
			// Instead it uses the property - "encoding".
1143
			this._formNode.setAttribute('encoding', uploadEncoding);
1144
		}
1145
		else{
1146
			this._formNode.setAttribute('enctype', uploadEncoding);
1147
		}
1148

    
1149
		if(postData){
1150
			var oElements = this.appendPostData(postData);
1151
		}
1152

    
1153
		// Start file upload.
1154
		this._formNode.submit();
1155

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

    
1159
		if(o.startEvent){
1160
			// Fire transaction custom event -- startEvent
1161
			o.startEvent.fire(o, args);
1162
		}
1163

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

    
1170
		// Remove HTML elements created by appendPostData
1171
		if(oElements && oElements.length > 0){
1172
			for(var i=0; i < oElements.length; i++){
1173
				this._formNode.removeChild(oElements[i]);
1174
			}
1175
		}
1176

    
1177
		// Restore HTML form attributes to their original
1178
		// values prior to file upload.
1179
		for(var prop in rawFormAttributes){
1180
			if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
1181
				if(rawFormAttributes[prop]){
1182
					this._formNode.setAttribute(prop, rawFormAttributes[prop]);
1183
				}
1184
				else{
1185
					this._formNode.removeAttribute(prop);
1186
				}
1187
			}
1188
		}
1189

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

    
1193
		// Create the upload callback handler that fires when the iframe
1194
		// receives the load event.  Subsequently, the event handler is detached
1195
		// and the iframe removed from the document.
1196
		var uploadCallback = function()
1197
		{
1198
			if(callback && callback.timeout){
1199
				window.clearTimeout(oConn._timeOut[o.tId]);
1200
				delete oConn._timeOut[o.tId];
1201
			}
1202

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

    
1206
			if(o.completeEvent){
1207
				// Fire transaction custom event -- completeEvent
1208
				o.completeEvent.fire(o, args);
1209
			}
1210

    
1211
			var obj = {};
1212
			obj.tId = o.tId;
1213
			obj.argument = callback.argument;
1214

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

    
1224
			if(callback && callback.upload){
1225
				if(!callback.scope){
1226
					callback.upload(obj);
1227
				}
1228
				else{
1229
					callback.upload.apply(callback.scope, [obj]);
1230
				}
1231
			}
1232

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

    
1236
			if(o.uploadEvent){
1237
				// Fire transaction custom event -- uploadEvent
1238
				o.uploadEvent.fire(obj);
1239
			}
1240

    
1241
			YAHOO.util.Event.removeListener(io, "load", uploadCallback);
1242

    
1243
			setTimeout(
1244
				function(){
1245
					document.body.removeChild(io);
1246
					oConn.releaseObject(o);
1247
				}, 100);
1248
		};
1249

    
1250
		// Bind the onload handler to the iframe to detect the file upload response.
1251
		YAHOO.util.Event.addListener(io, "load", uploadCallback);
1252
	},
1253

    
1254
  /**
1255
   * @description Method to terminate a transaction, if it has not reached readyState 4.
1256
   * @method abort
1257
   * @public
1258
   * @static
1259
   * @param {object} o The connection object returned by asyncRequest.
1260
   * @param {object} callback  User-defined callback object.
1261
   * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
1262
   * @return {boolean}
1263
   */
1264
	abort:function(o, callback, isTimeout)
1265
	{
1266
		var abortStatus;
1267
		var args = (callback && callback.argument)?callback.argument:null;
1268

    
1269

    
1270
		if(o && o.conn){
1271
			if(this.isCallInProgress(o)){
1272
				// Issue abort request
1273
				o.conn.abort();
1274

    
1275
				window.clearInterval(this._poll[o.tId]);
1276
				delete this._poll[o.tId];
1277

    
1278
				if(isTimeout){
1279
					window.clearTimeout(this._timeOut[o.tId]);
1280
					delete this._timeOut[o.tId];
1281
				}
1282

    
1283
				abortStatus = true;
1284
			}
1285
		}
1286
		else if(o && o.isUpload === true){
1287
			var frameId = 'yuiIO' + o.tId;
1288
			var io = document.getElementById(frameId);
1289

    
1290
			if(io){
1291
				// Remove all listeners on the iframe prior to
1292
				// its destruction.
1293
				YAHOO.util.Event.removeListener(io, "load");
1294
				// Destroy the iframe facilitating the transaction.
1295
				document.body.removeChild(io);
1296

    
1297
				if(isTimeout){
1298
					window.clearTimeout(this._timeOut[o.tId]);
1299
					delete this._timeOut[o.tId];
1300
				}
1301

    
1302
				abortStatus = true;
1303
			}
1304
		}
1305
		else{
1306
			abortStatus = false;
1307
		}
1308

    
1309
		if(abortStatus === true){
1310
			// Fire global custom event -- abortEvent
1311
			this.abortEvent.fire(o, args);
1312

    
1313
			if(o.abortEvent){
1314
				// Fire transaction custom event -- abortEvent
1315
				o.abortEvent.fire(o, args);
1316
			}
1317

    
1318
			this.handleTransactionResponse(o, callback, true);
1319
		}
1320

    
1321
		return abortStatus;
1322
	},
1323

    
1324
  /**
1325
   * @description Determines if the transaction is still being processed.
1326
   * @method isCallInProgress
1327
   * @public
1328
   * @static
1329
   * @param {object} o The connection object returned by asyncRequest
1330
   * @return {boolean}
1331
   */
1332
	isCallInProgress:function(o)
1333
	{
1334
		// if the XHR object assigned to the transaction has not been dereferenced,
1335
		// then check its readyState status.  Otherwise, return false.
1336
		if(o && o.conn){
1337
			return o.conn.readyState !== 4 && o.conn.readyState !== 0;
1338
		}
1339
		else if(o && o.isUpload === true){
1340
			var frameId = 'yuiIO' + o.tId;
1341
			return document.getElementById(frameId)?true:false;
1342
		}
1343
		else{
1344
			return false;
1345
		}
1346
	},
1347

    
1348
  /**
1349
   * @description Dereference the XHR instance and the connection object after the transaction is completed.
1350
   * @method releaseObject
1351
   * @private
1352
   * @static
1353
   * @param {object} o The connection object
1354
   * @return {void}
1355
   */
1356
	releaseObject:function(o)
1357
	{
1358
		if(o && o.conn){
1359
			//dereference the XHR instance.
1360
			o.conn = null;
1361

    
1362

    
1363
			//dereference the connection object.
1364
			o = null;
1365
		}
1366
	}
1367
};
1368

    
1369
YAHOO.register("connection", YAHOO.util.Connect, {version: "2.4.1", build: "742"});
(4-4/4)