API Docs for: 3.17.2
Show:

File: io/js/io-base.js

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