[483] | 1 | define([ |
---|
| 2 | '../errors/RequestError', |
---|
| 3 | './watch', |
---|
| 4 | './handlers', |
---|
| 5 | './util', |
---|
| 6 | '../has'/*=====, |
---|
| 7 | '../request', |
---|
| 8 | '../_base/declare' =====*/ |
---|
| 9 | ], function(RequestError, watch, handlers, util, has/*=====, request, declare =====*/){ |
---|
| 10 | has.add('native-xhr', function(){ |
---|
| 11 | // if true, the environment has a native XHR implementation |
---|
| 12 | return typeof XMLHttpRequest !== 'undefined'; |
---|
| 13 | }); |
---|
| 14 | has.add('dojo-force-activex-xhr', function(){ |
---|
| 15 | return has('activex') && !document.addEventListener && window.location.protocol === 'file:'; |
---|
| 16 | }); |
---|
| 17 | |
---|
| 18 | has.add('native-xhr2', function(){ |
---|
| 19 | if(!has('native-xhr')){ return; } |
---|
| 20 | var x = new XMLHttpRequest(); |
---|
| 21 | return typeof x['addEventListener'] !== 'undefined' && |
---|
| 22 | (typeof opera === 'undefined' || typeof x['upload'] !== 'undefined'); |
---|
| 23 | }); |
---|
| 24 | |
---|
| 25 | has.add('native-formdata', function(){ |
---|
| 26 | // if true, the environment has a native FormData implementation |
---|
| 27 | return typeof FormData === 'function'; |
---|
| 28 | }); |
---|
| 29 | |
---|
| 30 | function handleResponse(response, error){ |
---|
| 31 | var _xhr = response.xhr; |
---|
| 32 | response.status = response.xhr.status; |
---|
| 33 | response.text = _xhr.responseText; |
---|
| 34 | |
---|
| 35 | if(response.options.handleAs === 'xml'){ |
---|
| 36 | response.data = _xhr.responseXML; |
---|
| 37 | } |
---|
| 38 | |
---|
| 39 | if(!error){ |
---|
| 40 | try{ |
---|
| 41 | handlers(response); |
---|
| 42 | }catch(e){ |
---|
| 43 | error = e; |
---|
| 44 | } |
---|
| 45 | } |
---|
| 46 | |
---|
| 47 | if(error){ |
---|
| 48 | this.reject(error); |
---|
| 49 | }else if(util.checkStatus(_xhr.status)){ |
---|
| 50 | this.resolve(response); |
---|
| 51 | }else{ |
---|
| 52 | error = new RequestError('Unable to load ' + response.url + ' status: ' + _xhr.status, response); |
---|
| 53 | |
---|
| 54 | this.reject(error); |
---|
| 55 | } |
---|
| 56 | } |
---|
| 57 | |
---|
| 58 | var isValid, isReady, addListeners, cancel; |
---|
| 59 | if(has('native-xhr2')){ |
---|
| 60 | // Any platform with XHR2 will only use the watch mechanism for timeout. |
---|
| 61 | |
---|
| 62 | isValid = function(response){ |
---|
| 63 | // summary: |
---|
| 64 | // Check to see if the request should be taken out of the watch queue |
---|
| 65 | return !this.isFulfilled(); |
---|
| 66 | }; |
---|
| 67 | cancel = function(dfd, response){ |
---|
| 68 | // summary: |
---|
| 69 | // Canceler for deferred |
---|
| 70 | response.xhr.abort(); |
---|
| 71 | }; |
---|
| 72 | addListeners = function(_xhr, dfd, response){ |
---|
| 73 | // summary: |
---|
| 74 | // Adds event listeners to the XMLHttpRequest object |
---|
| 75 | function onLoad(evt){ |
---|
| 76 | dfd.handleResponse(response); |
---|
| 77 | } |
---|
| 78 | function onError(evt){ |
---|
| 79 | var _xhr = evt.target; |
---|
| 80 | var error = new RequestError('Unable to load ' + response.url + ' status: ' + _xhr.status, response); |
---|
| 81 | dfd.handleResponse(response, error); |
---|
| 82 | } |
---|
| 83 | |
---|
| 84 | function onProgress(evt){ |
---|
| 85 | if(evt.lengthComputable){ |
---|
| 86 | response.loaded = evt.loaded; |
---|
| 87 | response.total = evt.total; |
---|
| 88 | dfd.progress(response); |
---|
| 89 | } |
---|
| 90 | } |
---|
| 91 | |
---|
| 92 | _xhr.addEventListener('load', onLoad, false); |
---|
| 93 | _xhr.addEventListener('error', onError, false); |
---|
| 94 | _xhr.addEventListener('progress', onProgress, false); |
---|
| 95 | |
---|
| 96 | return function(){ |
---|
| 97 | _xhr.removeEventListener('load', onLoad, false); |
---|
| 98 | _xhr.removeEventListener('error', onError, false); |
---|
| 99 | _xhr.removeEventListener('progress', onProgress, false); |
---|
| 100 | _xhr = null; |
---|
| 101 | }; |
---|
| 102 | }; |
---|
| 103 | }else{ |
---|
| 104 | isValid = function(response){ |
---|
| 105 | return response.xhr.readyState; //boolean |
---|
| 106 | }; |
---|
| 107 | isReady = function(response){ |
---|
| 108 | return 4 === response.xhr.readyState; //boolean |
---|
| 109 | }; |
---|
| 110 | cancel = function(dfd, response){ |
---|
| 111 | // summary: |
---|
| 112 | // canceller function for util.deferred call. |
---|
| 113 | var xhr = response.xhr; |
---|
| 114 | var _at = typeof xhr.abort; |
---|
| 115 | if(_at === 'function' || _at === 'object' || _at === 'unknown'){ |
---|
| 116 | xhr.abort(); |
---|
| 117 | } |
---|
| 118 | }; |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | function getHeader(headerName){ |
---|
| 122 | return this.xhr.getResponseHeader(headerName); |
---|
| 123 | } |
---|
| 124 | |
---|
| 125 | var undefined, |
---|
| 126 | defaultOptions = { |
---|
| 127 | data: null, |
---|
| 128 | query: null, |
---|
| 129 | sync: false, |
---|
| 130 | method: 'GET' |
---|
| 131 | }; |
---|
| 132 | function xhr(url, options, returnDeferred){ |
---|
| 133 | var response = util.parseArgs( |
---|
| 134 | url, |
---|
| 135 | util.deepCreate(defaultOptions, options), |
---|
| 136 | has('native-formdata') && options && options.data && options.data instanceof FormData |
---|
| 137 | ); |
---|
| 138 | url = response.url; |
---|
| 139 | options = response.options; |
---|
| 140 | |
---|
| 141 | var remover, |
---|
| 142 | last = function(){ |
---|
| 143 | remover && remover(); |
---|
| 144 | }; |
---|
| 145 | |
---|
| 146 | //Make the Deferred object for this xhr request. |
---|
| 147 | var dfd = util.deferred( |
---|
| 148 | response, |
---|
| 149 | cancel, |
---|
| 150 | isValid, |
---|
| 151 | isReady, |
---|
| 152 | handleResponse, |
---|
| 153 | last |
---|
| 154 | ); |
---|
| 155 | var _xhr = response.xhr = xhr._create(); |
---|
| 156 | |
---|
| 157 | if(!_xhr){ |
---|
| 158 | // If XHR factory somehow returns nothings, |
---|
| 159 | // cancel the deferred. |
---|
| 160 | dfd.cancel(new RequestError('XHR was not created')); |
---|
| 161 | return returnDeferred ? dfd : dfd.promise; |
---|
| 162 | } |
---|
| 163 | |
---|
| 164 | response.getHeader = getHeader; |
---|
| 165 | |
---|
| 166 | if(addListeners){ |
---|
| 167 | remover = addListeners(_xhr, dfd, response); |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | var data = options.data, |
---|
| 171 | async = !options.sync, |
---|
| 172 | method = options.method; |
---|
| 173 | |
---|
| 174 | try{ |
---|
| 175 | // IE6 won't let you call apply() on the native function. |
---|
| 176 | _xhr.open(method, url, async, options.user || undefined, options.password || undefined); |
---|
| 177 | |
---|
| 178 | if(options.withCredentials){ |
---|
| 179 | _xhr.withCredentials = options.withCredentials; |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | var headers = options.headers, |
---|
| 183 | contentType = 'application/x-www-form-urlencoded'; |
---|
| 184 | if(headers){ |
---|
| 185 | for(var hdr in headers){ |
---|
| 186 | if(hdr.toLowerCase() === 'content-type'){ |
---|
| 187 | contentType = headers[hdr]; |
---|
| 188 | }else if(headers[hdr]){ |
---|
| 189 | //Only add header if it has a value. This allows for instance, skipping |
---|
| 190 | //insertion of X-Requested-With by specifying empty value. |
---|
| 191 | _xhr.setRequestHeader(hdr, headers[hdr]); |
---|
| 192 | } |
---|
| 193 | } |
---|
| 194 | } |
---|
| 195 | |
---|
| 196 | if(contentType && contentType !== false){ |
---|
| 197 | _xhr.setRequestHeader('Content-Type', contentType); |
---|
| 198 | } |
---|
| 199 | if(!headers || !('X-Requested-With' in headers)){ |
---|
| 200 | _xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); |
---|
| 201 | } |
---|
| 202 | |
---|
| 203 | if(util.notify){ |
---|
| 204 | util.notify.emit('send', response, dfd.promise.cancel); |
---|
| 205 | } |
---|
| 206 | _xhr.send(data); |
---|
| 207 | }catch(e){ |
---|
| 208 | dfd.reject(e); |
---|
| 209 | } |
---|
| 210 | |
---|
| 211 | watch(dfd); |
---|
| 212 | _xhr = null; |
---|
| 213 | |
---|
| 214 | return returnDeferred ? dfd : dfd.promise; |
---|
| 215 | } |
---|
| 216 | |
---|
| 217 | /*===== |
---|
| 218 | xhr = function(url, options){ |
---|
| 219 | // summary: |
---|
| 220 | // Sends a request using XMLHttpRequest with the given URL and options. |
---|
| 221 | // url: String |
---|
| 222 | // URL to request |
---|
| 223 | // options: dojo/request/xhr.__Options? |
---|
| 224 | // Options for the request. |
---|
| 225 | // returns: dojo/request.__Promise |
---|
| 226 | }; |
---|
| 227 | xhr.__BaseOptions = declare(request.__BaseOptions, { |
---|
| 228 | // sync: Boolean? |
---|
| 229 | // Whether to make a synchronous request or not. Default |
---|
| 230 | // is `false` (asynchronous). |
---|
| 231 | // data: String|Object|FormData? |
---|
| 232 | // Data to transfer. This is ignored for GET and DELETE |
---|
| 233 | // requests. |
---|
| 234 | // headers: Object? |
---|
| 235 | // Headers to use for the request. |
---|
| 236 | // user: String? |
---|
| 237 | // Username to use during the request. |
---|
| 238 | // password: String? |
---|
| 239 | // Password to use during the request. |
---|
| 240 | // withCredentials: Boolean? |
---|
| 241 | // For cross-site requests, whether to send credentials |
---|
| 242 | // or not. |
---|
| 243 | }); |
---|
| 244 | xhr.__MethodOptions = declare(null, { |
---|
| 245 | // method: String? |
---|
| 246 | // The HTTP method to use to make the request. Must be |
---|
| 247 | // uppercase. Default is `"GET"`. |
---|
| 248 | }); |
---|
| 249 | xhr.__Options = declare([xhr.__BaseOptions, xhr.__MethodOptions]); |
---|
| 250 | |
---|
| 251 | xhr.get = function(url, options){ |
---|
| 252 | // summary: |
---|
| 253 | // Send an HTTP GET request using XMLHttpRequest with the given URL and options. |
---|
| 254 | // url: String |
---|
| 255 | // URL to request |
---|
| 256 | // options: dojo/request/xhr.__BaseOptions? |
---|
| 257 | // Options for the request. |
---|
| 258 | // returns: dojo/request.__Promise |
---|
| 259 | }; |
---|
| 260 | xhr.post = function(url, options){ |
---|
| 261 | // summary: |
---|
| 262 | // Send an HTTP POST request using XMLHttpRequest with the given URL and options. |
---|
| 263 | // url: String |
---|
| 264 | // URL to request |
---|
| 265 | // options: dojo/request/xhr.__BaseOptions? |
---|
| 266 | // Options for the request. |
---|
| 267 | // returns: dojo/request.__Promise |
---|
| 268 | }; |
---|
| 269 | xhr.put = function(url, options){ |
---|
| 270 | // summary: |
---|
| 271 | // Send an HTTP PUT request using XMLHttpRequest with the given URL and options. |
---|
| 272 | // url: String |
---|
| 273 | // URL to request |
---|
| 274 | // options: dojo/request/xhr.__BaseOptions? |
---|
| 275 | // Options for the request. |
---|
| 276 | // returns: dojo/request.__Promise |
---|
| 277 | }; |
---|
| 278 | xhr.del = function(url, options){ |
---|
| 279 | // summary: |
---|
| 280 | // Send an HTTP DELETE request using XMLHttpRequest with the given URL and options. |
---|
| 281 | // url: String |
---|
| 282 | // URL to request |
---|
| 283 | // options: dojo/request/xhr.__BaseOptions? |
---|
| 284 | // Options for the request. |
---|
| 285 | // returns: dojo/request.__Promise |
---|
| 286 | }; |
---|
| 287 | =====*/ |
---|
| 288 | xhr._create = function(){ |
---|
| 289 | // summary: |
---|
| 290 | // does the work of portably generating a new XMLHTTPRequest object. |
---|
| 291 | throw new Error('XMLHTTP not available'); |
---|
| 292 | }; |
---|
| 293 | if(has('native-xhr') && !has('dojo-force-activex-xhr')){ |
---|
| 294 | xhr._create = function(){ |
---|
| 295 | return new XMLHttpRequest(); |
---|
| 296 | }; |
---|
| 297 | }else if(has('activex')){ |
---|
| 298 | try{ |
---|
| 299 | new ActiveXObject('Msxml2.XMLHTTP'); |
---|
| 300 | xhr._create = function(){ |
---|
| 301 | return new ActiveXObject('Msxml2.XMLHTTP'); |
---|
| 302 | }; |
---|
| 303 | }catch(e){ |
---|
| 304 | try{ |
---|
| 305 | new ActiveXObject('Microsoft.XMLHTTP'); |
---|
| 306 | xhr._create = function(){ |
---|
| 307 | return new ActiveXObject('Microsoft.XMLHTTP'); |
---|
| 308 | }; |
---|
| 309 | }catch(e){} |
---|
| 310 | } |
---|
| 311 | } |
---|
| 312 | |
---|
| 313 | util.addCommonMethods(xhr); |
---|
| 314 | |
---|
| 315 | return xhr; |
---|
| 316 | }); |
---|