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.
399 lines
11 KiB
399 lines
11 KiB
define([ |
|
'../errors/RequestError', |
|
'./watch', |
|
'./handlers', |
|
'./util', |
|
'../has'/*=====, |
|
'../request', |
|
'../_base/declare' =====*/ |
|
], function(RequestError, watch, handlers, util, has/*=====, request, declare =====*/){ |
|
has.add('native-xhr', function(){ |
|
// if true, the environment has a native XHR implementation |
|
return typeof XMLHttpRequest !== 'undefined'; |
|
}); |
|
has.add('dojo-force-activex-xhr', function(){ |
|
return has('activex') && window.location.protocol === 'file:'; |
|
}); |
|
|
|
has.add('native-xhr2', function(){ |
|
if(!has('native-xhr') || has('dojo-force-activex-xhr')){ return; } |
|
var x = new XMLHttpRequest(); |
|
return typeof x['addEventListener'] !== 'undefined' && |
|
(typeof opera === 'undefined' || typeof x['upload'] !== 'undefined'); |
|
}); |
|
|
|
has.add('native-formdata', function(){ |
|
// if true, the environment has a native FormData implementation |
|
return typeof FormData !== 'undefined'; |
|
}); |
|
|
|
has.add('native-blob', function(){ |
|
// if true, the environment has a native Blob implementation |
|
return typeof Blob !== 'undefined'; |
|
}); |
|
|
|
has.add('native-arraybuffer', function(){ |
|
// if true, the environment has a native ArrayBuffer implementation |
|
return typeof ArrayBuffer !== 'undefined'; |
|
}); |
|
|
|
has.add('native-response-type', function(){ |
|
return has('native-xhr') && typeof new XMLHttpRequest().responseType !== 'undefined'; |
|
}); |
|
|
|
has.add('native-xhr2-blob', function(){ |
|
if(!has('native-response-type')){ return; } |
|
var x = new XMLHttpRequest(); |
|
// The URL used here does not have to be reachable as the XHR's `send` method is never called. |
|
// It does need to be parsable/resolvable in all cases, so it should be an absolute URL. |
|
// XMLHttpRequest within a Worker created from a Blob does not support relative URL paths. |
|
x.open('GET', 'https://dojotoolkit.org/', true); |
|
x.responseType = 'blob'; |
|
// will not be set if unsupported |
|
var responseType = x.responseType; |
|
x.abort(); |
|
return responseType === 'blob'; |
|
}); |
|
|
|
// Google Chrome doesn't support "json" response type |
|
// up to version 30, so it's intentionally not included here |
|
var nativeResponseTypes = { |
|
'blob': has('native-xhr2-blob') ? 'blob' : 'arraybuffer', |
|
'document': 'document', |
|
'arraybuffer': 'arraybuffer' |
|
}; |
|
|
|
function handleResponse(response, error){ |
|
var _xhr = response.xhr; |
|
response.status = response.xhr.status; |
|
|
|
try { |
|
// Firefox throws an error when trying to access |
|
// xhr.responseText if response isn't text |
|
response.text = _xhr.responseText; |
|
} catch (e) {} |
|
|
|
if(response.options.handleAs === 'xml'){ |
|
response.data = _xhr.responseXML; |
|
} |
|
|
|
var handleError; |
|
if(error){ |
|
this.reject(error); |
|
}else{ |
|
try{ |
|
handlers(response); |
|
}catch(e){ |
|
handleError = e; |
|
} |
|
if(util.checkStatus(_xhr.status)){ |
|
if(!handleError){ |
|
this.resolve(response); |
|
}else{ |
|
this.reject(handleError); |
|
} |
|
}else{ |
|
if(!handleError){ |
|
error = new RequestError('Unable to load ' + response.url + ' status: ' + _xhr.status, response); |
|
this.reject(error); |
|
}else{ |
|
error = new RequestError('Unable to load ' + response.url + ' status: ' + _xhr.status + |
|
' and an error in handleAs: transformation of response', response); |
|
this.reject(error); |
|
} |
|
} |
|
} |
|
} |
|
|
|
var isValid, isReady, addListeners, cancel; |
|
if(has('native-xhr2')){ |
|
// Any platform with XHR2 will only use the watch mechanism for timeout. |
|
|
|
isValid = function(response){ |
|
// summary: |
|
// Check to see if the request should be taken out of the watch queue |
|
return !this.isFulfilled(); |
|
}; |
|
cancel = function(dfd, response){ |
|
// summary: |
|
// Canceler for deferred |
|
response.xhr.abort(); |
|
}; |
|
addListeners = function(_xhr, dfd, response, uploadProgress){ |
|
// summary: |
|
// Adds event listeners to the XMLHttpRequest object |
|
function onLoad(evt){ |
|
dfd.handleResponse(response); |
|
} |
|
function onError(evt){ |
|
var _xhr = evt.target; |
|
var error = new RequestError('Unable to load ' + response.url + ' status: ' + _xhr.status, response); |
|
dfd.handleResponse(response, error); |
|
} |
|
|
|
function onProgress(transferType, evt){ |
|
response.transferType = transferType; |
|
if(evt.lengthComputable){ |
|
response.loaded = evt.loaded; |
|
response.total = evt.total; |
|
dfd.progress(response); |
|
} else if(response.xhr.readyState === 3){ |
|
response.loaded = ('loaded' in evt) ? evt.loaded : evt.position; |
|
dfd.progress(response); |
|
} |
|
} |
|
|
|
function onDownloadProgress(evt) { |
|
return onProgress('download', evt); |
|
} |
|
|
|
function onUploadProgress(evt) { |
|
return onProgress('upload', evt); |
|
} |
|
|
|
_xhr.addEventListener('load', onLoad, false); |
|
_xhr.addEventListener('error', onError, false); |
|
_xhr.addEventListener('progress', onDownloadProgress, false); |
|
|
|
if (uploadProgress && _xhr.upload) { |
|
_xhr.upload.addEventListener('progress', onUploadProgress, false); |
|
} |
|
|
|
return function(){ |
|
_xhr.removeEventListener('load', onLoad, false); |
|
_xhr.removeEventListener('error', onError, false); |
|
_xhr.removeEventListener('progress', onDownloadProgress, false); |
|
_xhr.upload.removeEventListener('progress', onUploadProgress, false); |
|
_xhr = null; |
|
}; |
|
}; |
|
}else{ |
|
isValid = function(response){ |
|
return response.xhr.readyState; //boolean |
|
}; |
|
isReady = function(response){ |
|
return 4 === response.xhr.readyState; //boolean |
|
}; |
|
cancel = function(dfd, response){ |
|
// summary: |
|
// canceller function for util.deferred call. |
|
var xhr = response.xhr; |
|
var _at = typeof xhr.abort; |
|
if(_at === 'function' || _at === 'object' || _at === 'unknown'){ |
|
xhr.abort(); |
|
} |
|
}; |
|
} |
|
|
|
function getHeader(headerName){ |
|
return this.xhr.getResponseHeader(headerName); |
|
} |
|
|
|
var undefined, |
|
defaultOptions = { |
|
data: null, |
|
query: null, |
|
sync: false, |
|
method: 'GET' |
|
}; |
|
function xhr(url, options, returnDeferred){ |
|
var isFormData = has('native-formdata') && options && options.data && options.data instanceof FormData; |
|
var response = util.parseArgs( |
|
url, |
|
util.deepCreate(defaultOptions, options), |
|
isFormData |
|
); |
|
url = response.url; |
|
options = response.options; |
|
var hasNoData = !options.data && options.method !== 'POST' && options.method !== 'PUT'; |
|
|
|
if(has('ie') <= 10){ |
|
// older IE breaks point 9 in http://www.w3.org/TR/XMLHttpRequest/#the-open()-method and sends fragment, so strip it |
|
url = url.split('#')[0]; |
|
} |
|
|
|
var remover, |
|
last = function(){ |
|
remover && remover(); |
|
}; |
|
|
|
//Make the Deferred object for this xhr request. |
|
var dfd = util.deferred( |
|
response, |
|
cancel, |
|
isValid, |
|
isReady, |
|
handleResponse, |
|
last |
|
); |
|
var _xhr = response.xhr = xhr._create(); |
|
|
|
if(!_xhr){ |
|
// If XHR factory somehow returns nothings, |
|
// cancel the deferred. |
|
dfd.cancel(new RequestError('XHR was not created')); |
|
return returnDeferred ? dfd : dfd.promise; |
|
} |
|
|
|
response.getHeader = getHeader; |
|
|
|
if(addListeners){ |
|
remover = addListeners(_xhr, dfd, response, options.uploadProgress); |
|
} |
|
|
|
// IE11 treats data: undefined different than other browsers |
|
var data = typeof(options.data) === 'undefined' ? null : options.data, |
|
async = !options.sync, |
|
method = options.method; |
|
|
|
try{ |
|
// IE6 won't let you call apply() on the native function. |
|
_xhr.open(method, url, async, options.user || undefined, options.password || undefined); |
|
|
|
if(options.withCredentials){ |
|
_xhr.withCredentials = options.withCredentials; |
|
} |
|
|
|
if(has('native-response-type') && options.handleAs in nativeResponseTypes) { |
|
_xhr.responseType = nativeResponseTypes[options.handleAs]; |
|
} |
|
|
|
var headers = options.headers, |
|
contentType = (isFormData || hasNoData) ? false : 'application/x-www-form-urlencoded'; |
|
if(headers){ |
|
for(var hdr in headers){ |
|
if(hdr.toLowerCase() === 'content-type'){ |
|
contentType = headers[hdr]; |
|
}else if(headers[hdr]){ |
|
//Only add header if it has a value. This allows for instance, skipping |
|
//insertion of X-Requested-With by specifying empty value. |
|
_xhr.setRequestHeader(hdr, headers[hdr]); |
|
} |
|
} |
|
} |
|
|
|
if(contentType && contentType !== false){ |
|
_xhr.setRequestHeader('Content-Type', contentType); |
|
} |
|
if(!headers || !('X-Requested-With' in headers)){ |
|
_xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); |
|
} |
|
|
|
if(util.notify){ |
|
util.notify.emit('send', response, dfd.promise.cancel); |
|
} |
|
_xhr.send(data); |
|
}catch(e){ |
|
dfd.reject(e); |
|
} |
|
|
|
watch(dfd); |
|
_xhr = null; |
|
|
|
return returnDeferred ? dfd : dfd.promise; |
|
} |
|
|
|
/*===== |
|
xhr = function(url, options){ |
|
// summary: |
|
// Sends a request using XMLHttpRequest with the given URL and options. |
|
// url: String |
|
// URL to request |
|
// options: dojo/request/xhr.__Options? |
|
// Options for the request. |
|
// returns: dojo/request.__Promise |
|
}; |
|
xhr.__BaseOptions = declare(request.__BaseOptions, { |
|
// sync: Boolean? |
|
// Whether to make a synchronous request or not. Default |
|
// is `false` (asynchronous). |
|
// data: String|Object|FormData? |
|
// Data to transfer. This is ignored for GET and DELETE |
|
// requests. |
|
// headers: Object? |
|
// Headers to use for the request. |
|
// user: String? |
|
// Username to use during the request. |
|
// password: String? |
|
// Password to use during the request. |
|
// withCredentials: Boolean? |
|
// For cross-site requests, whether to send credentials |
|
// or not. |
|
// uploadProgress: Boolean? |
|
// Upload progress events cause preflighted requests. This |
|
// option enables upload progress event support but also |
|
// causes all requests to be preflighted. |
|
}); |
|
xhr.__MethodOptions = declare(null, { |
|
// method: String? |
|
// The HTTP method to use to make the request. Must be |
|
// uppercase. Default is `"GET"`. |
|
}); |
|
xhr.__Options = declare([xhr.__BaseOptions, xhr.__MethodOptions]); |
|
|
|
xhr.get = function(url, options){ |
|
// summary: |
|
// Send an HTTP GET request using XMLHttpRequest with the given URL and options. |
|
// url: String |
|
// URL to request |
|
// options: dojo/request/xhr.__BaseOptions? |
|
// Options for the request. |
|
// returns: dojo/request.__Promise |
|
}; |
|
xhr.post = function(url, options){ |
|
// summary: |
|
// Send an HTTP POST request using XMLHttpRequest with the given URL and options. |
|
// url: String |
|
// URL to request |
|
// options: dojo/request/xhr.__BaseOptions? |
|
// Options for the request. |
|
// returns: dojo/request.__Promise |
|
}; |
|
xhr.put = function(url, options){ |
|
// summary: |
|
// Send an HTTP PUT request using XMLHttpRequest with the given URL and options. |
|
// url: String |
|
// URL to request |
|
// options: dojo/request/xhr.__BaseOptions? |
|
// Options for the request. |
|
// returns: dojo/request.__Promise |
|
}; |
|
xhr.del = function(url, options){ |
|
// summary: |
|
// Send an HTTP DELETE request using XMLHttpRequest with the given URL and options. |
|
// url: String |
|
// URL to request |
|
// options: dojo/request/xhr.__BaseOptions? |
|
// Options for the request. |
|
// returns: dojo/request.__Promise |
|
}; |
|
=====*/ |
|
xhr._create = function(){ |
|
// summary: |
|
// does the work of portably generating a new XMLHTTPRequest object. |
|
throw new Error('XMLHTTP not available'); |
|
}; |
|
if(has('native-xhr') && !has('dojo-force-activex-xhr')){ |
|
xhr._create = function(){ |
|
return new XMLHttpRequest(); |
|
}; |
|
}else if(has('activex')){ |
|
try{ |
|
new ActiveXObject('Msxml2.XMLHTTP'); |
|
xhr._create = function(){ |
|
return new ActiveXObject('Msxml2.XMLHTTP'); |
|
}; |
|
}catch(e){ |
|
try{ |
|
new ActiveXObject('Microsoft.XMLHTTP'); |
|
xhr._create = function(){ |
|
return new ActiveXObject('Microsoft.XMLHTTP'); |
|
}; |
|
}catch(e){} |
|
} |
|
} |
|
|
|
util.addCommonMethods(xhr); |
|
|
|
return xhr; |
|
});
|
|
|