1 | define([ |
---|
2 | "./kernel", "./sniff", "require", "../io-query", "../dom", "../dom-form", "./Deferred", "./json", "./lang", "./array", "../on" |
---|
3 | ], function(dojo, has, require, ioq, dom, domForm, deferred, json, lang, array, on){ |
---|
4 | // module: |
---|
5 | // dojo/_base.xhr |
---|
6 | // summary: |
---|
7 | // This modules defines the dojo.xhr* API. |
---|
8 | |
---|
9 | has.add("native-xhr", function() { |
---|
10 | // if true, the environment has a native XHR implementation |
---|
11 | return typeof XMLHttpRequest !== 'undefined'; |
---|
12 | }); |
---|
13 | |
---|
14 | if(has("dojo-xhr-factory")){ |
---|
15 | dojo._xhrObj = require.getXhr; |
---|
16 | }else if (has("native-xhr")){ |
---|
17 | dojo._xhrObj = function(){ |
---|
18 | // summary: |
---|
19 | // does the work of portably generating a new XMLHTTPRequest object. |
---|
20 | try{ |
---|
21 | return new XMLHttpRequest(); |
---|
22 | }catch(e){ |
---|
23 | throw new Error("XMLHTTP not available: "+e); |
---|
24 | } |
---|
25 | }; |
---|
26 | }else{ |
---|
27 | // PROGIDs are in order of decreasing likelihood; this will change in time. |
---|
28 | for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], progid, i = 0; i < 3;){ |
---|
29 | try{ |
---|
30 | progid = XMLHTTP_PROGIDS[i++]; |
---|
31 | if (new ActiveXObject(progid)) { |
---|
32 | // this progid works; therefore, use it from now on |
---|
33 | break; |
---|
34 | } |
---|
35 | }catch(e){ |
---|
36 | // squelch; we're just trying to find a good ActiveX PROGID |
---|
37 | // if they all fail, then progid ends up as the last attempt and that will signal the error |
---|
38 | // the first time the client actually tries to exec an xhr |
---|
39 | } |
---|
40 | } |
---|
41 | dojo._xhrObj= function() { |
---|
42 | return new ActiveXObject(progid); |
---|
43 | }; |
---|
44 | } |
---|
45 | |
---|
46 | var cfg = dojo.config; |
---|
47 | |
---|
48 | // mix in io-query and dom-form |
---|
49 | dojo.objectToQuery = ioq.objectToQuery; |
---|
50 | dojo.queryToObject = ioq.queryToObject; |
---|
51 | dojo.fieldToObject = domForm.fieldToObject; |
---|
52 | dojo.formToObject = domForm.toObject; |
---|
53 | dojo.formToQuery = domForm.toQuery; |
---|
54 | dojo.formToJson = domForm.toJson; |
---|
55 | |
---|
56 | // need to block async callbacks from snatching this thread as the result |
---|
57 | // of an async callback might call another sync XHR, this hangs khtml forever |
---|
58 | // must checked by watchInFlight() |
---|
59 | |
---|
60 | dojo._blockAsync = false; |
---|
61 | |
---|
62 | // MOW: remove dojo._contentHandlers alias in 2.0 |
---|
63 | var handlers = dojo._contentHandlers = dojo.contentHandlers = { |
---|
64 | // summary: |
---|
65 | // A map of availble XHR transport handle types. Name matches the |
---|
66 | // `handleAs` attribute passed to XHR calls. |
---|
67 | // |
---|
68 | // description: |
---|
69 | // A map of availble XHR transport handle types. Name matches the |
---|
70 | // `handleAs` attribute passed to XHR calls. Each contentHandler is |
---|
71 | // called, passing the xhr object for manipulation. The return value |
---|
72 | // from the contentHandler will be passed to the `load` or `handle` |
---|
73 | // functions defined in the original xhr call. |
---|
74 | // |
---|
75 | // example: |
---|
76 | // Creating a custom content-handler: |
---|
77 | // | dojo.contentHandlers.makeCaps = function(xhr){ |
---|
78 | // | return xhr.responseText.toUpperCase(); |
---|
79 | // | } |
---|
80 | // | // and later: |
---|
81 | // | dojo.xhrGet({ |
---|
82 | // | url:"foo.txt", |
---|
83 | // | handleAs:"makeCaps", |
---|
84 | // | load: function(data){ /* data is a toUpper version of foo.txt */ } |
---|
85 | // | }); |
---|
86 | |
---|
87 | "text": function(xhr){ |
---|
88 | // summary: A contentHandler which simply returns the plaintext response data |
---|
89 | return xhr.responseText; |
---|
90 | }, |
---|
91 | "json": function(xhr){ |
---|
92 | // summary: A contentHandler which returns a JavaScript object created from the response data |
---|
93 | return json.fromJson(xhr.responseText || null); |
---|
94 | }, |
---|
95 | "json-comment-filtered": function(xhr){ |
---|
96 | // summary: A contentHandler which expects comment-filtered JSON. |
---|
97 | // description: |
---|
98 | // A contentHandler which expects comment-filtered JSON. |
---|
99 | // the json-comment-filtered option was implemented to prevent |
---|
100 | // "JavaScript Hijacking", but it is less secure than standard JSON. Use |
---|
101 | // standard JSON instead. JSON prefixing can be used to subvert hijacking. |
---|
102 | // |
---|
103 | // Will throw a notice suggesting to use application/json mimetype, as |
---|
104 | // json-commenting can introduce security issues. To decrease the chances of hijacking, |
---|
105 | // use the standard `json` contentHandler, and prefix your "JSON" with: {}&& |
---|
106 | // |
---|
107 | // use djConfig.useCommentedJson = true to turn off the notice |
---|
108 | if(!dojo.config.useCommentedJson){ |
---|
109 | console.warn("Consider using the standard mimetype:application/json." |
---|
110 | + " json-commenting can introduce security issues. To" |
---|
111 | + " decrease the chances of hijacking, use the standard the 'json' handler and" |
---|
112 | + " prefix your json with: {}&&\n" |
---|
113 | + "Use djConfig.useCommentedJson=true to turn off this message."); |
---|
114 | } |
---|
115 | |
---|
116 | var value = xhr.responseText; |
---|
117 | var cStartIdx = value.indexOf("\/*"); |
---|
118 | var cEndIdx = value.lastIndexOf("*\/"); |
---|
119 | if(cStartIdx == -1 || cEndIdx == -1){ |
---|
120 | throw new Error("JSON was not comment filtered"); |
---|
121 | } |
---|
122 | return json.fromJson(value.substring(cStartIdx+2, cEndIdx)); |
---|
123 | }, |
---|
124 | "javascript": function(xhr){ |
---|
125 | // summary: A contentHandler which evaluates the response data, expecting it to be valid JavaScript |
---|
126 | |
---|
127 | // FIXME: try Moz and IE specific eval variants? |
---|
128 | return dojo.eval(xhr.responseText); |
---|
129 | }, |
---|
130 | "xml": function(xhr){ |
---|
131 | // summary: A contentHandler returning an XML Document parsed from the response data |
---|
132 | var result = xhr.responseXML; |
---|
133 | |
---|
134 | if(has("ie")){ |
---|
135 | if((!result || !result.documentElement)){ |
---|
136 | //WARNING: this branch used by the xml handling in dojo.io.iframe, |
---|
137 | //so be sure to test dojo.io.iframe if making changes below. |
---|
138 | var ms = function(n){ return "MSXML" + n + ".DOMDocument"; }; |
---|
139 | var dp = ["Microsoft.XMLDOM", ms(6), ms(4), ms(3), ms(2)]; |
---|
140 | array.some(dp, function(p){ |
---|
141 | try{ |
---|
142 | var dom = new ActiveXObject(p); |
---|
143 | dom.async = false; |
---|
144 | dom.loadXML(xhr.responseText); |
---|
145 | result = dom; |
---|
146 | }catch(e){ return false; } |
---|
147 | return true; |
---|
148 | }); |
---|
149 | } |
---|
150 | } |
---|
151 | return result; // DOMDocument |
---|
152 | }, |
---|
153 | "json-comment-optional": function(xhr){ |
---|
154 | // summary: A contentHandler which checks the presence of comment-filtered JSON and |
---|
155 | // alternates between the `json` and `json-comment-filtered` contentHandlers. |
---|
156 | if(xhr.responseText && /^[^{\[]*\/\*/.test(xhr.responseText)){ |
---|
157 | return handlers["json-comment-filtered"](xhr); |
---|
158 | }else{ |
---|
159 | return handlers["json"](xhr); |
---|
160 | } |
---|
161 | } |
---|
162 | }; |
---|
163 | |
---|
164 | /*===== |
---|
165 | dojo.__IoArgs = function(){ |
---|
166 | // url: String |
---|
167 | // URL to server endpoint. |
---|
168 | // content: Object? |
---|
169 | // Contains properties with string values. These |
---|
170 | // properties will be serialized as name1=value2 and |
---|
171 | // passed in the request. |
---|
172 | // timeout: Integer? |
---|
173 | // Milliseconds to wait for the response. If this time |
---|
174 | // passes, the then error callbacks are called. |
---|
175 | // form: DOMNode? |
---|
176 | // DOM node for a form. Used to extract the form values |
---|
177 | // and send to the server. |
---|
178 | // preventCache: Boolean? |
---|
179 | // Default is false. If true, then a |
---|
180 | // "dojo.preventCache" parameter is sent in the request |
---|
181 | // with a value that changes with each request |
---|
182 | // (timestamp). Useful only with GET-type requests. |
---|
183 | // handleAs: String? |
---|
184 | // Acceptable values depend on the type of IO |
---|
185 | // transport (see specific IO calls for more information). |
---|
186 | // rawBody: String? |
---|
187 | // Sets the raw body for an HTTP request. If this is used, then the content |
---|
188 | // property is ignored. This is mostly useful for HTTP methods that have |
---|
189 | // a body to their requests, like PUT or POST. This property can be used instead |
---|
190 | // of postData and putData for dojo.rawXhrPost and dojo.rawXhrPut respectively. |
---|
191 | // ioPublish: Boolean? |
---|
192 | // Set this explicitly to false to prevent publishing of topics related to |
---|
193 | // IO operations. Otherwise, if djConfig.ioPublish is set to true, topics |
---|
194 | // will be published via dojo.publish for different phases of an IO operation. |
---|
195 | // See dojo.__IoPublish for a list of topics that are published. |
---|
196 | // load: Function? |
---|
197 | // This function will be |
---|
198 | // called on a successful HTTP response code. |
---|
199 | // error: Function? |
---|
200 | // This function will |
---|
201 | // be called when the request fails due to a network or server error, the url |
---|
202 | // is invalid, etc. It will also be called if the load or handle callback throws an |
---|
203 | // exception, unless djConfig.debugAtAllCosts is true. This allows deployed applications |
---|
204 | // to continue to run even when a logic error happens in the callback, while making |
---|
205 | // it easier to troubleshoot while in debug mode. |
---|
206 | // handle: Function? |
---|
207 | // This function will |
---|
208 | // be called at the end of every request, whether or not an error occurs. |
---|
209 | this.url = url; |
---|
210 | this.content = content; |
---|
211 | this.timeout = timeout; |
---|
212 | this.form = form; |
---|
213 | this.preventCache = preventCache; |
---|
214 | this.handleAs = handleAs; |
---|
215 | this.ioPublish = ioPublish; |
---|
216 | this.load = function(response, ioArgs){ |
---|
217 | // ioArgs: dojo.__IoCallbackArgs |
---|
218 | // Provides additional information about the request. |
---|
219 | // response: Object |
---|
220 | // The response in the format as defined with handleAs. |
---|
221 | } |
---|
222 | this.error = function(response, ioArgs){ |
---|
223 | // ioArgs: dojo.__IoCallbackArgs |
---|
224 | // Provides additional information about the request. |
---|
225 | // response: Object |
---|
226 | // The response in the format as defined with handleAs. |
---|
227 | } |
---|
228 | this.handle = function(loadOrError, response, ioArgs){ |
---|
229 | // loadOrError: String |
---|
230 | // Provides a string that tells you whether this function |
---|
231 | // was called because of success (load) or failure (error). |
---|
232 | // response: Object |
---|
233 | // The response in the format as defined with handleAs. |
---|
234 | // ioArgs: dojo.__IoCallbackArgs |
---|
235 | // Provides additional information about the request. |
---|
236 | } |
---|
237 | } |
---|
238 | =====*/ |
---|
239 | |
---|
240 | /*===== |
---|
241 | dojo.__IoCallbackArgs = function(args, xhr, url, query, handleAs, id, canDelete, json){ |
---|
242 | // args: Object |
---|
243 | // the original object argument to the IO call. |
---|
244 | // xhr: XMLHttpRequest |
---|
245 | // For XMLHttpRequest calls only, the |
---|
246 | // XMLHttpRequest object that was used for the |
---|
247 | // request. |
---|
248 | // url: String |
---|
249 | // The final URL used for the call. Many times it |
---|
250 | // will be different than the original args.url |
---|
251 | // value. |
---|
252 | // query: String |
---|
253 | // For non-GET requests, the |
---|
254 | // name1=value1&name2=value2 parameters sent up in |
---|
255 | // the request. |
---|
256 | // handleAs: String |
---|
257 | // The final indicator on how the response will be |
---|
258 | // handled. |
---|
259 | // id: String |
---|
260 | // For dojo.io.script calls only, the internal |
---|
261 | // script ID used for the request. |
---|
262 | // canDelete: Boolean |
---|
263 | // For dojo.io.script calls only, indicates |
---|
264 | // whether the script tag that represents the |
---|
265 | // request can be deleted after callbacks have |
---|
266 | // been called. Used internally to know when |
---|
267 | // cleanup can happen on JSONP-type requests. |
---|
268 | // json: Object |
---|
269 | // For dojo.io.script calls only: holds the JSON |
---|
270 | // response for JSONP-type requests. Used |
---|
271 | // internally to hold on to the JSON responses. |
---|
272 | // You should not need to access it directly -- |
---|
273 | // the same object should be passed to the success |
---|
274 | // callbacks directly. |
---|
275 | this.args = args; |
---|
276 | this.xhr = xhr; |
---|
277 | this.url = url; |
---|
278 | this.query = query; |
---|
279 | this.handleAs = handleAs; |
---|
280 | this.id = id; |
---|
281 | this.canDelete = canDelete; |
---|
282 | this.json = json; |
---|
283 | } |
---|
284 | =====*/ |
---|
285 | |
---|
286 | |
---|
287 | /*===== |
---|
288 | dojo.__IoPublish = function(){ |
---|
289 | // summary: |
---|
290 | // This is a list of IO topics that can be published |
---|
291 | // if djConfig.ioPublish is set to true. IO topics can be |
---|
292 | // published for any Input/Output, network operation. So, |
---|
293 | // dojo.xhr, dojo.io.script and dojo.io.iframe can all |
---|
294 | // trigger these topics to be published. |
---|
295 | // start: String |
---|
296 | // "/dojo/io/start" is sent when there are no outstanding IO |
---|
297 | // requests, and a new IO request is started. No arguments |
---|
298 | // are passed with this topic. |
---|
299 | // send: String |
---|
300 | // "/dojo/io/send" is sent whenever a new IO request is started. |
---|
301 | // It passes the dojo.Deferred for the request with the topic. |
---|
302 | // load: String |
---|
303 | // "/dojo/io/load" is sent whenever an IO request has loaded |
---|
304 | // successfully. It passes the response and the dojo.Deferred |
---|
305 | // for the request with the topic. |
---|
306 | // error: String |
---|
307 | // "/dojo/io/error" is sent whenever an IO request has errored. |
---|
308 | // It passes the error and the dojo.Deferred |
---|
309 | // for the request with the topic. |
---|
310 | // done: String |
---|
311 | // "/dojo/io/done" is sent whenever an IO request has completed, |
---|
312 | // either by loading or by erroring. It passes the error and |
---|
313 | // the dojo.Deferred for the request with the topic. |
---|
314 | // stop: String |
---|
315 | // "/dojo/io/stop" is sent when all outstanding IO requests have |
---|
316 | // finished. No arguments are passed with this topic. |
---|
317 | this.start = "/dojo/io/start"; |
---|
318 | this.send = "/dojo/io/send"; |
---|
319 | this.load = "/dojo/io/load"; |
---|
320 | this.error = "/dojo/io/error"; |
---|
321 | this.done = "/dojo/io/done"; |
---|
322 | this.stop = "/dojo/io/stop"; |
---|
323 | } |
---|
324 | =====*/ |
---|
325 | |
---|
326 | |
---|
327 | dojo._ioSetArgs = function(/*dojo.__IoArgs*/args, |
---|
328 | /*Function*/canceller, |
---|
329 | /*Function*/okHandler, |
---|
330 | /*Function*/errHandler){ |
---|
331 | // summary: |
---|
332 | // sets up the Deferred and ioArgs property on the Deferred so it |
---|
333 | // can be used in an io call. |
---|
334 | // args: |
---|
335 | // The args object passed into the public io call. Recognized properties on |
---|
336 | // the args object are: |
---|
337 | // canceller: |
---|
338 | // The canceller function used for the Deferred object. The function |
---|
339 | // will receive one argument, the Deferred object that is related to the |
---|
340 | // canceller. |
---|
341 | // okHandler: |
---|
342 | // The first OK callback to be registered with Deferred. It has the opportunity |
---|
343 | // to transform the OK response. It will receive one argument -- the Deferred |
---|
344 | // object returned from this function. |
---|
345 | // errHandler: |
---|
346 | // The first error callback to be registered with Deferred. It has the opportunity |
---|
347 | // to do cleanup on an error. It will receive two arguments: error (the |
---|
348 | // Error object) and dfd, the Deferred object returned from this function. |
---|
349 | |
---|
350 | var ioArgs = {args: args, url: args.url}; |
---|
351 | |
---|
352 | //Get values from form if requestd. |
---|
353 | var formObject = null; |
---|
354 | if(args.form){ |
---|
355 | var form = dom.byId(args.form); |
---|
356 | //IE requires going through getAttributeNode instead of just getAttribute in some form cases, |
---|
357 | //so use it for all. See #2844 |
---|
358 | var actnNode = form.getAttributeNode("action"); |
---|
359 | ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null); |
---|
360 | formObject = domForm.toObject(form); |
---|
361 | } |
---|
362 | |
---|
363 | // set up the query params |
---|
364 | var miArgs = [{}]; |
---|
365 | |
---|
366 | if(formObject){ |
---|
367 | // potentially over-ride url-provided params w/ form values |
---|
368 | miArgs.push(formObject); |
---|
369 | } |
---|
370 | if(args.content){ |
---|
371 | // stuff in content over-rides what's set by form |
---|
372 | miArgs.push(args.content); |
---|
373 | } |
---|
374 | if(args.preventCache){ |
---|
375 | miArgs.push({"dojo.preventCache": new Date().valueOf()}); |
---|
376 | } |
---|
377 | ioArgs.query = ioq.objectToQuery(lang.mixin.apply(null, miArgs)); |
---|
378 | |
---|
379 | // .. and the real work of getting the deferred in order, etc. |
---|
380 | ioArgs.handleAs = args.handleAs || "text"; |
---|
381 | var d = new deferred(canceller); |
---|
382 | d.addCallbacks(okHandler, function(error){ |
---|
383 | return errHandler(error, d); |
---|
384 | }); |
---|
385 | |
---|
386 | //Support specifying load, error and handle callback functions from the args. |
---|
387 | //For those callbacks, the "this" object will be the args object. |
---|
388 | //The callbacks will get the deferred result value as the |
---|
389 | //first argument and the ioArgs object as the second argument. |
---|
390 | var ld = args.load; |
---|
391 | if(ld && lang.isFunction(ld)){ |
---|
392 | d.addCallback(function(value){ |
---|
393 | return ld.call(args, value, ioArgs); |
---|
394 | }); |
---|
395 | } |
---|
396 | var err = args.error; |
---|
397 | if(err && lang.isFunction(err)){ |
---|
398 | d.addErrback(function(value){ |
---|
399 | return err.call(args, value, ioArgs); |
---|
400 | }); |
---|
401 | } |
---|
402 | var handle = args.handle; |
---|
403 | if(handle && lang.isFunction(handle)){ |
---|
404 | d.addBoth(function(value){ |
---|
405 | return handle.call(args, value, ioArgs); |
---|
406 | }); |
---|
407 | } |
---|
408 | |
---|
409 | //Plug in topic publishing, if dojo.publish is loaded. |
---|
410 | if(cfg.ioPublish && dojo.publish && ioArgs.args.ioPublish !== false){ |
---|
411 | d.addCallbacks( |
---|
412 | function(res){ |
---|
413 | dojo.publish("/dojo/io/load", [d, res]); |
---|
414 | return res; |
---|
415 | }, |
---|
416 | function(res){ |
---|
417 | dojo.publish("/dojo/io/error", [d, res]); |
---|
418 | return res; |
---|
419 | } |
---|
420 | ); |
---|
421 | d.addBoth(function(res){ |
---|
422 | dojo.publish("/dojo/io/done", [d, res]); |
---|
423 | return res; |
---|
424 | }); |
---|
425 | } |
---|
426 | |
---|
427 | d.ioArgs = ioArgs; |
---|
428 | |
---|
429 | // FIXME: need to wire up the xhr object's abort method to something |
---|
430 | // analagous in the Deferred |
---|
431 | return d; |
---|
432 | }; |
---|
433 | |
---|
434 | var _deferredCancel = function(/*Deferred*/dfd){ |
---|
435 | // summary: canceller function for dojo._ioSetArgs call. |
---|
436 | |
---|
437 | dfd.canceled = true; |
---|
438 | var xhr = dfd.ioArgs.xhr; |
---|
439 | var _at = typeof xhr.abort; |
---|
440 | if(_at == "function" || _at == "object" || _at == "unknown"){ |
---|
441 | xhr.abort(); |
---|
442 | } |
---|
443 | var err = dfd.ioArgs.error; |
---|
444 | if(!err){ |
---|
445 | err = new Error("xhr cancelled"); |
---|
446 | err.dojoType="cancel"; |
---|
447 | } |
---|
448 | return err; |
---|
449 | }; |
---|
450 | var _deferredOk = function(/*Deferred*/dfd){ |
---|
451 | // summary: okHandler function for dojo._ioSetArgs call. |
---|
452 | |
---|
453 | var ret = handlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr); |
---|
454 | return ret === undefined ? null : ret; |
---|
455 | }; |
---|
456 | var _deferError = function(/*Error*/error, /*Deferred*/dfd){ |
---|
457 | // summary: errHandler function for dojo._ioSetArgs call. |
---|
458 | |
---|
459 | if(!dfd.ioArgs.args.failOk){ |
---|
460 | console.error(error); |
---|
461 | } |
---|
462 | return error; |
---|
463 | }; |
---|
464 | |
---|
465 | // avoid setting a timer per request. It degrades performance on IE |
---|
466 | // something fierece if we don't use unified loops. |
---|
467 | var _inFlightIntvl = null; |
---|
468 | var _inFlight = []; |
---|
469 | |
---|
470 | |
---|
471 | //Use a separate count for knowing if we are starting/stopping io calls. |
---|
472 | //Cannot use _inFlight.length since it can change at a different time than |
---|
473 | //when we want to do this kind of test. We only want to decrement the count |
---|
474 | //after a callback/errback has finished, since the callback/errback should be |
---|
475 | //considered as part of finishing a request. |
---|
476 | var _pubCount = 0; |
---|
477 | var _checkPubCount = function(dfd){ |
---|
478 | if(_pubCount <= 0){ |
---|
479 | _pubCount = 0; |
---|
480 | if(cfg.ioPublish && dojo.publish && (!dfd || dfd && dfd.ioArgs.args.ioPublish !== false)){ |
---|
481 | dojo.publish("/dojo/io/stop"); |
---|
482 | } |
---|
483 | } |
---|
484 | }; |
---|
485 | |
---|
486 | var _watchInFlight = function(){ |
---|
487 | //summary: |
---|
488 | // internal method that checks each inflight XMLHttpRequest to see |
---|
489 | // if it has completed or if the timeout situation applies. |
---|
490 | |
---|
491 | var now = (new Date()).getTime(); |
---|
492 | // make sure sync calls stay thread safe, if this callback is called |
---|
493 | // during a sync call and this results in another sync call before the |
---|
494 | // first sync call ends the browser hangs |
---|
495 | if(!dojo._blockAsync){ |
---|
496 | // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating |
---|
497 | // note: the second clause is an assigment on purpose, lint may complain |
---|
498 | for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){ |
---|
499 | var dfd = tif.dfd; |
---|
500 | var func = function(){ |
---|
501 | if(!dfd || dfd.canceled || !tif.validCheck(dfd)){ |
---|
502 | _inFlight.splice(i--, 1); |
---|
503 | _pubCount -= 1; |
---|
504 | }else if(tif.ioCheck(dfd)){ |
---|
505 | _inFlight.splice(i--, 1); |
---|
506 | tif.resHandle(dfd); |
---|
507 | _pubCount -= 1; |
---|
508 | }else if(dfd.startTime){ |
---|
509 | //did we timeout? |
---|
510 | if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){ |
---|
511 | _inFlight.splice(i--, 1); |
---|
512 | var err = new Error("timeout exceeded"); |
---|
513 | err.dojoType = "timeout"; |
---|
514 | dfd.errback(err); |
---|
515 | //Cancel the request so the io module can do appropriate cleanup. |
---|
516 | dfd.cancel(); |
---|
517 | _pubCount -= 1; |
---|
518 | } |
---|
519 | } |
---|
520 | }; |
---|
521 | if(dojo.config.debugAtAllCosts){ |
---|
522 | func.call(this); |
---|
523 | }else{ |
---|
524 | // try{ |
---|
525 | func.call(this); |
---|
526 | /* }catch(e){ |
---|
527 | dfd.errback(e); |
---|
528 | }*/ |
---|
529 | } |
---|
530 | } |
---|
531 | } |
---|
532 | |
---|
533 | _checkPubCount(dfd); |
---|
534 | |
---|
535 | if(!_inFlight.length){ |
---|
536 | clearInterval(_inFlightIntvl); |
---|
537 | _inFlightIntvl = null; |
---|
538 | } |
---|
539 | }; |
---|
540 | |
---|
541 | dojo._ioCancelAll = function(){ |
---|
542 | //summary: Cancels all pending IO requests, regardless of IO type |
---|
543 | //(xhr, script, iframe). |
---|
544 | try{ |
---|
545 | array.forEach(_inFlight, function(i){ |
---|
546 | try{ |
---|
547 | i.dfd.cancel(); |
---|
548 | }catch(e){/*squelch*/} |
---|
549 | }); |
---|
550 | }catch(e){/*squelch*/} |
---|
551 | }; |
---|
552 | |
---|
553 | //Automatically call cancel all io calls on unload |
---|
554 | //in IE for trac issue #2357. |
---|
555 | if(has("ie")){ |
---|
556 | on(window, "unload", dojo._ioCancelAll); |
---|
557 | } |
---|
558 | |
---|
559 | dojo._ioNotifyStart = function(/*Deferred*/dfd){ |
---|
560 | // summary: |
---|
561 | // If dojo.publish is available, publish topics |
---|
562 | // about the start of a request queue and/or the |
---|
563 | // the beginning of request. |
---|
564 | // description: |
---|
565 | // Used by IO transports. An IO transport should |
---|
566 | // call this method before making the network connection. |
---|
567 | if(cfg.ioPublish && dojo.publish && dfd.ioArgs.args.ioPublish !== false){ |
---|
568 | if(!_pubCount){ |
---|
569 | dojo.publish("/dojo/io/start"); |
---|
570 | } |
---|
571 | _pubCount += 1; |
---|
572 | dojo.publish("/dojo/io/send", [dfd]); |
---|
573 | } |
---|
574 | }; |
---|
575 | |
---|
576 | dojo._ioWatch = function(dfd, validCheck, ioCheck, resHandle){ |
---|
577 | // summary: |
---|
578 | // Watches the io request represented by dfd to see if it completes. |
---|
579 | // dfd: Deferred |
---|
580 | // The Deferred object to watch. |
---|
581 | // validCheck: Function |
---|
582 | // Function used to check if the IO request is still valid. Gets the dfd |
---|
583 | // object as its only argument. |
---|
584 | // ioCheck: Function |
---|
585 | // Function used to check if basic IO call worked. Gets the dfd |
---|
586 | // object as its only argument. |
---|
587 | // resHandle: Function |
---|
588 | // Function used to process response. Gets the dfd |
---|
589 | // object as its only argument. |
---|
590 | var args = dfd.ioArgs.args; |
---|
591 | if(args.timeout){ |
---|
592 | dfd.startTime = (new Date()).getTime(); |
---|
593 | } |
---|
594 | |
---|
595 | _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle}); |
---|
596 | if(!_inFlightIntvl){ |
---|
597 | _inFlightIntvl = setInterval(_watchInFlight, 50); |
---|
598 | } |
---|
599 | // handle sync requests |
---|
600 | //A weakness: async calls in flight |
---|
601 | //could have their handlers called as part of the |
---|
602 | //_watchInFlight call, before the sync's callbacks |
---|
603 | // are called. |
---|
604 | if(args.sync){ |
---|
605 | _watchInFlight(); |
---|
606 | } |
---|
607 | }; |
---|
608 | |
---|
609 | var _defaultContentType = "application/x-www-form-urlencoded"; |
---|
610 | |
---|
611 | var _validCheck = function(/*Deferred*/dfd){ |
---|
612 | return dfd.ioArgs.xhr.readyState; //boolean |
---|
613 | }; |
---|
614 | var _ioCheck = function(/*Deferred*/dfd){ |
---|
615 | return 4 == dfd.ioArgs.xhr.readyState; //boolean |
---|
616 | }; |
---|
617 | var _resHandle = function(/*Deferred*/dfd){ |
---|
618 | var xhr = dfd.ioArgs.xhr; |
---|
619 | if(dojo._isDocumentOk(xhr)){ |
---|
620 | dfd.callback(dfd); |
---|
621 | }else{ |
---|
622 | var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status); |
---|
623 | err.status = xhr.status; |
---|
624 | err.responseText = xhr.responseText; |
---|
625 | err.xhr = xhr; |
---|
626 | dfd.errback(err); |
---|
627 | } |
---|
628 | }; |
---|
629 | |
---|
630 | dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){ |
---|
631 | //summary: Adds query params discovered by the io deferred construction to the URL. |
---|
632 | //Only use this for operations which are fundamentally GET-type operations. |
---|
633 | if(ioArgs.query.length){ |
---|
634 | ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query; |
---|
635 | ioArgs.query = null; |
---|
636 | } |
---|
637 | }; |
---|
638 | |
---|
639 | /*===== |
---|
640 | dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, { |
---|
641 | constructor: function(){ |
---|
642 | // summary: |
---|
643 | // In addition to the properties listed for the dojo._IoArgs type, |
---|
644 | // the following properties are allowed for dojo.xhr* methods. |
---|
645 | // handleAs: String? |
---|
646 | // Acceptable values are: text (default), json, json-comment-optional, |
---|
647 | // json-comment-filtered, javascript, xml. See `dojo.contentHandlers` |
---|
648 | // sync: Boolean? |
---|
649 | // false is default. Indicates whether the request should |
---|
650 | // be a synchronous (blocking) request. |
---|
651 | // headers: Object? |
---|
652 | // Additional HTTP headers to send in the request. |
---|
653 | // failOk: Boolean? |
---|
654 | // false is default. Indicates whether a request should be |
---|
655 | // allowed to fail (and therefore no console error message in |
---|
656 | // the event of a failure) |
---|
657 | // contentType: String|Boolean |
---|
658 | // "application/x-www-form-urlencoded" is default. Set to false to |
---|
659 | // prevent a Content-Type header from being sent, or to a string |
---|
660 | // to send a different Content-Type. |
---|
661 | this.handleAs = handleAs; |
---|
662 | this.sync = sync; |
---|
663 | this.headers = headers; |
---|
664 | this.failOk = failOk; |
---|
665 | } |
---|
666 | }); |
---|
667 | =====*/ |
---|
668 | |
---|
669 | dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){ |
---|
670 | // summary: |
---|
671 | // Sends an HTTP request with the given method. |
---|
672 | // description: |
---|
673 | // Sends an HTTP request with the given method. |
---|
674 | // See also dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts |
---|
675 | // for those HTTP methods. There are also methods for "raw" PUT and POST methods |
---|
676 | // via dojo.rawXhrPut() and dojo.rawXhrPost() respectively. |
---|
677 | // method: |
---|
678 | // HTTP method to be used, such as GET, POST, PUT, DELETE. Should be uppercase. |
---|
679 | // hasBody: |
---|
680 | // If the request has an HTTP body, then pass true for hasBody. |
---|
681 | |
---|
682 | //Make the Deferred object for this xhr request. |
---|
683 | var dfd = dojo._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError); |
---|
684 | var ioArgs = dfd.ioArgs; |
---|
685 | |
---|
686 | //Pass the args to _xhrObj, to allow alternate XHR calls based specific calls, like |
---|
687 | //the one used for iframe proxies. |
---|
688 | var xhr = ioArgs.xhr = dojo._xhrObj(ioArgs.args); |
---|
689 | //If XHR factory fails, cancel the deferred. |
---|
690 | if(!xhr){ |
---|
691 | dfd.cancel(); |
---|
692 | return dfd; |
---|
693 | } |
---|
694 | |
---|
695 | //Allow for specifying the HTTP body completely. |
---|
696 | if("postData" in args){ |
---|
697 | ioArgs.query = args.postData; |
---|
698 | }else if("putData" in args){ |
---|
699 | ioArgs.query = args.putData; |
---|
700 | }else if("rawBody" in args){ |
---|
701 | ioArgs.query = args.rawBody; |
---|
702 | }else if((arguments.length > 2 && !hasBody) || "POST|PUT".indexOf(method.toUpperCase()) == -1){ |
---|
703 | //Check for hasBody being passed. If no hasBody, |
---|
704 | //then only append query string if not a POST or PUT request. |
---|
705 | dojo._ioAddQueryToUrl(ioArgs); |
---|
706 | } |
---|
707 | |
---|
708 | // IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open). |
---|
709 | // workaround for IE6's apply() "issues" |
---|
710 | xhr.open(method, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined); |
---|
711 | if(args.headers){ |
---|
712 | for(var hdr in args.headers){ |
---|
713 | if(hdr.toLowerCase() === "content-type" && !args.contentType){ |
---|
714 | args.contentType = args.headers[hdr]; |
---|
715 | }else if(args.headers[hdr]){ |
---|
716 | //Only add header if it has a value. This allows for instnace, skipping |
---|
717 | //insertion of X-Requested-With by specifying empty value. |
---|
718 | xhr.setRequestHeader(hdr, args.headers[hdr]); |
---|
719 | } |
---|
720 | } |
---|
721 | } |
---|
722 | // FIXME: is this appropriate for all content types? |
---|
723 | if(args.contentType !== false){ |
---|
724 | xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType); |
---|
725 | } |
---|
726 | if(!args.headers || !("X-Requested-With" in args.headers)){ |
---|
727 | xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); |
---|
728 | } |
---|
729 | // FIXME: set other headers here! |
---|
730 | dojo._ioNotifyStart(dfd); |
---|
731 | if(dojo.config.debugAtAllCosts){ |
---|
732 | xhr.send(ioArgs.query); |
---|
733 | }else{ |
---|
734 | try{ |
---|
735 | xhr.send(ioArgs.query); |
---|
736 | }catch(e){ |
---|
737 | ioArgs.error = e; |
---|
738 | dfd.cancel(); |
---|
739 | } |
---|
740 | } |
---|
741 | dojo._ioWatch(dfd, _validCheck, _ioCheck, _resHandle); |
---|
742 | xhr = null; |
---|
743 | return dfd; // dojo.Deferred |
---|
744 | }; |
---|
745 | |
---|
746 | dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){ |
---|
747 | // summary: |
---|
748 | // Sends an HTTP GET request to the server. |
---|
749 | return dojo.xhr("GET", args); // dojo.Deferred |
---|
750 | }; |
---|
751 | |
---|
752 | dojo.rawXhrPost = dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){ |
---|
753 | // summary: |
---|
754 | // Sends an HTTP POST request to the server. In addtion to the properties |
---|
755 | // listed for the dojo.__XhrArgs type, the following property is allowed: |
---|
756 | // postData: |
---|
757 | // String. Send raw data in the body of the POST request. |
---|
758 | return dojo.xhr("POST", args, true); // dojo.Deferred |
---|
759 | }; |
---|
760 | |
---|
761 | dojo.rawXhrPut = dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){ |
---|
762 | // summary: |
---|
763 | // Sends an HTTP PUT request to the server. In addtion to the properties |
---|
764 | // listed for the dojo.__XhrArgs type, the following property is allowed: |
---|
765 | // putData: |
---|
766 | // String. Send raw data in the body of the PUT request. |
---|
767 | return dojo.xhr("PUT", args, true); // dojo.Deferred |
---|
768 | }; |
---|
769 | |
---|
770 | dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){ |
---|
771 | // summary: |
---|
772 | // Sends an HTTP DELETE request to the server. |
---|
773 | return dojo.xhr("DELETE", args); //dojo.Deferred |
---|
774 | }; |
---|
775 | |
---|
776 | /* |
---|
777 | dojo.wrapForm = function(formNode){ |
---|
778 | //summary: |
---|
779 | // A replacement for FormBind, but not implemented yet. |
---|
780 | |
---|
781 | // FIXME: need to think harder about what extensions to this we might |
---|
782 | // want. What should we allow folks to do w/ this? What events to |
---|
783 | // set/send? |
---|
784 | throw new Error("dojo.wrapForm not yet implemented"); |
---|
785 | } |
---|
786 | */ |
---|
787 | |
---|
788 | dojo._isDocumentOk = function(http){ |
---|
789 | var stat = http.status || 0; |
---|
790 | stat = |
---|
791 | (stat >= 200 && stat < 300) || // allow any 2XX response code |
---|
792 | stat == 304 || // or, get it out of the cache |
---|
793 | stat == 1223 || // or, Internet Explorer mangled the status code |
---|
794 | !stat; // or, we're Titanium/browser chrome/chrome extension requesting a local file |
---|
795 | return stat; // Boolean |
---|
796 | }; |
---|
797 | |
---|
798 | dojo._getText = function(url){ |
---|
799 | var result; |
---|
800 | dojo.xhrGet({url:url, sync:true, load:function(text){ |
---|
801 | result = text; |
---|
802 | }}); |
---|
803 | return result; |
---|
804 | }; |
---|
805 | |
---|
806 | // Add aliases for static functions to dojo.xhr since dojo.xhr is what's returned from this module |
---|
807 | lang.mixin(dojo.xhr, { |
---|
808 | _xhrObj: dojo._xhrObj, |
---|
809 | fieldToObject: domForm.fieldToObject, |
---|
810 | formToObject: domForm.toObject, |
---|
811 | objectToQuery: ioq.objectToQuery, |
---|
812 | formToQuery: domForm.toQuery, |
---|
813 | formToJson: domForm.toJson, |
---|
814 | queryToObject: ioq.queryToObject, |
---|
815 | contentHandlers: handlers, |
---|
816 | _ioSetArgs: dojo._ioSetArgs, |
---|
817 | _ioCancelAll: dojo._ioCancelAll, |
---|
818 | _ioNotifyStart: dojo._ioNotifyStart, |
---|
819 | _ioWatch: dojo._ioWatch, |
---|
820 | _ioAddQueryToUrl: dojo._ioAddQueryToUrl, |
---|
821 | _isDocumentOk: dojo._isDocumentOk, |
---|
822 | _getText: dojo._getText, |
---|
823 | get: dojo.xhrGet, |
---|
824 | post: dojo.xhrPost, |
---|
825 | put: dojo.xhrPut, |
---|
826 | del: dojo.xhrDelete // because "delete" is a reserved word |
---|
827 | }); |
---|
828 | |
---|
829 | return dojo.xhr; |
---|
830 | }); |
---|