Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('io-base', function (Y, NAME) {
2
 
3
/**
4
Base IO functionality. Provides basic XHR transport support.
5
 
6
@module io
7
@submodule io-base
8
@for IO
9
**/
10
 
11
var // List of events that comprise the IO event lifecycle.
12
    EVENTS = ['start', 'complete', 'end', 'success', 'failure', 'progress'],
13
 
14
    // Whitelist of used XHR response object properties.
15
    XHR_PROPS = ['status', 'statusText', 'responseText', 'responseXML'],
16
 
17
    win = Y.config.win,
18
    uid = 0;
19
 
20
/**
21
The IO class is a utility that brokers HTTP requests through a simplified
22
interface.  Specifically, it allows JavaScript to make HTTP requests to
23
a resource without a page reload.  The underlying transport for making
24
same-domain requests is the XMLHttpRequest object.  IO can also use
25
Flash, if specified as a transport, for cross-domain requests.
26
 
27
@class IO
28
@constructor
29
@param {Object} config Object of EventTarget's publish method configurations
30
                    used to configure IO's events.
31
 
32
IO can be called statically using {{#crossLink "YUI/io:method"}}YUI.io{{/crossLink}}.
33
**/
34
function IO (config) {
35
    var io = this;
36
 
37
    io._uid = 'io:' + uid++;
38
    io._init(config);
39
    Y.io._map[io._uid] = io;
40
}
41
 
42
IO.prototype = {
43
    //--------------------------------------
44
    //  Properties
45
    //--------------------------------------
46
 
47
   /**
48
    * A counter that increments for each transaction.
49
    *
50
    * @property _id
51
    * @private
52
    * @type {Number}
53
    */
54
    _id: 0,
55
 
56
   /**
57
    * Object of IO HTTP headers sent with each transaction.
58
    *
59
    * @property _headers
60
    * @private
61
    * @type {Object}
62
    */
63
    _headers: {
64
        'X-Requested-With' : 'XMLHttpRequest'
65
    },
66
 
67
   /**
68
    * Object that stores timeout values for any transaction with a defined
69
    * "timeout" configuration property.
70
    *
71
    * @property _timeout
72
    * @private
73
    * @type {Object}
74
    */
75
    _timeout: {},
76
 
77
    //--------------------------------------
78
    //  Methods
79
    //--------------------------------------
80
 
81
    _init: function(config) {
82
        var io = this, i, len;
83
 
84
        io.cfg = config || {};
85
 
86
        Y.augment(io, Y.EventTarget);
87
        for (i = 0, len = EVENTS.length; i < len; ++i) {
88
            // Publish IO global events with configurations, if any.
89
            // IO global events are set to broadcast by default.
90
            // These events use the "io:" namespace.
91
            io.publish('io:' + EVENTS[i], Y.merge({ broadcast: 1 }, config));
92
            // Publish IO transaction events with configurations, if
93
            // any.  These events use the "io-trn:" namespace.
94
            io.publish('io-trn:' + EVENTS[i], config);
95
        }
96
    },
97
 
98
   /**
99
    * Method that creates a unique transaction object for each request.
100
    *
101
    * @method _create
102
    * @private
103
    * @param {Object} cfg Configuration object subset to determine if
104
    *                 the transaction is an XDR or file upload,
105
    *                 requiring an alternate transport.
106
    * @param {Number} id Transaction id
107
    * @return {Object} The transaction object
108
    */
109
    _create: function(config, id) {
110
        var io = this,
111
            transaction = {
112
                id : Y.Lang.isNumber(id) ? id : io._id++,
113
                uid: io._uid
114
            },
115
            alt = config.xdr ? config.xdr.use : null,
116
            form = config.form && config.form.upload ? 'iframe' : null,
117
            use;
118
 
119
        if (alt === 'native') {
120
            // Non-IE and IE >= 10  can use XHR level 2 and not rely on an
121
            // external transport.
122
            alt = Y.UA.ie && !SUPPORTS_CORS ? 'xdr' : null;
123
 
124
            // Prevent "pre-flight" OPTIONS request by removing the
125
            // `X-Requested-With` HTTP header from CORS requests. This header
126
            // can be added back on a per-request basis, if desired.
127
            io.setHeader('X-Requested-With');
128
        }
129
 
130
        use = alt || form;
131
        transaction = use ? Y.merge(Y.IO.customTransport(use), transaction) :
132
                            Y.merge(Y.IO.defaultTransport(), transaction);
133
 
134
        if (transaction.notify) {
135
            config.notify = function (e, t, c) { io.notify(e, t, c); };
136
        }
137
 
138
        if (!use) {
139
            if (win && win.FormData && config.data instanceof win.FormData) {
140
                transaction.c.upload.onprogress = function (e) {
141
                    io.progress(transaction, e, config);
142
                };
143
                transaction.c.onload = function (e) {
144
                    io.load(transaction, e, config);
145
                };
146
                transaction.c.onerror = function (e) {
147
                    io.error(transaction, e, config);
148
                };
149
                transaction.upload = true;
150
            }
151
        }
152
 
153
        return transaction;
154
    },
155
 
156
    _destroy: function(transaction) {
157
        if (win && !transaction.notify && !transaction.xdr) {
158
            if (XHR && !transaction.upload) {
159
                transaction.c.onreadystatechange = null;
160
            } else if (transaction.upload) {
161
                transaction.c.upload.onprogress = null;
162
                transaction.c.onload = null;
163
                transaction.c.onerror = null;
164
            } else if (Y.UA.ie && !transaction.e) {
165
                // IE, when using XMLHttpRequest as an ActiveX Object, will throw
166
                // a "Type Mismatch" error if the event handler is set to "null".
167
                transaction.c.abort();
168
            }
169
        }
170
 
171
        transaction = transaction.c = null;
172
    },
173
 
174
   /**
175
    * Method for creating and firing events.
176
    *
177
    * @method _evt
178
    * @private
179
    * @param {String} eventName Event to be published.
180
    * @param {Object} transaction Transaction object.
181
    * @param {Object} config Configuration data subset for event subscription.
182
    */
183
    _evt: function(eventName, transaction, config) {
184
        var io          = this, params,
185
            args        = config['arguments'],
186
            emitFacade  = io.cfg.emitFacade,
187
            globalEvent = "io:" + eventName,
188
            trnEvent    = "io-trn:" + eventName;
189
 
190
        // Workaround for #2532107
191
        this.detach(trnEvent);
192
 
193
        if (transaction.e) {
194
            transaction.c = { status: 0, statusText: transaction.e };
195
        }
196
 
197
        // Fire event with parameters or an Event Facade.
198
        params = [ emitFacade ?
199
            {
200
                id: transaction.id,
201
                data: transaction.c,
202
                cfg: config,
203
                'arguments': args
204
            } :
205
            transaction.id
206
        ];
207
 
208
        if (!emitFacade) {
209
            if (eventName === EVENTS[0] || eventName === EVENTS[2]) {
210
                if (args) {
211
                    params.push(args);
212
                }
213
            } else {
214
                if (transaction.evt) {
215
                    params.push(transaction.evt);
216
                } else {
217
                    params.push(transaction.c);
218
                }
219
                if (args) {
220
                    params.push(args);
221
                }
222
            }
223
        }
224
 
225
        params.unshift(globalEvent);
226
        // Fire global events.
227
        io.fire.apply(io, params);
228
        // Fire transaction events, if receivers are defined.
229
        if (config.on) {
230
            params[0] = trnEvent;
231
            io.once(trnEvent, config.on[eventName], config.context || Y);
232
            io.fire.apply(io, params);
233
        }
234
    },
235
 
236
   /**
237
    * Fires event "io:start" and creates, fires a transaction-specific
238
    * start event, if `config.on.start` is defined.
239
    *
240
    * @method start
241
    * @param {Object} transaction Transaction object.
242
    * @param {Object} config Configuration object for the transaction.
243
    */
244
    start: function(transaction, config) {
245
       /**
246
        * Signals the start of an IO request.
247
        * @event io:start
248
        */
249
        this._evt(EVENTS[0], transaction, config);
250
    },
251
 
252
   /**
253
    * Fires event "io:complete" and creates, fires a
254
    * transaction-specific "complete" event, if config.on.complete is
255
    * defined.
256
    *
257
    * @method complete
258
    * @param {Object} transaction Transaction object.
259
    * @param {Object} config Configuration object for the transaction.
260
    */
261
    complete: function(transaction, config) {
262
       /**
263
        * Signals the completion of the request-response phase of a
264
        * transaction. Response status and data are accessible, if
265
        * available, in this event.
266
        * @event io:complete
267
        */
268
        this._evt(EVENTS[1], transaction, config);
269
    },
270
 
271
   /**
272
    * Fires event "io:end" and creates, fires a transaction-specific "end"
273
    * event, if config.on.end is defined.
274
    *
275
    * @method end
276
    * @param {Object} transaction Transaction object.
277
    * @param {Object} config Configuration object for the transaction.
278
    */
279
    end: function(transaction, config) {
280
       /**
281
        * Signals the end of the transaction lifecycle.
282
        * @event io:end
283
        */
284
        this._evt(EVENTS[2], transaction, config);
285
        this._destroy(transaction);
286
    },
287
 
288
   /**
289
    * Fires event "io:success" and creates, fires a transaction-specific
290
    * "success" event, if config.on.success is defined.
291
    *
292
    * @method success
293
    * @param {Object} transaction Transaction object.
294
    * @param {Object} config Configuration object for the transaction.
295
    */
296
    success: function(transaction, config) {
297
       /**
298
        * Signals an HTTP response with status in the 2xx range.
299
        * Fires after io:complete.
300
        * @event io:success
301
        */
302
        this._evt(EVENTS[3], transaction, config);
303
        this.end(transaction, config);
304
    },
305
 
306
   /**
307
    * Fires event "io:failure" and creates, fires a transaction-specific
308
    * "failure" event, if config.on.failure is defined.
309
    *
310
    * @method failure
311
    * @param {Object} transaction Transaction object.
312
    * @param {Object} config Configuration object for the transaction.
313
    */
314
    failure: function(transaction, config) {
315
       /**
316
        * Signals an HTTP response with status outside of the 2xx range.
317
        * Fires after io:complete.
318
        * @event io:failure
319
        */
320
        this._evt(EVENTS[4], transaction, config);
321
        this.end(transaction, config);
322
    },
323
 
324
   /**
325
    * Fires event "io:progress" and creates, fires a transaction-specific
326
    * "progress" event -- for XMLHttpRequest file upload -- if
327
    * config.on.progress is defined.
328
    *
329
    * @method progress
330
    * @param {Object} transaction Transaction object.
331
    * @param {Object} progress event.
332
    * @param {Object} config Configuration object for the transaction.
333
    */
334
    progress: function(transaction, e, config) {
335
       /**
336
        * Signals the interactive state during a file upload transaction.
337
        * This event fires after io:start and before io:complete.
338
        * @event io:progress
339
        */
340
        transaction.evt = e;
341
        this._evt(EVENTS[5], transaction, config);
342
    },
343
 
344
   /**
345
    * Fires event "io:complete" and creates, fires a transaction-specific
346
    * "complete" event -- for XMLHttpRequest file upload -- if
347
    * config.on.complete is defined.
348
    *
349
    * @method load
350
    * @param {Object} transaction Transaction object.
351
    * @param {Object} load event.
352
    * @param {Object} config Configuration object for the transaction.
353
    */
354
    load: function (transaction, e, config) {
355
        transaction.evt = e.target;
356
        this._evt(EVENTS[1], transaction, config);
357
    },
358
 
359
   /**
360
    * Fires event "io:failure" and creates, fires a transaction-specific
361
    * "failure" event -- for XMLHttpRequest file upload -- if
362
    * config.on.failure is defined.
363
    *
364
    * @method error
365
    * @param {Object} transaction Transaction object.
366
    * @param {Object} error event.
367
    * @param {Object} config Configuration object for the transaction.
368
    */
369
    error: function (transaction, e, config) {
370
        transaction.evt = e;
371
        this._evt(EVENTS[4], transaction, config);
372
    },
373
 
374
   /**
375
    * Retry an XDR transaction, using the Flash tranport, if the native
376
    * transport fails.
377
    *
378
    * @method _retry
379
    * @private
380
    * @param {Object} transaction Transaction object.
381
    * @param {String} uri Qualified path to transaction resource.
382
    * @param {Object} config Configuration object for the transaction.
383
    */
384
    _retry: function(transaction, uri, config) {
385
        this._destroy(transaction);
386
        config.xdr.use = 'flash';
387
        return this.send(uri, config, transaction.id);
388
    },
389
 
390
   /**
391
    * Method that concatenates string data for HTTP GET transactions.
392
    *
393
    * @method _concat
394
    * @private
395
    * @param {String} uri URI or root data.
396
    * @param {String} data Data to be concatenated onto URI.
397
    * @return {String}
398
    */
399
    _concat: function(uri, data) {
400
        uri += (uri.indexOf('?') === -1 ? '?' : '&') + data;
401
        return uri;
402
    },
403
 
404
   /**
405
    * Stores default client headers for all transactions. If a label is
406
    * passed with no value argument, the header will be deleted.
407
    *
408
    * @method setHeader
409
    * @param {String} name HTTP header
410
    * @param {String} value HTTP header value
411
    */
412
    setHeader: function(name, value) {
413
        if (value) {
414
            this._headers[name] = value;
415
        } else {
416
            delete this._headers[name];
417
        }
418
    },
419
 
420
   /**
421
    * Method that sets all HTTP headers to be sent in a transaction.
422
    *
423
    * @method _setHeaders
424
    * @private
425
    * @param {Object} transaction - XHR instance for the specific transaction.
426
    * @param {Object} headers - HTTP headers for the specific transaction, as
427
    *                    defined in the configuration object passed to YUI.io().
428
    */
429
    _setHeaders: function(transaction, headers) {
430
        headers = Y.merge(this._headers, headers);
431
        Y.Object.each(headers, function(value, name) {
432
            if (value !== 'disable') {
433
                transaction.setRequestHeader(name, headers[name]);
434
            }
435
        });
436
    },
437
 
438
   /**
439
    * Starts timeout count if the configuration object has a defined
440
    * timeout property.
441
    *
442
    * @method _startTimeout
443
    * @private
444
    * @param {Object} transaction Transaction object generated by _create().
445
    * @param {Object} timeout Timeout in milliseconds.
446
    */
447
    _startTimeout: function(transaction, timeout) {
448
        var io = this;
449
 
450
        io._timeout[transaction.id] = setTimeout(function() {
451
            io._abort(transaction, 'timeout');
452
        }, timeout);
453
    },
454
 
455
   /**
456
    * Clears the timeout interval started by _startTimeout().
457
    *
458
    * @method _clearTimeout
459
    * @private
460
    * @param {Number} id - Transaction id.
461
    */
462
    _clearTimeout: function(id) {
463
        clearTimeout(this._timeout[id]);
464
        delete this._timeout[id];
465
    },
466
 
467
   /**
468
    * Method that determines if a transaction response qualifies as success
469
    * or failure, based on the response HTTP status code, and fires the
470
    * appropriate success or failure events.
471
    *
472
    * @method _result
473
    * @private
474
    * @static
475
    * @param {Object} transaction Transaction object generated by _create().
476
    * @param {Object} config Configuration object passed to io().
477
    */
478
    _result: function(transaction, config) {
479
        var status;
480
        // Firefox will throw an exception if attempting to access
481
        // an XHR object's status property, after a request is aborted.
482
        try {
483
            status = transaction.c.status;
484
        } catch(e) {
485
            status = 0;
486
        }
487
 
488
        // IE reports HTTP 204 as HTTP 1223.
489
        if (status >= 200 && status < 300 || status === 304 || status === 1223) {
490
            this.success(transaction, config);
491
        } else {
492
            this.failure(transaction, config);
493
        }
494
    },
495
 
496
   /**
497
    * Event handler bound to onreadystatechange.
498
    *
499
    * @method _rS
500
    * @private
501
    * @param {Object} transaction Transaction object generated by _create().
502
    * @param {Object} config Configuration object passed to YUI.io().
503
    */
504
    _rS: function(transaction, config) {
505
        var io = this;
506
 
507
        if (transaction.c.readyState === 4) {
508
            if (config.timeout) {
509
                io._clearTimeout(transaction.id);
510
            }
511
 
512
            // Yield in the event of request timeout or abort.
513
            setTimeout(function() {
514
                io.complete(transaction, config);
515
                io._result(transaction, config);
516
            }, 0);
517
        }
518
    },
519
 
520
   /**
521
    * Terminates a transaction due to an explicit abort or timeout.
522
    *
523
    * @method _abort
524
    * @private
525
    * @param {Object} transaction Transaction object generated by _create().
526
    * @param {String} type Identifies timed out or aborted transaction.
527
    */
528
    _abort: function(transaction, type) {
529
        if (transaction && transaction.c) {
530
            transaction.e = type;
531
            transaction.c.abort();
532
        }
533
    },
534
 
535
   /**
536
    * Requests a transaction. `send()` is implemented as `Y.io()`.  Each
537
    * transaction may include a configuration object.  Its properties are:
538
    *
539
    * <dl>
540
    *   <dt>method</dt>
541
    *     <dd>HTTP method verb (e.g., GET or POST). If this property is not
542
    *         not defined, the default value will be GET.</dd>
543
    *
544
    *   <dt>data</dt>
545
    *     <dd>This is the name-value string that will be sent as the
546
    *     transaction data. If the request is HTTP GET, the data become
547
    *     part of querystring. If HTTP POST, the data are sent in the
548
    *     message body.</dd>
549
    *
550
    *   <dt>xdr</dt>
551
    *     <dd>Defines the transport to be used for cross-domain requests.
552
    *     By setting this property, the transaction will use the specified
553
    *     transport instead of XMLHttpRequest. The properties of the
554
    *     transport object are:
555
    *     <dl>
556
    *       <dt>use</dt>
557
    *         <dd>The transport to be used: 'flash' or 'native'</dd>
558
    *       <dt>dataType</dt>
559
    *         <dd>Set the value to 'XML' if that is the expected response
560
    *         content type.</dd>
561
    *       <dt>credentials</dt>
562
    *         <dd>Set the value to 'true' to set XHR.withCredentials property to true.</dd>
563
    *     </dl></dd>
564
    *
565
    *   <dt>form</dt>
566
    *     <dd>Form serialization configuration object.  Its properties are:
567
    *     <dl>
568
    *       <dt>id</dt>
569
    *         <dd>Node object or id of HTML form</dd>
570
    *       <dt>useDisabled</dt>
571
    *         <dd>`true` to also serialize disabled form field values
572
    *         (defaults to `false`)</dd>
573
    *     </dl></dd>
574
    *
575
    *   <dt>on</dt>
576
    *     <dd>Assigns transaction event subscriptions. Available events are:
577
    *     <dl>
578
    *       <dt>start</dt>
579
    *         <dd>Fires when a request is sent to a resource.</dd>
580
    *       <dt>complete</dt>
581
    *         <dd>Fires when the transaction is complete.</dd>
582
    *       <dt>success</dt>
583
    *         <dd>Fires when the HTTP response status is within the 2xx
584
    *         range.</dd>
585
    *       <dt>failure</dt>
586
    *         <dd>Fires when the HTTP response status is outside the 2xx
587
    *         range, if an exception occurs, if the transation is aborted,
588
    *         or if the transaction exceeds a configured `timeout`.</dd>
589
    *       <dt>end</dt>
590
    *         <dd>Fires at the conclusion of the transaction
591
    *            lifecycle, after `success` or `failure`.</dd>
592
    *     </dl>
593
    *
594
    *     <p>Callback functions for `start` and `end` receive the id of the
595
    *     transaction as a first argument. For `complete`, `success`, and
596
    *     `failure`, callbacks receive the id and the response object
597
    *     (usually the XMLHttpRequest instance).  If the `arguments`
598
    *     property was included in the configuration object passed to
599
    *     `Y.io()`, the configured data will be passed to all callbacks as
600
    *     the last argument.</p>
601
    *     </dd>
602
    *
603
    *   <dt>sync</dt>
604
    *     <dd>Pass `true` to make a same-domain transaction synchronous.
605
    *     <strong>CAVEAT</strong>: This will negatively impact the user
606
    *     experience. Have a <em>very</em> good reason if you intend to use
607
    *     this.</dd>
608
    *
609
    *   <dt>context</dt>
610
    *     <dd>The "`this'" object for all configured event handlers. If a
611
    *     specific context is needed for individual callbacks, bind the
612
    *     callback to a context using `Y.bind()`.</dd>
613
    *
614
    *   <dt>headers</dt>
615
    *     <dd>Object map of transaction headers to send to the server. The
616
    *     object keys are the header names and the values are the header
617
    *     values.</dd>
618
    *
619
    *   <dt>username</dt>
620
    *     <dd>Username to use in a HTTP authentication.</dd>
621
    *
622
    *   <dt>password</dt>
623
    *     <dd>Password to use in a HTTP authentication.</dd>
624
    *
625
    *   <dt>timeout</dt>
626
    *     <dd>Millisecond threshold for the transaction before being
627
    *     automatically aborted.</dd>
628
    *
629
    *   <dt>arguments</dt>
630
    *     <dd>User-defined data passed to all registered event handlers.
631
    *     This value is available as the second argument in the "start" and
632
    *     "end" event handlers. It is the third argument in the "complete",
633
    *     "success", and "failure" event handlers. <strong>Be sure to quote
634
    *     this property name in the transaction configuration as
635
    *     "arguments" is a reserved word in JavaScript</strong> (e.g.
636
    *     `Y.io({ ..., "arguments": stuff })`).</dd>
637
    * </dl>
638
    *
639
    * @method send
640
    * @public
641
    * @param {String} uri Qualified path to transaction resource.
642
    * @param {Object} config Configuration object for the transaction.
643
    * @param {Number} id Transaction id, if already set.
644
    * @return {Object} An object containing:
645
    * <dl>
646
    *  <dt>`id`</dt>
647
    *  <dd>
648
    *    The transaction ID for this request.
649
    *  </dd>
650
    *  <dt>`abort`</dt>
651
    *  <dd>
652
    *    A function to abort the current transaction.
653
    *  </dd>
654
    *  <dt>`isInProgress`</dt>
655
    *  <dd>
656
    *    A helper to determine whether the current transaction is in progress.
657
    *  </dd>
658
    *  <dt>`io`</dt>
659
    *  <dd>
660
    *    A reference to the IO object for this transaction.
661
    *  </dd>
662
    * </dl>
663
    */
664
    send: function(uri, config, id) {
665
        var transaction, method, i, len, sync, data,
666
            io = this,
667
            u = uri,
668
            response = {};
669
 
670
        config = config ? Y.Object(config) : {};
671
        transaction = io._create(config, id);
672
        method = config.method ? config.method.toUpperCase() : 'GET';
673
        sync = config.sync;
674
        data = config.data;
675
 
676
        // Serialize a map object into a key-value string using
677
        // querystring-stringify-simple.
678
        if ((Y.Lang.isObject(data) && !data.nodeType) && !transaction.upload) {
679
            if (Y.QueryString && Y.QueryString.stringify) {
680
                Y.log('Stringifying config.data for request', 'info', 'io');
681
                config.data = data = Y.QueryString.stringify(data);
682
            } else {
683
                Y.log('Failed to stringify config.data object, likely because `querystring-stringify-simple` is missing.', 'warn', 'io');
684
            }
685
        }
686
 
687
        if (config.form) {
688
            if (config.form.upload) {
689
                // This is a file upload transaction, calling
690
                // upload() in io-upload-iframe.
691
                return io.upload(transaction, uri, config);
692
            } else {
693
                // Serialize HTML form data into a key-value string.
694
                data = io._serialize(config.form, data);
695
            }
696
        }
697
 
698
        // Convert falsy values to an empty string. This way IE can't be
699
        // rediculous and translate `undefined` to "undefined".
700
        data || (data = '');
701
 
702
        if (data) {
703
            switch (method) {
704
                case 'GET':
705
                case 'HEAD':
706
                case 'DELETE':
707
                    u = io._concat(u, data);
708
                    data = '';
709
                    Y.log('HTTP' + method + ' with data.  The querystring is: ' + u, 'info', 'io');
710
                    break;
711
                case 'POST':
712
                case 'PUT':
713
                    // If Content-Type is defined in the configuration object, or
714
                    // or as a default header, it will be used instead of
715
                    // 'application/x-www-form-urlencoded; charset=UTF-8'
716
                    config.headers = Y.merge({
717
                        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
718
                    }, config.headers);
719
                    break;
720
            }
721
        }
722
 
723
        if (transaction.xdr) {
724
            // Route data to io-xdr module for flash and XDomainRequest.
725
            return io.xdr(u, transaction, config);
726
        }
727
        else if (transaction.notify) {
728
            // Route data to custom transport
729
            return transaction.c.send(transaction, uri, config);
730
        }
731
 
732
        if (!sync && !transaction.upload) {
733
            transaction.c.onreadystatechange = function() {
734
                io._rS(transaction, config);
735
            };
736
        }
737
 
738
        try {
739
            // Determine if request is to be set as
740
            // synchronous or asynchronous.
741
            transaction.c.open(method, u, !sync, config.username || null, config.password || null);
742
            io._setHeaders(transaction.c, config.headers || {});
743
            io.start(transaction, config);
744
 
745
            // Will work only in browsers that implement the
746
            // Cross-Origin Resource Sharing draft.
747
            if (config.xdr && config.xdr.credentials && SUPPORTS_CORS) {
748
                transaction.c.withCredentials = true;
749
            }
750
 
751
            // Using "null" with HTTP POST will result in a request
752
            // with no Content-Length header defined.
753
            transaction.c.send(data);
754
 
755
            if (sync) {
756
                // Create a response object for synchronous transactions,
757
                // mixing id and arguments properties with the xhr
758
                // properties whitelist.
759
                for (i = 0, len = XHR_PROPS.length; i < len; ++i) {
760
                    response[XHR_PROPS[i]] = transaction.c[XHR_PROPS[i]];
761
                }
762
 
763
                response.getAllResponseHeaders = function() {
764
                    return transaction.c.getAllResponseHeaders();
765
                };
766
 
767
                response.getResponseHeader = function(name) {
768
                    return transaction.c.getResponseHeader(name);
769
                };
770
 
771
                io.complete(transaction, config);
772
                io._result(transaction, config);
773
 
774
                return response;
775
            }
776
        } catch(e) {
777
            if (transaction.xdr) {
778
                // This exception is usually thrown by browsers
779
                // that do not support XMLHttpRequest Level 2.
780
                // Retry the request with the XDR transport set
781
                // to 'flash'.  If the Flash transport is not
782
                // initialized or available, the transaction
783
                // will resolve to a transport error.
784
                return io._retry(transaction, uri, config);
785
            } else {
786
                io.complete(transaction, config);
787
                io._result(transaction, config);
788
            }
789
        }
790
 
791
        // If config.timeout is defined, and the request is standard XHR,
792
        // initialize timeout polling.
793
        if (config.timeout) {
794
            io._startTimeout(transaction, config.timeout);
795
            Y.log('Configuration timeout set to: ' + config.timeout, 'info', 'io');
796
        }
797
 
798
        return {
799
            id: transaction.id,
800
            abort: function() {
801
                return transaction.c ? io._abort(transaction, 'abort') : false;
802
            },
803
            isInProgress: function() {
804
                return transaction.c ? (transaction.c.readyState % 4) : false;
805
            },
806
            io: io
807
        };
808
    }
809
};
810
 
811
/**
812
Method for initiating an ajax call.  The first argument is the url end
813
point for the call.  The second argument is an object to configure the
814
transaction and attach event subscriptions.  The configuration object
815
supports the following properties:
816
 
817
<dl>
818
  <dt>method</dt>
819
    <dd>HTTP method verb (e.g., GET or POST). If this property is not
820
        not defined, the default value will be GET.</dd>
821
 
822
  <dt>data</dt>
823
    <dd>This is the name-value string that will be sent as the
824
    transaction data. If the request is HTTP GET, the data become
825
    part of querystring. If HTTP POST, the data are sent in the
826
    message body.</dd>
827
 
828
  <dt>xdr</dt>
829
    <dd>Defines the transport to be used for cross-domain requests.
830
    By setting this property, the transaction will use the specified
831
    transport instead of XMLHttpRequest. The properties of the
832
    transport object are:
833
    <dl>
834
      <dt>use</dt>
835
        <dd>The transport to be used: 'flash' or 'native'</dd>
836
      <dt>dataType</dt>
837
        <dd>Set the value to 'XML' if that is the expected response
838
        content type.</dd>
839
    </dl></dd>
840
 
841
  <dt>form</dt>
842
    <dd>Form serialization configuration object.  Its properties are:
843
    <dl>
844
      <dt>id</dt>
845
        <dd>Node object or id of HTML form</dd>
846
      <dt>useDisabled</dt>
847
        <dd>`true` to also serialize disabled form field values
848
        (defaults to `false`)</dd>
849
    </dl></dd>
850
 
851
  <dt>on</dt>
852
    <dd>Assigns transaction event subscriptions. Available events are:
853
    <dl>
854
      <dt>start</dt>
855
        <dd>Fires when a request is sent to a resource.</dd>
856
      <dt>complete</dt>
857
        <dd>Fires when the transaction is complete.</dd>
858
      <dt>success</dt>
859
        <dd>Fires when the HTTP response status is within the 2xx
860
        range.</dd>
861
      <dt>failure</dt>
862
        <dd>Fires when the HTTP response status is outside the 2xx
863
        range, if an exception occurs, if the transation is aborted,
864
        or if the transaction exceeds a configured `timeout`.</dd>
865
      <dt>end</dt>
866
        <dd>Fires at the conclusion of the transaction
867
           lifecycle, after `success` or `failure`.</dd>
868
    </dl>
869
 
870
    <p>Callback functions for `start` and `end` receive the id of the
871
    transaction as a first argument. For `complete`, `success`, and
872
    `failure`, callbacks receive the id and the response object
873
    (usually the XMLHttpRequest instance).  If the `arguments`
874
    property was included in the configuration object passed to
875
    `Y.io()`, the configured data will be passed to all callbacks as
876
    the last argument.</p>
877
    </dd>
878
 
879
  <dt>sync</dt>
880
    <dd>Pass `true` to make a same-domain transaction synchronous.
881
    <strong>CAVEAT</strong>: This will negatively impact the user
882
    experience. Have a <em>very</em> good reason if you intend to use
883
    this.</dd>
884
 
885
  <dt>context</dt>
886
    <dd>The "`this'" object for all configured event handlers. If a
887
    specific context is needed for individual callbacks, bind the
888
    callback to a context using `Y.bind()`.</dd>
889
 
890
  <dt>headers</dt>
891
    <dd>Object map of transaction headers to send to the server. The
892
    object keys are the header names and the values are the header
893
    values.</dd>
894
 
895
  <dt>timeout</dt>
896
    <dd>Millisecond threshold for the transaction before being
897
    automatically aborted.</dd>
898
 
899
  <dt>arguments</dt>
900
    <dd>User-defined data passed to all registered event handlers.
901
    This value is available as the second argument in the "start" and
902
    "end" event handlers. It is the third argument in the "complete",
903
    "success", and "failure" event handlers. <strong>Be sure to quote
904
    this property name in the transaction configuration as
905
    "arguments" is a reserved word in JavaScript</strong> (e.g.
906
    `Y.io({ ..., "arguments": stuff })`).</dd>
907
</dl>
908
 
909
@method io
910
@static
911
@param {String} url qualified path to transaction resource.
912
@param {Object} config configuration object for the transaction.
913
@return {Object} An object containing:
914
<dl>
915
 <dt>`id`</dt>
916
 <dd>
917
   The transaction ID for this request.
918
 </dd>
919
 <dt>`abort`</dt>
920
 <dd>
921
   A function to abort the current transaction.
922
 </dd>
923
 <dt>`isInProgress`</dt>
924
 <dd>
925
   A helper to determine whether the current transaction is in progress.
926
 </dd>
927
 <dt>`io`</dt>
928
 <dd>
929
   A reference to the IO object for this transaction.
930
 </dd>
931
</dl>
932
 
933
@for YUI
934
**/
935
Y.io = function(url, config) {
936
    // Calling IO through the static interface will use and reuse
937
    // an instance of IO.
938
    var transaction = Y.io._map['io:0'] || new IO();
939
    return transaction.send.apply(transaction, [url, config]);
940
};
941
 
942
/**
943
Method for setting and deleting IO HTTP headers to be sent with every
944
request.
945
 
946
Hosted as a property on the `io` function (e.g. `Y.io.header`).
947
 
948
@method header
949
@param {String} name HTTP header
950
@param {String} value HTTP header value
951
@static
952
**/
953
Y.io.header = function(name, value) {
954
    // Calling IO through the static interface will use and reuse
955
    // an instance of IO.
956
    var transaction = Y.io._map['io:0'] || new IO();
957
    transaction.setHeader(name, value);
958
};
959
 
960
Y.IO = IO;
961
// Map of all IO instances created.
962
Y.io._map = {};
963
var XHR = win && win.XMLHttpRequest,
964
    XDR = win && win.XDomainRequest,
965
    AX = win && win.ActiveXObject,
966
 
967
    // Checks for the presence of the `withCredentials` in an XHR instance
968
    // object, which will be present if the environment supports CORS.
969
    SUPPORTS_CORS = XHR && 'withCredentials' in (new XMLHttpRequest());
970
 
971
 
972
Y.mix(Y.IO, {
973
    /**
974
    * The ID of the default IO transport, defaults to `xhr`
975
    * @property _default
976
    * @type {String}
977
    * @static
978
    */
979
    _default: 'xhr',
980
    /**
981
    *
982
    * @method defaultTransport
983
    * @static
984
    * @param {String} [id] The transport to set as the default, if empty a new transport is created.
985
    * @return {Object} The transport object with a `send` method
986
    */
987
    defaultTransport: function(id) {
988
        if (id) {
989
            Y.log('Setting default IO to: ' + id, 'info', 'io');
990
            Y.IO._default = id;
991
        } else {
992
            var o = {
993
                c: Y.IO.transports[Y.IO._default](),
994
                notify: Y.IO._default === 'xhr' ? false : true
995
            };
996
            Y.log('Creating default transport: ' + Y.IO._default, 'info', 'io');
997
            return o;
998
        }
999
    },
1000
    /**
1001
    * An object hash of custom transports available to IO
1002
    * @property transports
1003
    * @type {Object}
1004
    * @static
1005
    */
1006
    transports: {
1007
        xhr: function () {
1008
            return XHR ? new XMLHttpRequest() :
1009
                AX ? new ActiveXObject('Microsoft.XMLHTTP') : null;
1010
        },
1011
        xdr: function () {
1012
            return XDR ? new XDomainRequest() : null;
1013
        },
1014
        iframe: function () { return {}; },
1015
        flash: null,
1016
        nodejs: null
1017
    },
1018
    /**
1019
    * Create a custom transport of type and return it's object
1020
    * @method customTransport
1021
    * @param {String} id The id of the transport to create.
1022
    * @static
1023
    */
1024
    customTransport: function(id) {
1025
        var o = { c: Y.IO.transports[id]() };
1026
 
1027
        o[(id === 'xdr' || id === 'flash') ? 'xdr' : 'notify'] = true;
1028
        return o;
1029
    }
1030
});
1031
 
1032
Y.mix(Y.IO.prototype, {
1033
    /**
1034
    * Fired from the notify method of the transport which in turn fires
1035
    * the event on the IO object.
1036
    * @method notify
1037
    * @param {String} event The name of the event
1038
    * @param {Object} transaction The transaction object
1039
    * @param {Object} config The configuration object for this transaction
1040
    */
1041
    notify: function(event, transaction, config) {
1042
        var io = this;
1043
 
1044
        switch (event) {
1045
            case 'timeout':
1046
            case 'abort':
1047
            case 'transport error':
1048
                transaction.c = { status: 0, statusText: event };
1049
                event = 'failure';
1050
            default:
1051
                io[event].apply(io, [transaction, config]);
1052
        }
1053
    }
1054
});
1055
 
1056
 
1057
 
1058
 
1059
}, '3.18.1', {"requires": ["event-custom-base", "querystring-stringify-simple"]});