UNPKG

dojo

Version:

Dojo core is a powerful, lightweight library that makes common tasks quicker and easier. Animate elements, manipulate the DOM, and query with easy CSS syntax, all without sacrificing performance.

400 lines (359 loc) 11.6 kB
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; });