Project

General

Profile

1
/*
2
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
5
version: 2.8.0r4
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 Collection of polling references to the polling mechanism in handleReadyState.
135
 * @property _poll
136
 * @private
137
 * @static
138
 * @type object
139
  */
140
    _poll:{},
141

    
142
 /**
143
 * @description Queue of timeout values for each transaction callback with a defined timeout value.
144
 * @property _timeOut
145
 * @private
146
 * @static
147
 * @type object
148
  */
149
    _timeOut:{},
150

    
151
  /**
152
  * @description The polling frequency, in milliseconds, for HandleReadyState.
153
   * when attempting to determine a transaction's XHR readyState.
154
   * The default is 50 milliseconds.
155
  * @property _polling_interval
156
  * @private
157
  * @static
158
  * @type int
159
   */
160
     _polling_interval:50,
161

    
162
  /**
163
  * @description A transaction counter that increments the transaction id for each transaction.
164
  * @property _transaction_id
165
  * @private
166
  * @static
167
  * @type int
168
   */
169
     _transaction_id:0,
170

    
171
  /**
172
  * @description Custom event that fires at the start of a transaction
173
  * @property startEvent
174
  * @private
175
  * @static
176
  * @type CustomEvent
177
   */
178
    startEvent: new YAHOO.util.CustomEvent('start'),
179

    
180
  /**
181
  * @description Custom event that fires when a transaction response has completed.
182
  * @property completeEvent
183
  * @private
184
  * @static
185
  * @type CustomEvent
186
   */
187
    completeEvent: new YAHOO.util.CustomEvent('complete'),
188

    
189
  /**
190
  * @description Custom event that fires when handleTransactionResponse() determines a
191
   * response in the HTTP 2xx range.
192
  * @property successEvent
193
  * @private
194
  * @static
195
  * @type CustomEvent
196
   */
197
    successEvent: new YAHOO.util.CustomEvent('success'),
198

    
199
  /**
200
  * @description Custom event that fires when handleTransactionResponse() determines a
201
   * response in the HTTP 4xx/5xx range.
202
  * @property failureEvent
203
  * @private
204
  * @static
205
  * @type CustomEvent
206
   */
207
    failureEvent: new YAHOO.util.CustomEvent('failure'),
208

    
209
  /**
210
  * @description Custom event that fires when a transaction is successfully aborted.
211
  * @property abortEvent
212
  * @private
213
  * @static
214
  * @type CustomEvent
215
   */
216
    abortEvent: new YAHOO.util.CustomEvent('abort'),
217

    
218
  /**
219
  * @description A reference table that maps callback custom events members to its specific
220
   * event name.
221
  * @property _customEvents
222
  * @private
223
  * @static
224
  * @type object
225
   */
226
    _customEvents:
227
    {
228
        onStart:['startEvent', 'start'],
229
        onComplete:['completeEvent', 'complete'],
230
        onSuccess:['successEvent', 'success'],
231
        onFailure:['failureEvent', 'failure'],
232
        onUpload:['uploadEvent', 'upload'],
233
        onAbort:['abortEvent', 'abort']
234
    },
235

    
236
  /**
237
  * @description Member to add an ActiveX id to the existing xml_progid array.
238
   * In the event(unlikely) a new ActiveX id is introduced, it can be added
239
   * without internal code modifications.
240
  * @method setProgId
241
  * @public
242
  * @static
243
  * @param {string} id The ActiveX id to be added to initialize the XHR object.
244
  * @return void
245
   */
246
    setProgId:function(id)
247
    {
248
        this._msxml_progid.unshift(id);
249
        YAHOO.log('ActiveX Program Id  ' + id + ' added to _msxml_progid.', 'info', 'Connection');
250
    },
251

    
252
  /**
253
  * @description Member to override the default POST header.
254
  * @method setDefaultPostHeader
255
  * @public
256
  * @static
257
  * @param {boolean} b Set and use default header - true or false .
258
  * @return void
259
   */
260
    setDefaultPostHeader:function(b)
261
    {
262
        if(typeof b == 'string'){
263
            this._default_post_header = b;
264
            YAHOO.log('Default POST header set to  ' + b, 'info', 'Connection');
265
        }
266
        else if(typeof b == 'boolean'){
267
            this._use_default_post_header = b;
268
        }
269
    },
270

    
271
  /**
272
  * @description Member to override the default transaction header..
273
  * @method setDefaultXhrHeader
274
  * @public
275
  * @static
276
  * @param {boolean} b Set and use default header - true or false .
277
  * @return void
278
   */
279
    setDefaultXhrHeader:function(b)
280
    {
281
        if(typeof b == 'string'){
282
            this._default_xhr_header = b;
283
            YAHOO.log('Default XHR header set to  ' + b, 'info', 'Connection');
284
        }
285
        else{
286
            this._use_default_xhr_header = b;
287
        }
288
    },
289

    
290
  /**
291
  * @description Member to modify the default polling interval.
292
  * @method setPollingInterval
293
  * @public
294
  * @static
295
  * @param {int} i The polling interval in milliseconds.
296
  * @return void
297
   */
298
    setPollingInterval:function(i)
299
    {
300
        if(typeof i == 'number' && isFinite(i)){
301
            this._polling_interval = i;
302
            YAHOO.log('Default polling interval set to ' + i +'ms', 'info', 'Connection');
303
        }
304
    },
305

    
306
  /**
307
  * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
308
   * the XMLHttpRequest instance and the transaction id.
309
  * @method createXhrObject
310
  * @private
311
  * @static
312
  * @param {int} transactionId Property containing the transaction id for this transaction.
313
  * @return object
314
   */
315
    createXhrObject:function(transactionId)
316
    {
317
        var obj,http,i;
318
        try
319
        {
320
            // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
321
            http = new XMLHttpRequest();
322
            //  Object literal with http and tId properties
323
            obj = { conn:http, tId:transactionId, xhr: true };
324
            YAHOO.log('XHR object created for transaction ' + transactionId, 'info', 'Connection');
325
        }
326
        catch(e)
327
        {
328
            for(i=0; i<this._msxml_progid.length; ++i){
329
                try
330
                {
331
                    // Instantiates XMLHttpRequest for IE and assign to http
332
                    http = new ActiveXObject(this._msxml_progid[i]);
333
                    //  Object literal with conn and tId properties
334
                    obj = { conn:http, tId:transactionId, xhr: true };
335
                    YAHOO.log('ActiveX XHR object created for transaction ' + transactionId, 'info', 'Connection');
336
                    break;
337
                }
338
                catch(e1){}
339
            }
340
        }
341
        finally
342
        {
343
            return obj;
344
        }
345
    },
346

    
347
  /**
348
  * @description This method is called by asyncRequest to create a
349
   * valid connection object for the transaction.  It also passes a
350
   * transaction id and increments the transaction id counter.
351
  * @method getConnectionObject
352
  * @private
353
  * @static
354
  * @return {object}
355
   */
356
    getConnectionObject:function(t)
357
    {
358
        var o, tId = this._transaction_id;
359

    
360
        try
361
        {
362
            if(!t){
363
                o = this.createXhrObject(tId);
364
            }
365
            else{
366
                o = {tId:tId};
367
                if(t==='xdr'){
368
                    o.conn = this._transport;
369
                    o.xdr = true;
370
                }
371
                else if(t==='upload'){
372
                    o.upload = true;
373
                }
374
            }
375

    
376
            if(o){
377
                this._transaction_id++;
378
            }
379
        }
380
        catch(e){}
381
        return o;
382
    },
383

    
384
  /**
385
  * @description Method for initiating an asynchronous request via the XHR object.
386
  * @method asyncRequest
387
  * @public
388
  * @static
389
  * @param {string} method HTTP transaction method
390
  * @param {string} uri Fully qualified path of resource
391
  * @param {callback} callback User-defined callback function or object
392
  * @param {string} postData POST body
393
  * @return {object} Returns the connection object
394
   */
395
    asyncRequest:function(method, uri, callback, postData)
396
    {
397
        var o,t,args = (callback && callback.argument)?callback.argument:null;
398

    
399
        if(this._isFileUpload){
400
            t = 'upload';
401
        }
402
        else if(callback.xdr){
403
            t = 'xdr';
404
        }
405

    
406
        o = this.getConnectionObject(t);
407
        if(!o){
408
            YAHOO.log('Unable to create connection object.', 'error', 'Connection');
409
            return null;
410
        }
411
        else{
412

    
413
            // Intialize any transaction-specific custom events, if provided.
414
            if(callback && callback.customevents){
415
                this.initCustomEvents(o, callback);
416
            }
417

    
418
            if(this._isFormSubmit){
419
                if(this._isFileUpload){
420
                    this.uploadFile(o, callback, uri, postData);
421
                    return o;
422
                }
423

    
424
                // If the specified HTTP method is GET, setForm() will return an
425
                // encoded string that is concatenated to the uri to
426
                // create a querystring.
427
                if(method.toUpperCase() == 'GET'){
428
                    if(this._sFormData.length !== 0){
429
                        // If the URI already contains a querystring, append an ampersand
430
                        // and then concatenate _sFormData to the URI.
431
                        uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
432
                    }
433
                }
434
                else if(method.toUpperCase() == 'POST'){
435
                    // If POST data exist in addition to the HTML form data,
436
                    // it will be concatenated to the form data.
437
                    postData = postData?this._sFormData + "&" + postData:this._sFormData;
438
                }
439
            }
440

    
441
            if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
442
                // If callback.cache is defined and set to false, a
443
                // timestamp value will be added to the querystring.
444
                uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
445
            }
446

    
447
            // Each transaction will automatically include a custom header of
448
            // "X-Requested-With: XMLHttpRequest" to identify the request as
449
            // having originated from Connection Manager.
450
            if(this._use_default_xhr_header){
451
                if(!this._default_headers['X-Requested-With']){
452
                    this.initHeader('X-Requested-With', this._default_xhr_header, true);
453
                    YAHOO.log('Initialize transaction header X-Request-Header to XMLHttpRequest.', 'info', 'Connection');
454
                }
455
            }
456

    
457
            //If the transaction method is POST and the POST header value is set to true
458
            //or a custom value, initalize the Content-Type header to this value.
459
            if((method.toUpperCase() === 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
460
                this.initHeader('Content-Type', this._default_post_header);
461
                YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded; UTF-8 for POST transaction.', 'info', 'Connection');
462
            }
463

    
464
            if(o.xdr){
465
                this.xdr(o, method, uri, callback, postData);
466
                return o;
467
            }
468

    
469
            o.conn.open(method, uri, true);
470
            //Initialize all default and custom HTTP headers,
471
            if(this._has_default_headers || this._has_http_headers){
472
                this.setHeader(o);
473
            }
474

    
475
            this.handleReadyState(o, callback);
476
            o.conn.send(postData || '');
477
            YAHOO.log('Transaction ' + o.tId + ' sent.', 'info', 'Connection');
478

    
479
            // Reset the HTML form data and state properties as
480
            // soon as the data are submitted.
481
            if(this._isFormSubmit === true){
482
                this.resetFormState();
483
            }
484

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

    
488
            if(o.startEvent){
489
                // Fire transaction custom event -- startEvent
490
                o.startEvent.fire(o, args);
491
            }
492

    
493
            return o;
494
        }
495
    },
496

    
497
  /**
498
  * @description This method creates and subscribes custom events,
499
   * specific to each transaction
500
  * @method initCustomEvents
501
  * @private
502
  * @static
503
  * @param {object} o The connection object
504
  * @param {callback} callback The user-defined callback object
505
  * @return {void}
506
   */
507
    initCustomEvents:function(o, callback)
508
    {
509
        var prop;
510
        // Enumerate through callback.customevents members and bind/subscribe
511
        // events that match in the _customEvents table.
512
        for(prop in callback.customevents){
513
            if(this._customEvents[prop][0]){
514
                // Create the custom event
515
                o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
516
                YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' created.', 'info', 'Connection');
517

    
518
                // Subscribe the custom event
519
                o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
520
                YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' subscribed.', 'info', 'Connection');
521
            }
522
        }
523
    },
524

    
525
  /**
526
  * @description This method serves as a timer that polls the XHR object's readyState
527
   * property during a transaction, instead of binding a callback to the
528
   * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
529
   * will process the response, and the timer will be cleared.
530
  * @method handleReadyState
531
  * @private
532
  * @static
533
  * @param {object} o The connection object
534
  * @param {callback} callback The user-defined callback object
535
  * @return {void}
536
   */
537

    
538
    handleReadyState:function(o, callback)
539

    
540
    {
541
        var oConn = this,
542
            args = (callback && callback.argument)?callback.argument:null;
543

    
544
        if(callback && callback.timeout){
545
            this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
546
        }
547

    
548
        this._poll[o.tId] = window.setInterval(
549
            function(){
550
                if(o.conn && o.conn.readyState === 4){
551

    
552
                    // Clear the polling interval for the transaction
553
                    // and remove the reference from _poll.
554
                    window.clearInterval(oConn._poll[o.tId]);
555
                    delete oConn._poll[o.tId];
556

    
557
                    if(callback && callback.timeout){
558
                        window.clearTimeout(oConn._timeOut[o.tId]);
559
                        delete oConn._timeOut[o.tId];
560
                    }
561

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

    
565
                    if(o.completeEvent){
566
                        // Fire transaction custom event -- completeEvent
567
                        o.completeEvent.fire(o, args);
568
                    }
569

    
570
                    oConn.handleTransactionResponse(o, callback);
571
                }
572
            }
573
        ,this._polling_interval);
574
    },
575

    
576
  /**
577
  * @description This method attempts to interpret the server response and
578
   * determine whether the transaction was successful, or if an error or
579
   * exception was encountered.
580
  * @method handleTransactionResponse
581
  * @private
582
  * @static
583
  * @param {object} o The connection object
584
  * @param {object} callback The user-defined callback object
585
  * @param {boolean} isAbort Determines if the transaction was terminated via abort().
586
  * @return {void}
587
   */
588
    handleTransactionResponse:function(o, callback, isAbort)
589
    {
590
        var httpStatus, responseObject,
591
            args = (callback && callback.argument)?callback.argument:null,
592
            xdrS = (o.r && o.r.statusText === 'xdr:success')?true:false,
593
            xdrF = (o.r && o.r.statusText === 'xdr:failure')?true:false,
594
            xdrA = isAbort;
595

    
596
        try
597
        {
598
            if((o.conn.status !== undefined && o.conn.status !== 0) || xdrS){
599
                // XDR requests will not have HTTP status defined. The
600
                // statusText property will define the response status
601
                // set by the Flash transport.
602
                httpStatus = o.conn.status;
603
            }
604
            else if(xdrF && !xdrA){
605
                // Set XDR transaction failure to a status of 0, which
606
                // resolves as an HTTP failure, instead of an exception.
607
                httpStatus = 0;
608
            }
609
            else{
610
                httpStatus = 13030;
611
            }
612
        }
613
        catch(e){
614

    
615
             // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
616
             // when the XHR object's status and statusText properties are
617
             // unavailable, and a query attempt throws an exception.
618
            httpStatus = 13030;
619
        }
620

    
621
        if((httpStatus >= 200 && httpStatus < 300) || httpStatus === 1223 || xdrS){
622
            responseObject = o.xdr ? o.r : this.createResponseObject(o, args);
623
            if(callback && callback.success){
624
                if(!callback.scope){
625
                    callback.success(responseObject);
626
                    YAHOO.log('Success callback. HTTP code is ' + httpStatus, 'info', 'Connection');
627
                }
628
                else{
629
                    // If a scope property is defined, the callback will be fired from
630
                    // the context of the object.
631
                    callback.success.apply(callback.scope, [responseObject]);
632
                    YAHOO.log('Success callback with scope. HTTP code is ' + httpStatus, 'info', 'Connection');
633
                }
634
            }
635

    
636
            // Fire global custom event -- successEvent
637
            this.successEvent.fire(responseObject);
638

    
639
            if(o.successEvent){
640
                // Fire transaction custom event -- successEvent
641
                o.successEvent.fire(responseObject);
642
            }
643
        }
644
        else{
645
            switch(httpStatus){
646
                // The following cases are wininet.dll error codes that may be encountered.
647
                case 12002: // Server timeout
648
                case 12029: // 12029 to 12031 correspond to dropped connections.
649
                case 12030:
650
                case 12031:
651
                case 12152: // Connection closed by server.
652
                case 13030: // See above comments for variable status.
653
                    // XDR transactions will not resolve to this case, since the
654
                    // response object is already built in the xdr response.
655
                    responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
656
                    if(callback && callback.failure){
657
                        if(!callback.scope){
658
                            callback.failure(responseObject);
659
                            YAHOO.log('Failure callback. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection');
660
                        }
661
                        else{
662
                            callback.failure.apply(callback.scope, [responseObject]);
663
                            YAHOO.log('Failure callback with scope. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection');
664
                        }
665
                    }
666

    
667
                    break;
668
                default:
669
                    responseObject = (o.xdr) ? o.response : this.createResponseObject(o, args);
670
                    if(callback && callback.failure){
671
                        if(!callback.scope){
672
                            callback.failure(responseObject);
673
                            YAHOO.log('Failure callback. HTTP status code is ' + httpStatus, 'warn', 'Connection');
674
                        }
675
                        else{
676
                            callback.failure.apply(callback.scope, [responseObject]);
677
                            YAHOO.log('Failure callback with scope. HTTP status code is ' + httpStatus, 'warn', 'Connection');
678
                        }
679
                    }
680
            }
681

    
682
            // Fire global custom event -- failureEvent
683
            this.failureEvent.fire(responseObject);
684

    
685
            if(o.failureEvent){
686
                // Fire transaction custom event -- failureEvent
687
                o.failureEvent.fire(responseObject);
688
            }
689

    
690
        }
691

    
692
        this.releaseObject(o);
693
        responseObject = null;
694
    },
695

    
696
  /**
697
  * @description This method evaluates the server response, creates and returns the results via
698
   * its properties.  Success and failure cases will differ in the response
699
   * object's property values.
700
  * @method createResponseObject
701
  * @private
702
  * @static
703
  * @param {object} o The connection object
704
  * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
705
  * @return {object}
706
   */
707
    createResponseObject:function(o, callbackArg)
708
    {
709
        var obj = {}, headerObj = {},
710
            i, headerStr, header, delimitPos;
711

    
712
        try
713
        {
714
            headerStr = o.conn.getAllResponseHeaders();
715
            header = headerStr.split('\n');
716
            for(i=0; i<header.length; i++){
717
                delimitPos = header[i].indexOf(':');
718
                if(delimitPos != -1){
719
                    headerObj[header[i].substring(0,delimitPos)] = YAHOO.lang.trim(header[i].substring(delimitPos+2));
720
                }
721
            }
722
        }
723
        catch(e){}
724

    
725
        obj.tId = o.tId;
726
        // Normalize IE's response to HTTP 204 when Win error 1223.
727
        obj.status = (o.conn.status == 1223)?204:o.conn.status;
728
        // Normalize IE's statusText to "No Content" instead of "Unknown".
729
        obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
730
        obj.getResponseHeader = headerObj;
731
        obj.getAllResponseHeaders = headerStr;
732
        obj.responseText = o.conn.responseText;
733
        obj.responseXML = o.conn.responseXML;
734

    
735
        if(callbackArg){
736
            obj.argument = callbackArg;
737
        }
738

    
739
        return obj;
740
    },
741

    
742
  /**
743
  * @description If a transaction cannot be completed due to dropped or closed connections,
744
   * there may be not be enough information to build a full response object.
745
   * The failure callback will be fired and this specific condition can be identified
746
   * by a status property value of 0.
747
   *
748
   * If an abort was successful, the status property will report a value of -1.
749
   *
750
  * @method createExceptionObject
751
  * @private
752
  * @static
753
  * @param {int} tId The Transaction Id
754
  * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
755
  * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
756
  * @return {object}
757
   */
758
    createExceptionObject:function(tId, callbackArg, isAbort)
759
    {
760
        var COMM_CODE = 0,
761
            COMM_ERROR = 'communication failure',
762
            ABORT_CODE = -1,
763
            ABORT_ERROR = 'transaction aborted',
764
            obj = {};
765

    
766
        obj.tId = tId;
767
        if(isAbort){
768
            obj.status = ABORT_CODE;
769
            obj.statusText = ABORT_ERROR;
770
        }
771
        else{
772
            obj.status = COMM_CODE;
773
            obj.statusText = COMM_ERROR;
774
        }
775

    
776
        if(callbackArg){
777
            obj.argument = callbackArg;
778
        }
779

    
780
        return obj;
781
    },
782

    
783
  /**
784
  * @description Method that initializes the custom HTTP headers for the each transaction.
785
  * @method initHeader
786
  * @public
787
  * @static
788
  * @param {string} label The HTTP header label
789
  * @param {string} value The HTTP header value
790
  * @param {string} isDefault Determines if the specific header is a default header
791
   * automatically sent with each transaction.
792
  * @return {void}
793
   */
794
    initHeader:function(label, value, isDefault)
795
    {
796
        var headerObj = (isDefault)?this._default_headers:this._http_headers;
797

    
798
        headerObj[label] = value;
799
        if(isDefault){
800
            this._has_default_headers = true;
801
        }
802
        else{
803
            this._has_http_headers = true;
804
        }
805
    },
806

    
807

    
808
  /**
809
  * @description Accessor that sets the HTTP headers for each transaction.
810
  * @method setHeader
811
  * @private
812
  * @static
813
  * @param {object} o The connection object for the transaction.
814
  * @return {void}
815
   */
816
    setHeader:function(o)
817
    {
818
        var prop;
819
        if(this._has_default_headers){
820
            for(prop in this._default_headers){
821
                if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
822
                    o.conn.setRequestHeader(prop, this._default_headers[prop]);
823
                    YAHOO.log('Default HTTP header ' + prop + ' set with value of ' + this._default_headers[prop], 'info', 'Connection');
824
                }
825
            }
826
        }
827

    
828
        if(this._has_http_headers){
829
            for(prop in this._http_headers){
830
                if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
831
                    o.conn.setRequestHeader(prop, this._http_headers[prop]);
832
                    YAHOO.log('HTTP header ' + prop + ' set with value of ' + this._http_headers[prop], 'info', 'Connection');
833
                }
834
            }
835

    
836
            this._http_headers = {};
837
            this._has_http_headers = false;
838
        }
839
    },
840

    
841
  /**
842
  * @description Resets the default HTTP headers object
843
  * @method resetDefaultHeaders
844
  * @public
845
  * @static
846
  * @return {void}
847
   */
848
    resetDefaultHeaders:function(){
849
        this._default_headers = {};
850
        this._has_default_headers = false;
851
    },
852

    
853
  /**
854
  * @description Method to terminate a transaction, if it has not reached readyState 4.
855
  * @method abort
856
  * @public
857
  * @static
858
  * @param {object} o The connection object returned by asyncRequest.
859
  * @param {object} callback  User-defined callback object.
860
  * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
861
  * @return {boolean}
862
   */
863
    abort:function(o, callback, isTimeout)
864
    {
865
        var abortStatus,
866
            args = (callback && callback.argument)?callback.argument:null;
867
            o = o || {};
868

    
869
        if(o.conn){
870
            if(o.xhr){
871
                if(this.isCallInProgress(o)){
872
                    // Issue abort request
873
                    o.conn.abort();
874

    
875
                    window.clearInterval(this._poll[o.tId]);
876
                    delete this._poll[o.tId];
877

    
878
                    if(isTimeout){
879
                        window.clearTimeout(this._timeOut[o.tId]);
880
                        delete this._timeOut[o.tId];
881
                    }
882

    
883
                    abortStatus = true;
884
                }
885
            }
886
            else if(o.xdr){
887
                o.conn.abort(o.tId);
888
                abortStatus = true;
889
            }
890
        }
891
        else if(o.upload){
892
            var frameId = 'yuiIO' + o.tId;
893
            var io = document.getElementById(frameId);
894

    
895
            if(io){
896
                // Remove all listeners on the iframe prior to
897
                // its destruction.
898
                YAHOO.util.Event.removeListener(io, "load");
899
                // Destroy the iframe facilitating the transaction.
900
                document.body.removeChild(io);
901
                YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
902

    
903
                if(isTimeout){
904
                    window.clearTimeout(this._timeOut[o.tId]);
905
                    delete this._timeOut[o.tId];
906
                }
907

    
908
                abortStatus = true;
909
            }
910
        }
911
        else{
912
            abortStatus = false;
913
        }
914

    
915
        if(abortStatus === true){
916
            // Fire global custom event -- abortEvent
917
            this.abortEvent.fire(o, args);
918

    
919
            if(o.abortEvent){
920
                // Fire transaction custom event -- abortEvent
921
                o.abortEvent.fire(o, args);
922
            }
923

    
924
            this.handleTransactionResponse(o, callback, true);
925
            YAHOO.log('Transaction ' + o.tId + ' aborted.', 'info', 'Connection');
926
        }
927

    
928
        return abortStatus;
929
    },
930

    
931
  /**
932
  * @description Determines if the transaction is still being processed.
933
  * @method isCallInProgress
934
  * @public
935
  * @static
936
  * @param {object} o The connection object returned by asyncRequest
937
  * @return {boolean}
938
   */
939
    isCallInProgress:function(o)
940
    {
941
        o = o || {};
942
        // if the XHR object assigned to the transaction has not been dereferenced,
943
        // then check its readyState status.  Otherwise, return false.
944
        if(o.xhr && o.conn){
945
            return o.conn.readyState !== 4 && o.conn.readyState !== 0;
946
        }
947
        else if(o.xdr && o.conn){
948
            return o.conn.isCallInProgress(o.tId);
949
        }
950
        else if(o.upload === true){
951
            return document.getElementById('yuiIO' + o.tId)?true:false;
952
        }
953
        else{
954
            return false;
955
        }
956
    },
957

    
958
  /**
959
  * @description Dereference the XHR instance and the connection object after the transaction is completed.
960
  * @method releaseObject
961
  * @private
962
  * @static
963
  * @param {object} o The connection object
964
  * @return {void}
965
   */
966
    releaseObject:function(o)
967
    {
968
        if(o && o.conn){
969
            //dereference the XHR instance.
970
            o.conn = null;
971

    
972
            YAHOO.log('Connection object for transaction ' + o.tId + ' destroyed.', 'info', 'Connection');
973

    
974
            //dereference the connection object.
975
            o = null;
976
        }
977
    }
978
};
979

    
980
/**
981
 * @for Connect
982
  */
983
(function() {
984
    var YCM = YAHOO.util.Connect, _fn = {};
985

    
986
   /**
987
   * @description This method creates and instantiates the Flash transport.
988
   * @method _swf
989
   * @private
990
   * @static
991
   * @param {string} URI to connection.swf.
992
   * @return {void}
993
    */
994
    function _swf(uri) {
995
        var o = '<object id="YUIConnectionSwf" type="application/x-shockwave-flash" data="' +
996
                uri + '" width="0" height="0">' +
997
                 '<param name="movie" value="' + uri + '">' +
998
                '<param name="allowScriptAccess" value="always">' +
999
                '</object>',
1000
            c = document.createElement('div');
1001

    
1002
        document.body.appendChild(c);
1003
        c.innerHTML = o;
1004
    }
1005

    
1006
   /**
1007
   * @description This method calls the public method on the
1008
    * Flash transport to start the XDR transaction.  It is analogous
1009
    * to Connection Manager's asyncRequest method.
1010
   * @method xdr
1011
   * @private
1012
   * @static
1013
   * @param {object} The transaction object.
1014
   * @param {string} HTTP request method.
1015
   * @param {string} URI for the transaction.
1016
   * @param {object} The transaction's callback object.
1017
   * @param {object} The JSON object used as HTTP POST data.
1018
   * @return {void}
1019
    */
1020
    function _xdr(o, m, u, c, d) {
1021
        _fn[parseInt(o.tId)] = { 'o':o, 'c':c };
1022
        if (d) {
1023
            c.method = m;
1024
            c.data = d;
1025
        }
1026

    
1027
        o.conn.send(u, c, o.tId);
1028
    }
1029

    
1030
   /**
1031
   * @description This method instantiates the Flash transport and
1032
    * establishes a static reference to it, used for all XDR requests.
1033
   * @method transport
1034
   * @public
1035
   * @static
1036
   * @param {string} URI to connection.swf.
1037
   * @return {void}
1038
    */
1039
    function _init(uri) {
1040
        _swf(uri);
1041
        YCM._transport = document.getElementById('YUIConnectionSwf');
1042
    }
1043

    
1044
    function _xdrReady() {
1045
        YCM.xdrReadyEvent.fire();
1046
    }
1047

    
1048
   /**
1049
   * @description This method fires the global and transaction start
1050
    * events.
1051
   * @method _xdrStart
1052
   * @private
1053
   * @static
1054
   * @param {object} The transaction object.
1055
   * @param {string} The transaction's callback object.
1056
   * @return {void}
1057
    */
1058
    function _xdrStart(o, cb) {
1059
        if (o) {
1060
            // Fire global custom event -- startEvent
1061
            YCM.startEvent.fire(o, cb.argument);
1062

    
1063
            if(o.startEvent){
1064
                // Fire transaction custom event -- startEvent
1065
                o.startEvent.fire(o, cb.argument);
1066
            }
1067
        }
1068
    }
1069

    
1070
   /**
1071
   * @description This method is the initial response handler
1072
    * for XDR transactions.  The Flash transport calls this
1073
    * function and sends the response payload.
1074
   * @method handleXdrResponse
1075
   * @private
1076
   * @static
1077
   * @param {object} The response object sent from the Flash transport.
1078
   * @return {void}
1079
    */
1080
    function _handleXdrResponse(r) {
1081
        var o = _fn[r.tId].o,
1082
            cb = _fn[r.tId].c;
1083

    
1084
        if (r.statusText === 'xdr:start') {
1085
            _xdrStart(o, cb);
1086
            return;
1087
        }
1088

    
1089
        r.responseText = decodeURI(r.responseText);
1090
        o.r = r;
1091
        if (cb.argument) {
1092
            o.r.argument = cb.argument;
1093
        }
1094

    
1095
        this.handleTransactionResponse(o, cb, r.statusText === 'xdr:abort' ? true : false);
1096
        delete _fn[r.tId];
1097
    }
1098

    
1099
    // Bind the functions to Connection Manager as static fields.
1100
    YCM.xdr = _xdr;
1101
    YCM.swf = _swf;
1102
    YCM.transport = _init;
1103
    YCM.xdrReadyEvent = new YAHOO.util.CustomEvent('xdrReady');
1104
    YCM.xdrReady = _xdrReady;
1105
    YCM.handleXdrResponse = _handleXdrResponse;
1106
})();
1107

    
1108
/**
1109
 * @for Connect
1110
  */
1111
(function(){
1112
    var YCM = YAHOO.util.Connect,
1113
        YE = YAHOO.util.Event;
1114
   /**
1115
    * @description Property modified by setForm() to determine if the data
1116
    * should be submitted as an HTML form.
1117
    * @property _isFormSubmit
1118
    * @private
1119
    * @static
1120
    * @type boolean
1121
    */
1122
    YCM._isFormSubmit = false;
1123

    
1124
   /**
1125
    * @description Property modified by setForm() to determine if a file(s)
1126
    * upload is expected.
1127
    * @property _isFileUpload
1128
    * @private
1129
    * @static
1130
    * @type boolean
1131
    */
1132
    YCM._isFileUpload = false;
1133

    
1134
   /**
1135
    * @description Property modified by setForm() to set a reference to the HTML
1136
    * form node if the desired action is file upload.
1137
    * @property _formNode
1138
    * @private
1139
    * @static
1140
    * @type object
1141
    */
1142
    YCM._formNode = null;
1143

    
1144
   /**
1145
    * @description Property modified by setForm() to set the HTML form data
1146
    * for each transaction.
1147
    * @property _sFormData
1148
    * @private
1149
    * @static
1150
    * @type string
1151
    */
1152
    YCM._sFormData = null;
1153

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

    
1164
   /**
1165
   * @description Custom event that fires when handleTransactionResponse() determines a
1166
    * response in the HTTP 4xx/5xx range.
1167
   * @property failureEvent
1168
   * @private
1169
   * @static
1170
   * @type CustomEvent
1171
    */
1172
    YCM.uploadEvent = new YAHOO.util.CustomEvent('upload'),
1173

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

    
1192
                    if((name === 'input' || name === 'button') && (obj.type && obj.type.toLowerCase() == 'submit')){
1193
                        YCM._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
1194
                    }
1195
                });
1196
            return true;
1197
        }
1198
        return false;
1199
    }();
1200

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

    
1221
        this.resetFormState();
1222

    
1223
        if(typeof formId == 'string'){
1224
            // Determine if the argument is a form id or a form name.
1225
            // Note form name usage is deprecated by supported
1226
            // here for legacy reasons.
1227
            oForm = (document.getElementById(formId) || document.forms[formId]);
1228
        }
1229
        else if(typeof formId == 'object'){
1230
            // Treat argument as an HTML form object.
1231
            oForm = formId;
1232
        }
1233
        else{
1234
            YAHOO.log('Unable to create form object ' + formId, 'warn', 'Connection');
1235
            return;
1236
        }
1237

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

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

    
1249
            // Set form reference and file upload properties to true.
1250
            this._isFormSubmit = true;
1251
            this._isFileUpload = true;
1252
            this._formNode = oForm;
1253

    
1254
            return;
1255
        }
1256

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

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

    
1271
                switch(oElement.type)
1272
                {
1273
                    // Safari, Opera, FF all default opt.value from .text if
1274
                    // value attribute not specified in markup
1275
                    case 'select-one':
1276
                        if (oElement.selectedIndex > -1) {
1277
                            opt = oElement.options[oElement.selectedIndex];
1278
                            data[item++] = oName + encodeURIComponent(
1279
                                (opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
1280
                        }
1281
                        break;
1282
                    case 'select-multiple':
1283
                        if (oElement.selectedIndex > -1) {
1284
                            for(j=oElement.selectedIndex, jlen=oElement.options.length; j<jlen; ++j){
1285
                                opt = oElement.options[j];
1286
                                if (opt.selected) {
1287
                                    data[item++] = oName + encodeURIComponent(
1288
                                        (opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
1289
                                }
1290
                            }
1291
                        }
1292
                        break;
1293
                    case 'radio':
1294
                    case 'checkbox':
1295
                        if(oElement.checked){
1296
                            data[item++] = oName + oValue;
1297
                        }
1298
                        break;
1299
                    case 'file':
1300
                        // stub case as XMLHttpRequest will only send the file path as a string.
1301
                    case undefined:
1302
                        // stub case for fieldset element which returns undefined.
1303
                    case 'reset':
1304
                        // stub case for input type reset button.
1305
                    case 'button':
1306
                        // stub case for input type button elements.
1307
                        break;
1308
                    case 'submit':
1309
                        if(hasSubmit === false){
1310
                            if(this._hasSubmitListener && this._submitElementValue){
1311
                                data[item++] = this._submitElementValue;
1312
                            }
1313
                            hasSubmit = true;
1314
                        }
1315
                        break;
1316
                    default:
1317
                        data[item++] = oName + oValue;
1318
                }
1319
            }
1320
        }
1321

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

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

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

    
1330
        return this._sFormData;
1331
    }
1332

    
1333
   /**
1334
   * @description Resets HTML form properties when an HTML form or HTML form
1335
    * with file upload transaction is sent.
1336
   * @method resetFormState
1337
   * @private
1338
   * @static
1339
   * @return {void}
1340
    */
1341
    function _resetFormState(){
1342
        this._isFormSubmit = false;
1343
        this._isFileUpload = false;
1344
        this._formNode = null;
1345
        this._sFormData = "";
1346
    }
1347

    
1348

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

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

    
1368
            // IE will throw a security exception in an SSL environment if the
1369
            // iframe source is undefined.
1370
            if(typeof secureUri == 'boolean'){
1371
                io.src = 'javascript:false';
1372
            }
1373
        }
1374
        else{
1375
            io = document.createElement('iframe');
1376
            io.id = frameId;
1377
            io.name = frameId;
1378
        }
1379

    
1380
        io.style.position = 'absolute';
1381
        io.style.top = '-1000px';
1382
        io.style.left = '-1000px';
1383

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

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

    
1402
        for(i=0; i < postMessage.length; i++){
1403
            delimitPos = postMessage[i].indexOf('=');
1404
            if(delimitPos != -1){
1405
                formElements[i] = document.createElement('input');
1406
                formElements[i].type = 'hidden';
1407
                formElements[i].name = decodeURIComponent(postMessage[i].substring(0,delimitPos));
1408
                formElements[i].value = decodeURIComponent(postMessage[i].substring(delimitPos+1));
1409
                this._formNode.appendChild(formElements[i]);
1410
            }
1411
        }
1412

    
1413
        return formElements;
1414
    }
1415

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

    
1439
        // Track original HTML form attribute values.
1440
        rawFormAttributes = {
1441
            action:this._formNode.getAttribute('action'),
1442
            method:this._formNode.getAttribute('method'),
1443
            target:this._formNode.getAttribute('target')
1444
        };
1445

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

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

    
1461
        if(postData){
1462
            oElements = this.appendPostData(postData);
1463
        }
1464

    
1465
        // Start file upload.
1466
        this._formNode.submit();
1467

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

    
1471
        if(o.startEvent){
1472
            // Fire transaction custom event -- startEvent
1473
            o.startEvent.fire(o, args);
1474
        }
1475

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

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

    
1489
        // Restore HTML form attributes to their original
1490
        // values prior to file upload.
1491
        for(prop in rawFormAttributes){
1492
            if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
1493
                if(rawFormAttributes[prop]){
1494
                    this._formNode.setAttribute(prop, rawFormAttributes[prop]);
1495
                }
1496
                else{
1497
                    this._formNode.removeAttribute(prop);
1498
                }
1499
            }
1500
        }
1501

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

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

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

    
1517
            if(o.completeEvent){
1518
                // Fire transaction custom event -- completeEvent
1519
                o.completeEvent.fire(o, args);
1520
            }
1521

    
1522
            obj = {
1523
                tId : o.tId,
1524
                argument : callback.argument
1525
            };
1526

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

    
1536
            if(callback && callback.upload){
1537
                if(!callback.scope){
1538
                    callback.upload(obj);
1539
                    YAHOO.log('Upload callback.', 'info', 'Connection');
1540
                }
1541
                else{
1542
                    callback.upload.apply(callback.scope, [obj]);
1543
                    YAHOO.log('Upload callback with scope.', 'info', 'Connection');
1544
                }
1545
            }
1546

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

    
1550
            if(o.uploadEvent){
1551
                // Fire transaction custom event -- uploadEvent
1552
                o.uploadEvent.fire(obj);
1553
            }
1554

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

    
1557
            setTimeout(
1558
                function(){
1559
                    document.body.removeChild(io);
1560
                    oConn.releaseObject(o);
1561
                    YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
1562
                }, 100);
1563
        };
1564

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

    
1569
    YCM.setForm = _setForm;
1570
    YCM.resetFormState = _resetFormState;
1571
    YCM.createFrame = _createFrame;
1572
    YCM.appendPostData = _appendPostData;
1573
    YCM.uploadFile = _uploadFile;
1574
})();
1575

    
1576
YAHOO.register("connection", YAHOO.util.Connect, {version: "2.8.0r4", build: "2449"});
(2-2/9)