]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/request/xhr", [ |
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 | }; | |
101 | }; | |
102 | }else{ | |
103 | isValid = function(response){ | |
104 | return response.xhr.readyState; //boolean | |
105 | }; | |
106 | isReady = function(response){ | |
107 | return 4 === response.xhr.readyState; //boolean | |
108 | }; | |
109 | cancel = function(dfd, response){ | |
110 | // summary: | |
111 | // canceller function for util.deferred call. | |
112 | var xhr = response.xhr; | |
113 | var _at = typeof xhr.abort; | |
114 | if(_at === 'function' || _at === 'object' || _at === 'unknown'){ | |
115 | xhr.abort(); | |
116 | } | |
117 | }; | |
118 | } | |
119 | ||
120 | var undefined, | |
121 | defaultOptions = { | |
122 | data: null, | |
123 | query: null, | |
124 | sync: false, | |
125 | method: 'GET', | |
126 | headers: { | |
127 | 'Content-Type': 'application/x-www-form-urlencoded' | |
128 | } | |
129 | }; | |
130 | function xhr(url, options, returnDeferred){ | |
131 | var response = util.parseArgs( | |
132 | url, | |
133 | util.deepCreate(defaultOptions, options), | |
134 | has('native-formdata') && options && options.data && options.data instanceof FormData | |
135 | ); | |
136 | url = response.url; | |
137 | options = response.options; | |
138 | ||
139 | var remover, | |
140 | last = function(){ | |
141 | remover && remover(); | |
142 | }; | |
143 | ||
144 | //Make the Deferred object for this xhr request. | |
145 | var dfd = util.deferred( | |
146 | response, | |
147 | cancel, | |
148 | isValid, | |
149 | isReady, | |
150 | handleResponse, | |
151 | last | |
152 | ); | |
153 | var _xhr = response.xhr = xhr._create(); | |
154 | ||
155 | if(!_xhr){ | |
156 | // If XHR factory somehow returns nothings, | |
157 | // cancel the deferred. | |
158 | dfd.cancel(new RequestError('XHR was not created')); | |
159 | return returnDeferred ? dfd : dfd.promise; | |
160 | } | |
161 | ||
162 | response.getHeader = function(headerName){ | |
163 | return this.xhr.getResponseHeader(headerName); | |
164 | }; | |
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; | |
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 | }); |