* Send `data` as the request body, defaulting the `.type()` to "json" when
* an object is given.
*
* Examples:
*
* // manual json
* request.post('/user')
* .type('json')
* .send('{"name":"tj"}')
* .end(callback)
*
* // auto json
* request.post('/user')
* .send({ name: 'tj' })
* .end(callback)
*
* // manual x-www-form-urlencoded
* request.post('/user')
* .type('form')
* .send('name=tj')
* .end(callback)
*
* // auto x-www-form-urlencoded
* request.post('/user')
* .type('form')
* .send({ name: 'tj' })
* .end(callback)
*
* // defaults to x-www-form-urlencoded
* request.post('/user')
* .send('name=tobi')
* .send('species=ferret')
* .end(callback)
*
* @param {String|Object} data
* @return {Request} for chaining
* @api public
*/
Request.prototype.send=function(data){
varobj=isObject(data);
vartype=this.getHeader('Content-Type');
// merge
if(obj&&isObject(this._data)){
for(varkeyindata){
this._data[key]=data[key];
}
}elseif('string'==typeofdata){
if(!type)this.type('form');
type=this.getHeader('Content-Type');
if('application/x-www-form-urlencoded'==type){
this._data=this._data
?this._data+'&'+data
:data;
}else{
this._data=(this._data||'')+data;
}
}else{
this._data=data;
}
if(!obj||isHost(data))returnthis;
if(!type)this.type('json');
returnthis;
};
/**
* Invoke the callback with `err` and `res`
* and handle arity check.
*
* @param {Error} err
* @param {Response} res
* @api private
*/
Request.prototype.callback=function(err,res){
varfn=this._callback;
this.clearTimeout();
fn(err,res);
};
/**
* Invoke callback with x-domain error.
*
* @api private
*/
Request.prototype.crossDomainError=function(){
varerr=newError('Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.');
err.crossDomain=true;
err.status=this.status;
err.method=this.method;
err.url=this.url;
this.callback(err);
};
/**
* Invoke callback with timeout error.
*
* @api private
*/
Request.prototype.timeoutError=function(){
vartimeout=this._timeout;
varerr=newError('timeout of '+timeout+'ms exceeded');
err.timeout=timeout;
this.callback(err);
};
/**
* Enable transmission of cookies with x-domain requests.
*
* Note that for this to work the origin must not be
* using "Access-Control-Allow-Origin" with a wildcard,
* and also must set "Access-Control-Allow-Credentials"
* to "true".
*
* @api public
*/
Request.prototype.withCredentials=function(){
this._withCredentials=true;
returnthis;
};
/**
* Initiate request, invoking callback `fn(res)`
* with an instanceof `Response`.
*
* @param {Function} fn
* @return {Request} for chaining
* @api public
*/
Request.prototype.end=function(fn){
varself=this;
varxhr=this.xhr=request.getXHR();
varquery=this._query.join('&');
vartimeout=this._timeout;
vardata=this._formData||this._data;
// store callback
this._callback=fn||noop;
// state change
xhr.onreadystatechange=function(){
if(4!=xhr.readyState)return;
// In IE9, reads to any property (e.g. status) off of an aborted XHR will
// result in the error "Could not complete the operation due to error c00c023f"
varstatus;
try{status=xhr.status}catch(e){status=0;}
if(0==status){
if(self.timedout)returnself.timeoutError();
if(self.aborted)return;
returnself.crossDomainError();
}
self.emit('end');
};
// progress
varhandleProgress=function(e){
if(e.total>0){
e.percent=e.loaded/e.total*100;
}
e.direction='download';
self.emit('progress',e);
};
if(this.hasListeners('progress')){
xhr.onprogress=handleProgress;
}
try{
if(xhr.upload&&this.hasListeners('progress')){
xhr.upload.onprogress=handleProgress;
}
}catch(e){
// Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.