You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1028 lines
34 KiB
1028 lines
34 KiB
2 years ago
|
/*
|
||
|
YUI 3.17.2 (build 9c3c78e)
|
||
|
Copyright 2014 Yahoo! Inc. All rights reserved.
|
||
|
Licensed under the BSD License.
|
||
|
http://yuilibrary.com/license/
|
||
|
*/
|
||
|
|
||
|
YUI.add('io-base', function (Y, NAME) {
|
||
|
|
||
|
/**
|
||
|
Base IO functionality. Provides basic XHR transport support.
|
||
|
|
||
|
@module io
|
||
|
@submodule io-base
|
||
|
@for IO
|
||
|
**/
|
||
|
|
||
|
var // List of events that comprise the IO event lifecycle.
|
||
|
EVENTS = ['start', 'complete', 'end', 'success', 'failure', 'progress'],
|
||
|
|
||
|
// Whitelist of used XHR response object properties.
|
||
|
XHR_PROPS = ['status', 'statusText', 'responseText', 'responseXML'],
|
||
|
|
||
|
win = Y.config.win,
|
||
|
uid = 0;
|
||
|
|
||
|
/**
|
||
|
The IO class is a utility that brokers HTTP requests through a simplified
|
||
|
interface. Specifically, it allows JavaScript to make HTTP requests to
|
||
|
a resource without a page reload. The underlying transport for making
|
||
|
same-domain requests is the XMLHttpRequest object. IO can also use
|
||
|
Flash, if specified as a transport, for cross-domain requests.
|
||
|
|
||
|
@class IO
|
||
|
@constructor
|
||
|
@param {Object} config Object of EventTarget's publish method configurations
|
||
|
used to configure IO's events.
|
||
|
**/
|
||
|
function IO (config) {
|
||
|
var io = this;
|
||
|
|
||
|
io._uid = 'io:' + uid++;
|
||
|
io._init(config);
|
||
|
Y.io._map[io._uid] = io;
|
||
|
}
|
||
|
|
||
|
IO.prototype = {
|
||
|
//--------------------------------------
|
||
|
// Properties
|
||
|
//--------------------------------------
|
||
|
|
||
|
/**
|
||
|
* A counter that increments for each transaction.
|
||
|
*
|
||
|
* @property _id
|
||
|
* @private
|
||
|
* @type {Number}
|
||
|
*/
|
||
|
_id: 0,
|
||
|
|
||
|
/**
|
||
|
* Object of IO HTTP headers sent with each transaction.
|
||
|
*
|
||
|
* @property _headers
|
||
|
* @private
|
||
|
* @type {Object}
|
||
|
*/
|
||
|
_headers: {
|
||
|
'X-Requested-With' : 'XMLHttpRequest'
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Object that stores timeout values for any transaction with a defined
|
||
|
* "timeout" configuration property.
|
||
|
*
|
||
|
* @property _timeout
|
||
|
* @private
|
||
|
* @type {Object}
|
||
|
*/
|
||
|
_timeout: {},
|
||
|
|
||
|
//--------------------------------------
|
||
|
// Methods
|
||
|
//--------------------------------------
|
||
|
|
||
|
_init: function(config) {
|
||
|
var io = this, i, len;
|
||
|
|
||
|
io.cfg = config || {};
|
||
|
|
||
|
Y.augment(io, Y.EventTarget);
|
||
|
for (i = 0, len = EVENTS.length; i < len; ++i) {
|
||
|
// Publish IO global events with configurations, if any.
|
||
|
// IO global events are set to broadcast by default.
|
||
|
// These events use the "io:" namespace.
|
||
|
io.publish('io:' + EVENTS[i], Y.merge({ broadcast: 1 }, config));
|
||
|
// Publish IO transaction events with configurations, if
|
||
|
// any. These events use the "io-trn:" namespace.
|
||
|
io.publish('io-trn:' + EVENTS[i], config);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Method that creates a unique transaction object for each request.
|
||
|
*
|
||
|
* @method _create
|
||
|
* @private
|
||
|
* @param {Object} cfg Configuration object subset to determine if
|
||
|
* the transaction is an XDR or file upload,
|
||
|
* requiring an alternate transport.
|
||
|
* @param {Number} id Transaction id
|
||
|
* @return {Object} The transaction object
|
||
|
*/
|
||
|
_create: function(config, id) {
|
||
|
var io = this,
|
||
|
transaction = {
|
||
|
id : Y.Lang.isNumber(id) ? id : io._id++,
|
||
|
uid: io._uid
|
||
|
},
|
||
|
alt = config.xdr ? config.xdr.use : null,
|
||
|
form = config.form && config.form.upload ? 'iframe' : null,
|
||
|
use;
|
||
|
|
||
|
if (alt === 'native') {
|
||
|
// Non-IE and IE >= 10 can use XHR level 2 and not rely on an
|
||
|
// external transport.
|
||
|
alt = Y.UA.ie && !SUPPORTS_CORS ? 'xdr' : null;
|
||
|
|
||
|
// Prevent "pre-flight" OPTIONS request by removing the
|
||
|
// `X-Requested-With` HTTP header from CORS requests. This header
|
||
|
// can be added back on a per-request basis, if desired.
|
||
|
io.setHeader('X-Requested-With');
|
||
|
}
|
||
|
|
||
|
use = alt || form;
|
||
|
transaction = use ? Y.merge(Y.IO.customTransport(use), transaction) :
|
||
|
Y.merge(Y.IO.defaultTransport(), transaction);
|
||
|
|
||
|
if (transaction.notify) {
|
||
|
config.notify = function (e, t, c) { io.notify(e, t, c); };
|
||
|
}
|
||
|
|
||
|
if (!use) {
|
||
|
if (win && win.FormData && config.data instanceof win.FormData) {
|
||
|
transaction.c.upload.onprogress = function (e) {
|
||
|
io.progress(transaction, e, config);
|
||
|
};
|
||
|
transaction.c.onload = function (e) {
|
||
|
io.load(transaction, e, config);
|
||
|
};
|
||
|
transaction.c.onerror = function (e) {
|
||
|
io.error(transaction, e, config);
|
||
|
};
|
||
|
transaction.upload = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return transaction;
|
||
|
},
|
||
|
|
||
|
_destroy: function(transaction) {
|
||
|
if (win && !transaction.notify && !transaction.xdr) {
|
||
|
if (XHR && !transaction.upload) {
|
||
|
transaction.c.onreadystatechange = null;
|
||
|
} else if (transaction.upload) {
|
||
|
transaction.c.upload.onprogress = null;
|
||
|
transaction.c.onload = null;
|
||
|
transaction.c.onerror = null;
|
||
|
} else if (Y.UA.ie && !transaction.e) {
|
||
|
// IE, when using XMLHttpRequest as an ActiveX Object, will throw
|
||
|
// a "Type Mismatch" error if the event handler is set to "null".
|
||
|
transaction.c.abort();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
transaction = transaction.c = null;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Method for creating and firing events.
|
||
|
*
|
||
|
* @method _evt
|
||
|
* @private
|
||
|
* @param {String} eventName Event to be published.
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} config Configuration data subset for event subscription.
|
||
|
*/
|
||
|
_evt: function(eventName, transaction, config) {
|
||
|
var io = this, params,
|
||
|
args = config['arguments'],
|
||
|
emitFacade = io.cfg.emitFacade,
|
||
|
globalEvent = "io:" + eventName,
|
||
|
trnEvent = "io-trn:" + eventName;
|
||
|
|
||
|
// Workaround for #2532107
|
||
|
this.detach(trnEvent);
|
||
|
|
||
|
if (transaction.e) {
|
||
|
transaction.c = { status: 0, statusText: transaction.e };
|
||
|
}
|
||
|
|
||
|
// Fire event with parameters or an Event Facade.
|
||
|
params = [ emitFacade ?
|
||
|
{
|
||
|
id: transaction.id,
|
||
|
data: transaction.c,
|
||
|
cfg: config,
|
||
|
'arguments': args
|
||
|
} :
|
||
|
transaction.id
|
||
|
];
|
||
|
|
||
|
if (!emitFacade) {
|
||
|
if (eventName === EVENTS[0] || eventName === EVENTS[2]) {
|
||
|
if (args) {
|
||
|
params.push(args);
|
||
|
}
|
||
|
} else {
|
||
|
if (transaction.evt) {
|
||
|
params.push(transaction.evt);
|
||
|
} else {
|
||
|
params.push(transaction.c);
|
||
|
}
|
||
|
if (args) {
|
||
|
params.push(args);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
params.unshift(globalEvent);
|
||
|
// Fire global events.
|
||
|
io.fire.apply(io, params);
|
||
|
// Fire transaction events, if receivers are defined.
|
||
|
if (config.on) {
|
||
|
params[0] = trnEvent;
|
||
|
io.once(trnEvent, config.on[eventName], config.context || Y);
|
||
|
io.fire.apply(io, params);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Fires event "io:start" and creates, fires a transaction-specific
|
||
|
* start event, if `config.on.start` is defined.
|
||
|
*
|
||
|
* @method start
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
start: function(transaction, config) {
|
||
|
/**
|
||
|
* Signals the start of an IO request.
|
||
|
* @event io:start
|
||
|
*/
|
||
|
this._evt(EVENTS[0], transaction, config);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Fires event "io:complete" and creates, fires a
|
||
|
* transaction-specific "complete" event, if config.on.complete is
|
||
|
* defined.
|
||
|
*
|
||
|
* @method complete
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
complete: function(transaction, config) {
|
||
|
/**
|
||
|
* Signals the completion of the request-response phase of a
|
||
|
* transaction. Response status and data are accessible, if
|
||
|
* available, in this event.
|
||
|
* @event io:complete
|
||
|
*/
|
||
|
this._evt(EVENTS[1], transaction, config);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Fires event "io:end" and creates, fires a transaction-specific "end"
|
||
|
* event, if config.on.end is defined.
|
||
|
*
|
||
|
* @method end
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
end: function(transaction, config) {
|
||
|
/**
|
||
|
* Signals the end of the transaction lifecycle.
|
||
|
* @event io:end
|
||
|
*/
|
||
|
this._evt(EVENTS[2], transaction, config);
|
||
|
this._destroy(transaction);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Fires event "io:success" and creates, fires a transaction-specific
|
||
|
* "success" event, if config.on.success is defined.
|
||
|
*
|
||
|
* @method success
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
success: function(transaction, config) {
|
||
|
/**
|
||
|
* Signals an HTTP response with status in the 2xx range.
|
||
|
* Fires after io:complete.
|
||
|
* @event io:success
|
||
|
*/
|
||
|
this._evt(EVENTS[3], transaction, config);
|
||
|
this.end(transaction, config);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Fires event "io:failure" and creates, fires a transaction-specific
|
||
|
* "failure" event, if config.on.failure is defined.
|
||
|
*
|
||
|
* @method failure
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
failure: function(transaction, config) {
|
||
|
/**
|
||
|
* Signals an HTTP response with status outside of the 2xx range.
|
||
|
* Fires after io:complete.
|
||
|
* @event io:failure
|
||
|
*/
|
||
|
this._evt(EVENTS[4], transaction, config);
|
||
|
this.end(transaction, config);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Fires event "io:progress" and creates, fires a transaction-specific
|
||
|
* "progress" event -- for XMLHttpRequest file upload -- if
|
||
|
* config.on.progress is defined.
|
||
|
*
|
||
|
* @method progress
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} progress event.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
progress: function(transaction, e, config) {
|
||
|
/**
|
||
|
* Signals the interactive state during a file upload transaction.
|
||
|
* This event fires after io:start and before io:complete.
|
||
|
* @event io:progress
|
||
|
*/
|
||
|
transaction.evt = e;
|
||
|
this._evt(EVENTS[5], transaction, config);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Fires event "io:complete" and creates, fires a transaction-specific
|
||
|
* "complete" event -- for XMLHttpRequest file upload -- if
|
||
|
* config.on.complete is defined.
|
||
|
*
|
||
|
* @method load
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} load event.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
load: function (transaction, e, config) {
|
||
|
transaction.evt = e.target;
|
||
|
this._evt(EVENTS[1], transaction, config);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Fires event "io:failure" and creates, fires a transaction-specific
|
||
|
* "failure" event -- for XMLHttpRequest file upload -- if
|
||
|
* config.on.failure is defined.
|
||
|
*
|
||
|
* @method error
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {Object} error event.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
error: function (transaction, e, config) {
|
||
|
transaction.evt = e;
|
||
|
this._evt(EVENTS[4], transaction, config);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Retry an XDR transaction, using the Flash tranport, if the native
|
||
|
* transport fails.
|
||
|
*
|
||
|
* @method _retry
|
||
|
* @private
|
||
|
* @param {Object} transaction Transaction object.
|
||
|
* @param {String} uri Qualified path to transaction resource.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
*/
|
||
|
_retry: function(transaction, uri, config) {
|
||
|
this._destroy(transaction);
|
||
|
config.xdr.use = 'flash';
|
||
|
return this.send(uri, config, transaction.id);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Method that concatenates string data for HTTP GET transactions.
|
||
|
*
|
||
|
* @method _concat
|
||
|
* @private
|
||
|
* @param {String} uri URI or root data.
|
||
|
* @param {String} data Data to be concatenated onto URI.
|
||
|
* @return {String}
|
||
|
*/
|
||
|
_concat: function(uri, data) {
|
||
|
uri += (uri.indexOf('?') === -1 ? '?' : '&') + data;
|
||
|
return uri;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Stores default client headers for all transactions. If a label is
|
||
|
* passed with no value argument, the header will be deleted.
|
||
|
*
|
||
|
* @method setHeader
|
||
|
* @param {String} name HTTP header
|
||
|
* @param {String} value HTTP header value
|
||
|
*/
|
||
|
setHeader: function(name, value) {
|
||
|
if (value) {
|
||
|
this._headers[name] = value;
|
||
|
} else {
|
||
|
delete this._headers[name];
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Method that sets all HTTP headers to be sent in a transaction.
|
||
|
*
|
||
|
* @method _setHeaders
|
||
|
* @private
|
||
|
* @param {Object} transaction - XHR instance for the specific transaction.
|
||
|
* @param {Object} headers - HTTP headers for the specific transaction, as
|
||
|
* defined in the configuration object passed to YUI.io().
|
||
|
*/
|
||
|
_setHeaders: function(transaction, headers) {
|
||
|
headers = Y.merge(this._headers, headers);
|
||
|
Y.Object.each(headers, function(value, name) {
|
||
|
if (value !== 'disable') {
|
||
|
transaction.setRequestHeader(name, headers[name]);
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Starts timeout count if the configuration object has a defined
|
||
|
* timeout property.
|
||
|
*
|
||
|
* @method _startTimeout
|
||
|
* @private
|
||
|
* @param {Object} transaction Transaction object generated by _create().
|
||
|
* @param {Object} timeout Timeout in milliseconds.
|
||
|
*/
|
||
|
_startTimeout: function(transaction, timeout) {
|
||
|
var io = this;
|
||
|
|
||
|
io._timeout[transaction.id] = setTimeout(function() {
|
||
|
io._abort(transaction, 'timeout');
|
||
|
}, timeout);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Clears the timeout interval started by _startTimeout().
|
||
|
*
|
||
|
* @method _clearTimeout
|
||
|
* @private
|
||
|
* @param {Number} id - Transaction id.
|
||
|
*/
|
||
|
_clearTimeout: function(id) {
|
||
|
clearTimeout(this._timeout[id]);
|
||
|
delete this._timeout[id];
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Method that determines if a transaction response qualifies as success
|
||
|
* or failure, based on the response HTTP status code, and fires the
|
||
|
* appropriate success or failure events.
|
||
|
*
|
||
|
* @method _result
|
||
|
* @private
|
||
|
* @static
|
||
|
* @param {Object} transaction Transaction object generated by _create().
|
||
|
* @param {Object} config Configuration object passed to io().
|
||
|
*/
|
||
|
_result: function(transaction, config) {
|
||
|
var status;
|
||
|
// Firefox will throw an exception if attempting to access
|
||
|
// an XHR object's status property, after a request is aborted.
|
||
|
try {
|
||
|
status = transaction.c.status;
|
||
|
} catch(e) {
|
||
|
status = 0;
|
||
|
}
|
||
|
|
||
|
// IE reports HTTP 204 as HTTP 1223.
|
||
|
if (status >= 200 && status < 300 || status === 304 || status === 1223) {
|
||
|
this.success(transaction, config);
|
||
|
} else {
|
||
|
this.failure(transaction, config);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Event handler bound to onreadystatechange.
|
||
|
*
|
||
|
* @method _rS
|
||
|
* @private
|
||
|
* @param {Object} transaction Transaction object generated by _create().
|
||
|
* @param {Object} config Configuration object passed to YUI.io().
|
||
|
*/
|
||
|
_rS: function(transaction, config) {
|
||
|
var io = this;
|
||
|
|
||
|
if (transaction.c.readyState === 4) {
|
||
|
if (config.timeout) {
|
||
|
io._clearTimeout(transaction.id);
|
||
|
}
|
||
|
|
||
|
// Yield in the event of request timeout or abort.
|
||
|
setTimeout(function() {
|
||
|
io.complete(transaction, config);
|
||
|
io._result(transaction, config);
|
||
|
}, 0);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Terminates a transaction due to an explicit abort or timeout.
|
||
|
*
|
||
|
* @method _abort
|
||
|
* @private
|
||
|
* @param {Object} transaction Transaction object generated by _create().
|
||
|
* @param {String} type Identifies timed out or aborted transaction.
|
||
|
*/
|
||
|
_abort: function(transaction, type) {
|
||
|
if (transaction && transaction.c) {
|
||
|
transaction.e = type;
|
||
|
transaction.c.abort();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Requests a transaction. `send()` is implemented as `Y.io()`. Each
|
||
|
* transaction may include a configuration object. Its properties are:
|
||
|
*
|
||
|
* <dl>
|
||
|
* <dt>method</dt>
|
||
|
* <dd>HTTP method verb (e.g., GET or POST). If this property is not
|
||
|
* not defined, the default value will be GET.</dd>
|
||
|
*
|
||
|
* <dt>data</dt>
|
||
|
* <dd>This is the name-value string that will be sent as the
|
||
|
* transaction data. If the request is HTTP GET, the data become
|
||
|
* part of querystring. If HTTP POST, the data are sent in the
|
||
|
* message body.</dd>
|
||
|
*
|
||
|
* <dt>xdr</dt>
|
||
|
* <dd>Defines the transport to be used for cross-domain requests.
|
||
|
* By setting this property, the transaction will use the specified
|
||
|
* transport instead of XMLHttpRequest. The properties of the
|
||
|
* transport object are:
|
||
|
* <dl>
|
||
|
* <dt>use</dt>
|
||
|
* <dd>The transport to be used: 'flash' or 'native'</dd>
|
||
|
* <dt>dataType</dt>
|
||
|
* <dd>Set the value to 'XML' if that is the expected response
|
||
|
* content type.</dd>
|
||
|
* <dt>credentials</dt>
|
||
|
* <dd>Set the value to 'true' to set XHR.withCredentials property to true.</dd>
|
||
|
* </dl></dd>
|
||
|
*
|
||
|
* <dt>form</dt>
|
||
|
* <dd>Form serialization configuration object. Its properties are:
|
||
|
* <dl>
|
||
|
* <dt>id</dt>
|
||
|
* <dd>Node object or id of HTML form</dd>
|
||
|
* <dt>useDisabled</dt>
|
||
|
* <dd>`true` to also serialize disabled form field values
|
||
|
* (defaults to `false`)</dd>
|
||
|
* </dl></dd>
|
||
|
*
|
||
|
* <dt>on</dt>
|
||
|
* <dd>Assigns transaction event subscriptions. Available events are:
|
||
|
* <dl>
|
||
|
* <dt>start</dt>
|
||
|
* <dd>Fires when a request is sent to a resource.</dd>
|
||
|
* <dt>complete</dt>
|
||
|
* <dd>Fires when the transaction is complete.</dd>
|
||
|
* <dt>success</dt>
|
||
|
* <dd>Fires when the HTTP response status is within the 2xx
|
||
|
* range.</dd>
|
||
|
* <dt>failure</dt>
|
||
|
* <dd>Fires when the HTTP response status is outside the 2xx
|
||
|
* range, if an exception occurs, if the transation is aborted,
|
||
|
* or if the transaction exceeds a configured `timeout`.</dd>
|
||
|
* <dt>end</dt>
|
||
|
* <dd>Fires at the conclusion of the transaction
|
||
|
* lifecycle, after `success` or `failure`.</dd>
|
||
|
* </dl>
|
||
|
*
|
||
|
* <p>Callback functions for `start` and `end` receive the id of the
|
||
|
* transaction as a first argument. For `complete`, `success`, and
|
||
|
* `failure`, callbacks receive the id and the response object
|
||
|
* (usually the XMLHttpRequest instance). If the `arguments`
|
||
|
* property was included in the configuration object passed to
|
||
|
* `Y.io()`, the configured data will be passed to all callbacks as
|
||
|
* the last argument.</p>
|
||
|
* </dd>
|
||
|
*
|
||
|
* <dt>sync</dt>
|
||
|
* <dd>Pass `true` to make a same-domain transaction synchronous.
|
||
|
* <strong>CAVEAT</strong>: This will negatively impact the user
|
||
|
* experience. Have a <em>very</em> good reason if you intend to use
|
||
|
* this.</dd>
|
||
|
*
|
||
|
* <dt>context</dt>
|
||
|
* <dd>The "`this'" object for all configured event handlers. If a
|
||
|
* specific context is needed for individual callbacks, bind the
|
||
|
* callback to a context using `Y.bind()`.</dd>
|
||
|
*
|
||
|
* <dt>headers</dt>
|
||
|
* <dd>Object map of transaction headers to send to the server. The
|
||
|
* object keys are the header names and the values are the header
|
||
|
* values.</dd>
|
||
|
*
|
||
|
* <dt>username</dt>
|
||
|
* <dd>Username to use in a HTTP authentication.</dd>
|
||
|
*
|
||
|
* <dt>password</dt>
|
||
|
* <dd>Password to use in a HTTP authentication.</dd>
|
||
|
*
|
||
|
* <dt>timeout</dt>
|
||
|
* <dd>Millisecond threshold for the transaction before being
|
||
|
* automatically aborted.</dd>
|
||
|
*
|
||
|
* <dt>arguments</dt>
|
||
|
* <dd>User-defined data passed to all registered event handlers.
|
||
|
* This value is available as the second argument in the "start" and
|
||
|
* "end" event handlers. It is the third argument in the "complete",
|
||
|
* "success", and "failure" event handlers. <strong>Be sure to quote
|
||
|
* this property name in the transaction configuration as
|
||
|
* "arguments" is a reserved word in JavaScript</strong> (e.g.
|
||
|
* `Y.io({ ..., "arguments": stuff })`).</dd>
|
||
|
* </dl>
|
||
|
*
|
||
|
* @method send
|
||
|
* @public
|
||
|
* @param {String} uri Qualified path to transaction resource.
|
||
|
* @param {Object} config Configuration object for the transaction.
|
||
|
* @param {Number} id Transaction id, if already set.
|
||
|
* @return {Object}
|
||
|
*/
|
||
|
send: function(uri, config, id) {
|
||
|
var transaction, method, i, len, sync, data,
|
||
|
io = this,
|
||
|
u = uri,
|
||
|
response = {};
|
||
|
|
||
|
config = config ? Y.Object(config) : {};
|
||
|
transaction = io._create(config, id);
|
||
|
method = config.method ? config.method.toUpperCase() : 'GET';
|
||
|
sync = config.sync;
|
||
|
data = config.data;
|
||
|
|
||
|
// Serialize a map object into a key-value string using
|
||
|
// querystring-stringify-simple.
|
||
|
if ((Y.Lang.isObject(data) && !data.nodeType) && !transaction.upload) {
|
||
|
if (Y.QueryString && Y.QueryString.stringify) {
|
||
|
Y.log('Stringifying config.data for request', 'info', 'io');
|
||
|
config.data = data = Y.QueryString.stringify(data);
|
||
|
} else {
|
||
|
Y.log('Failed to stringify config.data object, likely because `querystring-stringify-simple` is missing.', 'warn', 'io');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (config.form) {
|
||
|
if (config.form.upload) {
|
||
|
// This is a file upload transaction, calling
|
||
|
// upload() in io-upload-iframe.
|
||
|
return io.upload(transaction, uri, config);
|
||
|
} else {
|
||
|
// Serialize HTML form data into a key-value string.
|
||
|
data = io._serialize(config.form, data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Convert falsy values to an empty string. This way IE can't be
|
||
|
// rediculous and translate `undefined` to "undefined".
|
||
|
data || (data = '');
|
||
|
|
||
|
if (data) {
|
||
|
switch (method) {
|
||
|
case 'GET':
|
||
|
case 'HEAD':
|
||
|
case 'DELETE':
|
||
|
u = io._concat(u, data);
|
||
|
data = '';
|
||
|
Y.log('HTTP' + method + ' with data. The querystring is: ' + u, 'info', 'io');
|
||
|
break;
|
||
|
case 'POST':
|
||
|
case 'PUT':
|
||
|
// If Content-Type is defined in the configuration object, or
|
||
|
// or as a default header, it will be used instead of
|
||
|
// 'application/x-www-form-urlencoded; charset=UTF-8'
|
||
|
config.headers = Y.merge({
|
||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||
|
}, config.headers);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (transaction.xdr) {
|
||
|
// Route data to io-xdr module for flash and XDomainRequest.
|
||
|
return io.xdr(u, transaction, config);
|
||
|
}
|
||
|
else if (transaction.notify) {
|
||
|
// Route data to custom transport
|
||
|
return transaction.c.send(transaction, uri, config);
|
||
|
}
|
||
|
|
||
|
if (!sync && !transaction.upload) {
|
||
|
transaction.c.onreadystatechange = function() {
|
||
|
io._rS(transaction, config);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
// Determine if request is to be set as
|
||
|
// synchronous or asynchronous.
|
||
|
transaction.c.open(method, u, !sync, config.username || null, config.password || null);
|
||
|
io._setHeaders(transaction.c, config.headers || {});
|
||
|
io.start(transaction, config);
|
||
|
|
||
|
// Will work only in browsers that implement the
|
||
|
// Cross-Origin Resource Sharing draft.
|
||
|
if (config.xdr && config.xdr.credentials && SUPPORTS_CORS) {
|
||
|
transaction.c.withCredentials = true;
|
||
|
}
|
||
|
|
||
|
// Using "null" with HTTP POST will result in a request
|
||
|
// with no Content-Length header defined.
|
||
|
transaction.c.send(data);
|
||
|
|
||
|
if (sync) {
|
||
|
// Create a response object for synchronous transactions,
|
||
|
// mixing id and arguments properties with the xhr
|
||
|
// properties whitelist.
|
||
|
for (i = 0, len = XHR_PROPS.length; i < len; ++i) {
|
||
|
response[XHR_PROPS[i]] = transaction.c[XHR_PROPS[i]];
|
||
|
}
|
||
|
|
||
|
response.getAllResponseHeaders = function() {
|
||
|
return transaction.c.getAllResponseHeaders();
|
||
|
};
|
||
|
|
||
|
response.getResponseHeader = function(name) {
|
||
|
return transaction.c.getResponseHeader(name);
|
||
|
};
|
||
|
|
||
|
io.complete(transaction, config);
|
||
|
io._result(transaction, config);
|
||
|
|
||
|
return response;
|
||
|
}
|
||
|
} catch(e) {
|
||
|
if (transaction.xdr) {
|
||
|
// This exception is usually thrown by browsers
|
||
|
// that do not support XMLHttpRequest Level 2.
|
||
|
// Retry the request with the XDR transport set
|
||
|
// to 'flash'. If the Flash transport is not
|
||
|
// initialized or available, the transaction
|
||
|
// will resolve to a transport error.
|
||
|
return io._retry(transaction, uri, config);
|
||
|
} else {
|
||
|
io.complete(transaction, config);
|
||
|
io._result(transaction, config);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If config.timeout is defined, and the request is standard XHR,
|
||
|
// initialize timeout polling.
|
||
|
if (config.timeout) {
|
||
|
io._startTimeout(transaction, config.timeout);
|
||
|
Y.log('Configuration timeout set to: ' + config.timeout, 'info', 'io');
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
id: transaction.id,
|
||
|
abort: function() {
|
||
|
return transaction.c ? io._abort(transaction, 'abort') : false;
|
||
|
},
|
||
|
isInProgress: function() {
|
||
|
return transaction.c ? (transaction.c.readyState % 4) : false;
|
||
|
},
|
||
|
io: io
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
Method for initiating an ajax call. The first argument is the url end
|
||
|
point for the call. The second argument is an object to configure the
|
||
|
transaction and attach event subscriptions. The configuration object
|
||
|
supports the following properties:
|
||
|
|
||
|
<dl>
|
||
|
<dt>method</dt>
|
||
|
<dd>HTTP method verb (e.g., GET or POST). If this property is not
|
||
|
not defined, the default value will be GET.</dd>
|
||
|
|
||
|
<dt>data</dt>
|
||
|
<dd>This is the name-value string that will be sent as the
|
||
|
transaction data. If the request is HTTP GET, the data become
|
||
|
part of querystring. If HTTP POST, the data are sent in the
|
||
|
message body.</dd>
|
||
|
|
||
|
<dt>xdr</dt>
|
||
|
<dd>Defines the transport to be used for cross-domain requests.
|
||
|
By setting this property, the transaction will use the specified
|
||
|
transport instead of XMLHttpRequest. The properties of the
|
||
|
transport object are:
|
||
|
<dl>
|
||
|
<dt>use</dt>
|
||
|
<dd>The transport to be used: 'flash' or 'native'</dd>
|
||
|
<dt>dataType</dt>
|
||
|
<dd>Set the value to 'XML' if that is the expected response
|
||
|
content type.</dd>
|
||
|
</dl></dd>
|
||
|
|
||
|
<dt>form</dt>
|
||
|
<dd>Form serialization configuration object. Its properties are:
|
||
|
<dl>
|
||
|
<dt>id</dt>
|
||
|
<dd>Node object or id of HTML form</dd>
|
||
|
<dt>useDisabled</dt>
|
||
|
<dd>`true` to also serialize disabled form field values
|
||
|
(defaults to `false`)</dd>
|
||
|
</dl></dd>
|
||
|
|
||
|
<dt>on</dt>
|
||
|
<dd>Assigns transaction event subscriptions. Available events are:
|
||
|
<dl>
|
||
|
<dt>start</dt>
|
||
|
<dd>Fires when a request is sent to a resource.</dd>
|
||
|
<dt>complete</dt>
|
||
|
<dd>Fires when the transaction is complete.</dd>
|
||
|
<dt>success</dt>
|
||
|
<dd>Fires when the HTTP response status is within the 2xx
|
||
|
range.</dd>
|
||
|
<dt>failure</dt>
|
||
|
<dd>Fires when the HTTP response status is outside the 2xx
|
||
|
range, if an exception occurs, if the transation is aborted,
|
||
|
or if the transaction exceeds a configured `timeout`.</dd>
|
||
|
<dt>end</dt>
|
||
|
<dd>Fires at the conclusion of the transaction
|
||
|
lifecycle, after `success` or `failure`.</dd>
|
||
|
</dl>
|
||
|
|
||
|
<p>Callback functions for `start` and `end` receive the id of the
|
||
|
transaction as a first argument. For `complete`, `success`, and
|
||
|
`failure`, callbacks receive the id and the response object
|
||
|
(usually the XMLHttpRequest instance). If the `arguments`
|
||
|
property was included in the configuration object passed to
|
||
|
`Y.io()`, the configured data will be passed to all callbacks as
|
||
|
the last argument.</p>
|
||
|
</dd>
|
||
|
|
||
|
<dt>sync</dt>
|
||
|
<dd>Pass `true` to make a same-domain transaction synchronous.
|
||
|
<strong>CAVEAT</strong>: This will negatively impact the user
|
||
|
experience. Have a <em>very</em> good reason if you intend to use
|
||
|
this.</dd>
|
||
|
|
||
|
<dt>context</dt>
|
||
|
<dd>The "`this'" object for all configured event handlers. If a
|
||
|
specific context is needed for individual callbacks, bind the
|
||
|
callback to a context using `Y.bind()`.</dd>
|
||
|
|
||
|
<dt>headers</dt>
|
||
|
<dd>Object map of transaction headers to send to the server. The
|
||
|
object keys are the header names and the values are the header
|
||
|
values.</dd>
|
||
|
|
||
|
<dt>timeout</dt>
|
||
|
<dd>Millisecond threshold for the transaction before being
|
||
|
automatically aborted.</dd>
|
||
|
|
||
|
<dt>arguments</dt>
|
||
|
<dd>User-defined data passed to all registered event handlers.
|
||
|
This value is available as the second argument in the "start" and
|
||
|
"end" event handlers. It is the third argument in the "complete",
|
||
|
"success", and "failure" event handlers. <strong>Be sure to quote
|
||
|
this property name in the transaction configuration as
|
||
|
"arguments" is a reserved word in JavaScript</strong> (e.g.
|
||
|
`Y.io({ ..., "arguments": stuff })`).</dd>
|
||
|
</dl>
|
||
|
|
||
|
@method io
|
||
|
@static
|
||
|
@param {String} url qualified path to transaction resource.
|
||
|
@param {Object} config configuration object for the transaction.
|
||
|
@return {Object}
|
||
|
@for YUI
|
||
|
**/
|
||
|
Y.io = function(url, config) {
|
||
|
// Calling IO through the static interface will use and reuse
|
||
|
// an instance of IO.
|
||
|
var transaction = Y.io._map['io:0'] || new IO();
|
||
|
return transaction.send.apply(transaction, [url, config]);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
Method for setting and deleting IO HTTP headers to be sent with every
|
||
|
request.
|
||
|
|
||
|
Hosted as a property on the `io` function (e.g. `Y.io.header`).
|
||
|
|
||
|
@method header
|
||
|
@param {String} name HTTP header
|
||
|
@param {String} value HTTP header value
|
||
|
@static
|
||
|
**/
|
||
|
Y.io.header = function(name, value) {
|
||
|
// Calling IO through the static interface will use and reuse
|
||
|
// an instance of IO.
|
||
|
var transaction = Y.io._map['io:0'] || new IO();
|
||
|
transaction.setHeader(name, value);
|
||
|
};
|
||
|
|
||
|
Y.IO = IO;
|
||
|
// Map of all IO instances created.
|
||
|
Y.io._map = {};
|
||
|
var XHR = win && win.XMLHttpRequest,
|
||
|
XDR = win && win.XDomainRequest,
|
||
|
AX = win && win.ActiveXObject,
|
||
|
|
||
|
// Checks for the presence of the `withCredentials` in an XHR instance
|
||
|
// object, which will be present if the environment supports CORS.
|
||
|
SUPPORTS_CORS = XHR && 'withCredentials' in (new XMLHttpRequest());
|
||
|
|
||
|
|
||
|
Y.mix(Y.IO, {
|
||
|
/**
|
||
|
* The ID of the default IO transport, defaults to `xhr`
|
||
|
* @property _default
|
||
|
* @type {String}
|
||
|
* @static
|
||
|
*/
|
||
|
_default: 'xhr',
|
||
|
/**
|
||
|
*
|
||
|
* @method defaultTransport
|
||
|
* @static
|
||
|
* @param {String} [id] The transport to set as the default, if empty a new transport is created.
|
||
|
* @return {Object} The transport object with a `send` method
|
||
|
*/
|
||
|
defaultTransport: function(id) {
|
||
|
if (id) {
|
||
|
Y.log('Setting default IO to: ' + id, 'info', 'io');
|
||
|
Y.IO._default = id;
|
||
|
} else {
|
||
|
var o = {
|
||
|
c: Y.IO.transports[Y.IO._default](),
|
||
|
notify: Y.IO._default === 'xhr' ? false : true
|
||
|
};
|
||
|
Y.log('Creating default transport: ' + Y.IO._default, 'info', 'io');
|
||
|
return o;
|
||
|
}
|
||
|
},
|
||
|
/**
|
||
|
* An object hash of custom transports available to IO
|
||
|
* @property transports
|
||
|
* @type {Object}
|
||
|
* @static
|
||
|
*/
|
||
|
transports: {
|
||
|
xhr: function () {
|
||
|
return XHR ? new XMLHttpRequest() :
|
||
|
AX ? new ActiveXObject('Microsoft.XMLHTTP') : null;
|
||
|
},
|
||
|
xdr: function () {
|
||
|
return XDR ? new XDomainRequest() : null;
|
||
|
},
|
||
|
iframe: function () { return {}; },
|
||
|
flash: null,
|
||
|
nodejs: null
|
||
|
},
|
||
|
/**
|
||
|
* Create a custom transport of type and return it's object
|
||
|
* @method customTransport
|
||
|
* @param {String} id The id of the transport to create.
|
||
|
* @static
|
||
|
*/
|
||
|
customTransport: function(id) {
|
||
|
var o = { c: Y.IO.transports[id]() };
|
||
|
|
||
|
o[(id === 'xdr' || id === 'flash') ? 'xdr' : 'notify'] = true;
|
||
|
return o;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Y.mix(Y.IO.prototype, {
|
||
|
/**
|
||
|
* Fired from the notify method of the transport which in turn fires
|
||
|
* the event on the IO object.
|
||
|
* @method notify
|
||
|
* @param {String} event The name of the event
|
||
|
* @param {Object} transaction The transaction object
|
||
|
* @param {Object} config The configuration object for this transaction
|
||
|
*/
|
||
|
notify: function(event, transaction, config) {
|
||
|
var io = this;
|
||
|
|
||
|
switch (event) {
|
||
|
case 'timeout':
|
||
|
case 'abort':
|
||
|
case 'transport error':
|
||
|
transaction.c = { status: 0, statusText: event };
|
||
|
event = 'failure';
|
||
|
default:
|
||
|
io[event].apply(io, [transaction, config]);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
}, '3.17.2', {"requires": ["event-custom-base", "querystring-stringify-simple"]});
|