]>
Commit | Line | Data |
---|---|---|
1354d172 AD |
1 | define("dojo/_base/xhr", [ |
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(1 && require.getXhr){ | |
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 | }); |