~ubuntu-branches/ubuntu/utopic/moodle/utopic

« back to all changes in this revision

Viewing changes to lib/yuilib/3.13.0/io-base/io-base.js

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2014-05-12 16:10:38 UTC
  • mfrom: (36.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20140512161038-puyqf65k4e0s8ytz
Tags: 2.6.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

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