]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojo/_base/xhr.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dojo / _base / xhr.js
1 if(!dojo._hasResource["dojo._base.xhr"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojo._base.xhr"] = true;
3 dojo.provide("dojo._base.xhr");
4 dojo.require("dojo._base.Deferred");
5 dojo.require("dojo._base.json");
6 dojo.require("dojo._base.lang");
7 dojo.require("dojo._base.query");
8
9 (function(){
10         var _d = dojo;
11         function setValue(/*Object*/obj, /*String*/name, /*String*/value){
12                 //summary:
13                 //              For the nameed property in object, set the value. If a value
14                 //              already exists and it is a string, convert the value to be an
15                 //              array of values.
16                 var val = obj[name];
17                 if(_d.isString(val)){
18                         obj[name] = [val, value];
19                 }else if(_d.isArray(val)){
20                         val.push(value);
21                 }else{
22                         obj[name] = value;
23                 }
24         }
25
26         dojo.formToObject = function(/*DOMNode||String*/ formNode){
27                 // summary:
28                 //              dojo.formToObject returns the values encoded in an HTML form as
29                 //              string properties in an object which it then returns. Disabled form
30                 //              elements, buttons, and other non-value form elements are skipped.
31                 //              Multi-select elements are returned as an array of string values.
32                 // description:
33                 //              This form:
34                 //
35                 //              |       <form id="test_form">
36                 //              |               <input type="text" name="blah" value="blah">
37                 //              |               <input type="text" name="no_value" value="blah" disabled>
38                 //              |               <input type="button" name="no_value2" value="blah">
39                 //              |               <select type="select" multiple name="multi" size="5">
40                 //              |                       <option value="blah">blah</option>
41                 //              |                       <option value="thud" selected>thud</option>
42                 //              |                       <option value="thonk" selected>thonk</option>
43                 //              |               </select>
44                 //              |       </form>
45                 //
46                 //              yields this object structure as the result of a call to
47                 //              formToObject():
48                 //
49                 //              |       { 
50                 //              |               blah: "blah",
51                 //              |               multi: [
52                 //              |                       "thud",
53                 //              |                       "thonk"
54                 //              |               ]
55                 //              |       };
56         
57                 var ret = {};
58                 var iq = "input:not([type=file]):not([type=submit]):not([type=image]):not([type=reset]):not([type=button]), select, textarea";
59                 _d.query(iq, formNode).filter(function(node){
60                         return !node.disabled && node.name;
61                 }).forEach(function(item){
62                         var _in = item.name;
63                         var type = (item.type||"").toLowerCase();
64                         if(type == "radio" || type == "checkbox"){
65                                 if(item.checked){ setValue(ret, _in, item.value); }
66                         }else if(item.multiple){
67                                 ret[_in] = [];
68                                 _d.query("option", item).forEach(function(opt){
69                                         if(opt.selected){
70                                                 setValue(ret, _in, opt.value);
71                                         }
72                                 });
73                         }else{ 
74                                 setValue(ret, _in, item.value);
75                                 if(type == "image"){
76                                         ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0;
77                                 }
78                         }
79                 });
80                 return ret; // Object
81         }
82
83         dojo.objectToQuery = function(/*Object*/ map){
84                 //      summary:
85                 //              takes a name/value mapping object and returns a string representing
86                 //              a URL-encoded version of that object.
87                 //      example:
88                 //              this object:
89                 //
90                 //              |       { 
91                 //              |               blah: "blah",
92                 //              |               multi: [
93                 //              |                       "thud",
94                 //              |                       "thonk"
95                 //              |               ]
96                 //              |       };
97                 //
98                 //      yields the following query string:
99                 //      
100                 //      |       "blah=blah&multi=thud&multi=thonk"
101
102                 // FIXME: need to implement encodeAscii!!
103                 var enc = encodeURIComponent;
104                 var pairs = [];
105                 var backstop = {};
106                 for(var name in map){
107                         var value = map[name];
108                         if(value != backstop[name]){
109                                 var assign = enc(name) + "=";
110                                 if(_d.isArray(value)){
111                                         for(var i=0; i < value.length; i++){
112                                                 pairs.push(assign + enc(value[i]));
113                                         }
114                                 }else{
115                                         pairs.push(assign + enc(value));
116                                 }
117                         }
118                 }
119                 return pairs.join("&"); // String
120         }
121
122         dojo.formToQuery = function(/*DOMNode||String*/ formNode){
123                 // summary:
124                 //              Returns a URL-encoded string representing the form passed as either a
125                 //              node or string ID identifying the form to serialize
126                 return _d.objectToQuery(_d.formToObject(formNode)); // String
127         }
128
129         dojo.formToJson = function(/*DOMNode||String*/ formNode, /*Boolean?*/prettyPrint){
130                 // summary:
131                 //              return a serialized JSON string from a form node or string
132                 //              ID identifying the form to serialize
133                 return _d.toJson(_d.formToObject(formNode), prettyPrint); // String
134         }
135
136         dojo.queryToObject = function(/*String*/ str){
137                 // summary:
138                 //              returns an object representing a de-serialized query section of a
139                 //              URL. Query keys with multiple values are returned in an array.
140                 // description:
141                 //              This string:
142                 //
143                 //      |               "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&"
144                 //              
145                 //              results in this object structure:
146                 //
147                 //      |               {
148                 //      |                       foo: [ "bar", "baz" ],
149                 //      |                       thinger: " spaces =blah",
150                 //      |                       zonk: "blarg"
151                 //      |               }
152                 //      
153                 //              Note that spaces and other urlencoded entities are correctly
154                 //              handled.
155
156                 // FIXME: should we grab the URL string if we're not passed one?
157                 var ret = {};
158                 var qp = str.split("&");
159                 var dec = decodeURIComponent;
160                 _d.forEach(qp, function(item){
161                         if(item.length){
162                                 var parts = item.split("=");
163                                 var name = dec(parts.shift());
164                                 var val = dec(parts.join("="));
165                                 if(_d.isString(ret[name])){
166                                         ret[name] = [ret[name]];
167                                 }
168                                 if(_d.isArray(ret[name])){
169                                         ret[name].push(val);
170                                 }else{
171                                         ret[name] = val;
172                                 }
173                         }
174                 });
175                 return ret; // Object
176         }
177
178         /*
179                 from refactor.txt:
180
181                 all bind() replacement APIs take the following argument structure:
182
183                         {
184                                 url: "blah.html",
185
186                                 // all below are optional, but must be supported in some form by
187                                 // every IO API
188                                 timeout: 1000, // milliseconds
189                                 handleAs: "text", // replaces the always-wrong "mimetype"
190                                 content: { 
191                                         key: "value"
192                                 },
193
194                                 // browser-specific, MAY be unsupported
195                                 sync: true, // defaults to false
196                                 form: dojo.byId("someForm") 
197                         }
198         */
199
200         // need to block async callbacks from snatching this thread as the result
201         // of an async callback might call another sync XHR, this hangs khtml forever
202         // must checked by watchInFlight()
203
204         dojo._blockAsync = false;
205
206         dojo._contentHandlers = {
207                 "text": function(xhr){ return xhr.responseText; },
208                 "json": function(xhr){
209                         if(!dojo.config.usePlainJson){
210                                 console.warn("Consider using mimetype:text/json-comment-filtered"
211                                         + " to avoid potential security issues with JSON endpoints"
212                                         + " (use djConfig.usePlainJson=true to turn off this message)");
213                         }
214                         return (xhr.status == 204) ? undefined : _d.fromJson(xhr.responseText);
215                 },
216                 "json-comment-filtered": function(xhr){ 
217                         // NOTE: we provide the json-comment-filtered option as one solution to
218                         // the "JavaScript Hijacking" issue noted by Fortify and others. It is
219                         // not appropriate for all circumstances.
220
221                         var value = xhr.responseText;
222                         var cStartIdx = value.indexOf("\/*");
223                         var cEndIdx = value.lastIndexOf("*\/");
224                         if(cStartIdx == -1 || cEndIdx == -1){
225                                 throw new Error("JSON was not comment filtered");
226                         }
227                         return (xhr.status == 204) ? undefined :
228                                 _d.fromJson(value.substring(cStartIdx+2, cEndIdx));
229                 },
230                 "javascript": function(xhr){ 
231                         // FIXME: try Moz and IE specific eval variants?
232                         return _d.eval(xhr.responseText);
233                 },
234                 "xml": function(xhr){ 
235                         var result = xhr.responseXML;
236                         if(_d.isIE && (!result || window.location.protocol == "file:")){
237                                 _d.forEach(["MSXML2", "Microsoft", "MSXML", "MSXML3"], function(prefix){
238                                         try{
239                                                 var dom = new ActiveXObject(prefix + ".XMLDOM");
240                                                 dom.async = false;
241                                                 dom.loadXML(xhr.responseText);
242                                                 result = dom;
243                                         }catch(e){ /* Not available. Squelch and try next one. */ }
244                                 });
245                         }
246                         return result; // DOMDocument
247                 }
248         };
249
250         dojo._contentHandlers["json-comment-optional"] = function(xhr){
251                 var handlers = _d._contentHandlers;
252                 try{
253                         return handlers["json-comment-filtered"](xhr);
254                 }catch(e){
255                         return handlers["json"](xhr);
256                 }
257         };
258
259         /*=====
260         dojo.__IoArgs = function(){
261                 //      url: String
262                 //              URL to server endpoint.
263                 //      content: Object?
264                 //              Contains properties with string values. These
265                 //              properties will be serialized as name1=value2 and
266                 //              passed in the request.
267                 //      timeout: Integer?
268                 //              Milliseconds to wait for the response. If this time
269                 //              passes, the then error callbacks are called.
270                 //      form: DOMNode?
271                 //              DOM node for a form. Used to extract the form values
272                 //              and send to the server.
273                 //      preventCache: Boolean?
274                 //              Default is false. If true, then a
275                 //              "dojo.preventCache" parameter is sent in the request
276                 //              with a value that changes with each request
277                 //              (timestamp). Useful only with GET-type requests.
278                 //      handleAs: String?
279                 //              Acceptable values depend on the type of IO
280                 //              transport (see specific IO calls for more information).
281                 //      load: Function?
282                 //              function(response, ioArgs){}. response is an Object, ioArgs
283                 //              is of type dojo.__IoCallbackArgs. The load function will be
284                 //              called on a successful response.
285                 //      error: Function?
286                 //              function(response, ioArgs){}. response is an Object, ioArgs
287                 //              is of type dojo.__IoCallbackArgs. The error function will
288                 //              be called in an error case. 
289                 //      handle: Function?
290                 //              function(response, ioArgs){}. response is an Object, ioArgs
291                 //              is of type dojo.__IoCallbackArgs. The handle function will
292                 //              be called in either the successful or error case.  For
293                 //              the load, error and handle functions, the ioArgs object
294                 //              will contain the following properties: 
295                 this.url = url;
296                 this.content = content;
297                 this.timeout = timeout;
298                 this.form = form;
299                 this.preventCache = preventCache;
300                 this.handleAs = handleAs;
301                 this.load = load;
302                 this.error = error;
303                 this.handle = handle;
304         }
305         =====*/
306
307         /*=====
308         dojo.__IoCallbackArgs = function(args, xhr, url, query, handleAs, id, canDelete, json){
309                 //      args: Object
310                 //              the original object argument to the IO call.
311                 //      xhr: XMLHttpRequest
312                 //              For XMLHttpRequest calls only, the
313                 //              XMLHttpRequest object that was used for the
314                 //              request.
315                 //      url: String
316                 //              The final URL used for the call. Many times it
317                 //              will be different than the original args.url
318                 //              value.
319                 //      query: String
320                 //              For non-GET requests, the
321                 //              name1=value1&name2=value2 parameters sent up in
322                 //              the request.
323                 //      handleAs: String
324                 //              The final indicator on how the response will be
325                 //              handled.
326                 //      id: String
327                 //              For dojo.io.script calls only, the internal
328                 //              script ID used for the request.
329                 //      canDelete: Boolean
330                 //              For dojo.io.script calls only, indicates
331                 //              whether the script tag that represents the
332                 //              request can be deleted after callbacks have
333                 //              been called. Used internally to know when
334                 //              cleanup can happen on JSONP-type requests.
335                 //      json: Object
336                 //              For dojo.io.script calls only: holds the JSON
337                 //              response for JSONP-type requests. Used
338                 //              internally to hold on to the JSON responses.
339                 //              You should not need to access it directly --
340                 //              the same object should be passed to the success
341                 //              callbacks directly.
342                 this.args = args;
343                 this.xhr = xhr;
344                 this.url = url;
345                 this.query = query;
346                 this.handleAs = handleAs;
347                 this.id = id;
348                 this.canDelete = canDelete;
349                 this.json = json;
350         }
351         =====*/
352
353
354
355         dojo._ioSetArgs = function(/*dojo.__IoArgs*/args,
356                         /*Function*/canceller,
357                         /*Function*/okHandler,
358                         /*Function*/errHandler){
359                 //      summary: 
360                 //              sets up the Deferred and ioArgs property on the Deferred so it
361                 //              can be used in an io call.
362                 //      args:
363                 //              The args object passed into the public io call. Recognized properties on
364                 //              the args object are:
365                 //      canceller:
366                 //              The canceller function used for the Deferred object. The function
367                 //              will receive one argument, the Deferred object that is related to the
368                 //              canceller.
369                 //      okHandler:
370                 //              The first OK callback to be registered with Deferred. It has the opportunity
371                 //              to transform the OK response. It will receive one argument -- the Deferred
372                 //              object returned from this function.
373                 //      errHandler:
374                 //              The first error callback to be registered with Deferred. It has the opportunity
375                 //              to do cleanup on an error. It will receive two arguments: error (the 
376                 //              Error object) and dfd, the Deferred object returned from this function.
377
378                 var ioArgs = {args: args, url: args.url};
379
380                 //Get values from form if requestd.
381                 var formObject = null;
382                 if(args.form){ 
383                         var form = _d.byId(args.form);
384                         //IE requires going through getAttributeNode instead of just getAttribute in some form cases, 
385                         //so use it for all.  See #2844
386                         var actnNode = form.getAttributeNode("action");
387                         ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null); 
388                         formObject = _d.formToObject(form);
389                 }
390
391                 // set up the query params
392                 var miArgs = [{}];
393         
394                 if(formObject){
395                         // potentially over-ride url-provided params w/ form values
396                         miArgs.push(formObject);
397                 }
398                 if(args.content){
399                         // stuff in content over-rides what's set by form
400                         miArgs.push(args.content);
401                 }
402                 if(args.preventCache){
403                         miArgs.push({"dojo.preventCache": new Date().valueOf()});
404                 }
405                 ioArgs.query = _d.objectToQuery(_d.mixin.apply(null, miArgs));
406         
407                 // .. and the real work of getting the deferred in order, etc.
408                 ioArgs.handleAs = args.handleAs || "text";
409                 var d = new _d.Deferred(canceller);
410                 d.addCallbacks(okHandler, function(error){
411                         return errHandler(error, d);
412                 });
413
414                 //Support specifying load, error and handle callback functions from the args.
415                 //For those callbacks, the "this" object will be the args object.
416                 //The callbacks will get the deferred result value as the
417                 //first argument and the ioArgs object as the second argument.
418                 var ld = args.load;
419                 if(ld && _d.isFunction(ld)){
420                         d.addCallback(function(value){
421                                 return ld.call(args, value, ioArgs);
422                         });
423                 }
424                 var err = args.error;
425                 if(err && _d.isFunction(err)){
426                         d.addErrback(function(value){
427                                 return err.call(args, value, ioArgs);
428                         });
429                 }
430                 var handle = args.handle;
431                 if(handle && _d.isFunction(handle)){
432                         d.addBoth(function(value){
433                                 return handle.call(args, value, ioArgs);
434                         });
435                 }
436                 
437                 d.ioArgs = ioArgs;
438         
439                 // FIXME: need to wire up the xhr object's abort method to something
440                 // analagous in the Deferred
441                 return d;
442         }
443
444         var _deferredCancel = function(/*Deferred*/dfd){
445                 //summary: canceller function for dojo._ioSetArgs call.
446                 
447                 dfd.canceled = true;
448                 var xhr = dfd.ioArgs.xhr;
449                 var _at = typeof xhr.abort;
450                 if(_at == "function" || _at == "unknown"){
451                         xhr.abort();
452                 }
453                 var err = new Error("xhr cancelled");
454                 err.dojoType = "cancel";
455                 return err;
456         }
457         var _deferredOk = function(/*Deferred*/dfd){
458                 //summary: okHandler function for dojo._ioSetArgs call.
459
460                 return _d._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);
461         }
462         var _deferError = function(/*Error*/error, /*Deferred*/dfd){
463                 //summary: errHandler function for dojo._ioSetArgs call.
464                 
465                 // console.debug("xhr error in:", dfd.ioArgs.xhr);
466                 console.debug(error);
467                 return error;
468         }
469
470         var _makeXhrDeferred = function(/*dojo.__XhrArgs*/args){
471                 //summary: makes the Deferred object for this xhr request.
472                 var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError);
473                 //Pass the args to _xhrObj, to allow xhr iframe proxy interceptions.
474                 dfd.ioArgs.xhr = _d._xhrObj(dfd.ioArgs.args);
475                 return dfd;
476         }
477
478         // avoid setting a timer per request. It degrades performance on IE
479         // something fierece if we don't use unified loops.
480         var _inFlightIntvl = null;
481         var _inFlight = [];
482         var _watchInFlight = function(){
483                 //summary: 
484                 //              internal method that checks each inflight XMLHttpRequest to see
485                 //              if it has completed or if the timeout situation applies.
486                 
487                 var now = (new Date()).getTime();
488                 // make sure sync calls stay thread safe, if this callback is called
489                 // during a sync call and this results in another sync call before the
490                 // first sync call ends the browser hangs
491                 if(!_d._blockAsync){
492                         // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating
493                         // note: the second clause is an assigment on purpose, lint may complain
494                         for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){
495                                 var dfd = tif.dfd;
496                                 try{
497                                         if(!dfd || dfd.canceled || !tif.validCheck(dfd)){
498                                                 _inFlight.splice(i--, 1); 
499                                         }else if(tif.ioCheck(dfd)){
500                                                 _inFlight.splice(i--, 1);
501                                                 tif.resHandle(dfd);
502                                         }else if(dfd.startTime){
503                                                 //did we timeout?
504                                                 if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){
505                                                         _inFlight.splice(i--, 1);
506                                                         var err = new Error("timeout exceeded");
507                                                         err.dojoType = "timeout";
508                                                         dfd.errback(err);
509                                                         //Cancel the request so the io module can do appropriate cleanup.
510                                                         dfd.cancel();
511                                                 }
512                                         }
513                                 }catch(e){
514                                         // FIXME: make sure we errback! (fixed?  remove console.debug?)
515                                         console.debug(e);
516                                         dfd.errback(new Error("_watchInFlightError!"));
517                                 }
518                         }
519                 }
520
521                 if(!_inFlight.length){
522                         clearInterval(_inFlightIntvl);
523                         _inFlightIntvl = null;
524                         return;
525                 }
526
527         }
528
529         dojo._ioCancelAll = function(){
530                 //summary: Cancels all pending IO requests, regardless of IO type
531                 //(xhr, script, iframe).
532                 try{
533                         _d.forEach(_inFlight, function(i){
534                                 i.dfd.cancel();
535                         });
536                 }catch(e){/*squelch*/}
537         }
538
539         //Automatically call cancel all io calls on unload
540         //in IE for trac issue #2357.
541         if(_d.isIE){
542                 _d.addOnUnload(_d._ioCancelAll);
543         }
544
545         _d._ioWatch = function(/*Deferred*/dfd,
546                 /*Function*/validCheck,
547                 /*Function*/ioCheck,
548                 /*Function*/resHandle){
549                 //summary: watches the io request represented by dfd to see if it completes.
550                 //dfd:
551                 //              The Deferred object to watch.
552                 //validCheck:
553                 //              Function used to check if the IO request is still valid. Gets the dfd
554                 //              object as its only argument.
555                 //ioCheck:
556                 //              Function used to check if basic IO call worked. Gets the dfd
557                 //              object as its only argument.
558                 //resHandle:
559                 //              Function used to process response. Gets the dfd
560                 //              object as its only argument.
561                 if(dfd.ioArgs.args.timeout){
562                         dfd.startTime = (new Date()).getTime();
563                 }
564                 _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle});
565                 if(!_inFlightIntvl){
566                         _inFlightIntvl = setInterval(_watchInFlight, 50);
567                 }
568                 _watchInFlight(); // handle sync requests
569         }
570
571         var _defaultContentType = "application/x-www-form-urlencoded";
572
573         var _validCheck = function(/*Deferred*/dfd){
574                 return dfd.ioArgs.xhr.readyState; //boolean
575         }
576         var _ioCheck = function(/*Deferred*/dfd){
577                 return 4 == dfd.ioArgs.xhr.readyState; //boolean
578         }
579         var _resHandle = function(/*Deferred*/dfd){
580                 var xhr = dfd.ioArgs.xhr;
581                 if(_d._isDocumentOk(xhr)){
582                         dfd.callback(dfd);
583                 }else{
584                         var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status);
585                         err.status = xhr.status;
586                         err.responseText = xhr.responseText;
587                         dfd.errback(err);
588                 }
589         }
590
591         var _doIt = function(/*String*/type, /*Deferred*/dfd){
592                 // IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open).
593                 // workaround for IE6's apply() "issues"
594                 var ioArgs = dfd.ioArgs;
595                 var args = ioArgs.args;
596                 var xhr = ioArgs.xhr;
597                 xhr.open(type, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined);
598                 if(args.headers){
599                         for(var hdr in args.headers){
600                                 if(hdr.toLowerCase() === "content-type" && !args.contentType){
601                                         args.contentType = args.headers[hdr];
602                                 }else{
603                                         xhr.setRequestHeader(hdr, args.headers[hdr]);
604                                 }
605                         }
606                 }
607                 // FIXME: is this appropriate for all content types?
608                 xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType);
609                 if(!args.headers || !args.headers["X-Requested-With"]){
610                         xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
611                 }
612                 // FIXME: set other headers here!
613                 try{
614                         xhr.send(ioArgs.query);
615                 }catch(e){
616                         dfd.cancel();
617                 }
618                 _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle);
619                 xhr = null;
620                 return dfd; //Deferred
621         }
622
623         dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){
624                 //summary: Adds query params discovered by the io deferred construction to the URL.
625                 //Only use this for operations which are fundamentally GET-type operations.
626                 if(ioArgs.query.length){
627                         ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query;
628                         ioArgs.query = null;
629                 }               
630         }
631
632         /*=====
633         dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, {
634                 constructor: function(){
635                         //      summary:
636                         //              In addition to the properties listed for the dojo._IoArgs type,
637                         //              the following properties are allowed for dojo.xhr* methods.
638                         //      handleAs: String?
639                         //              Acceptable values are: text (default), json, json-comment-optional,
640                         //              json-comment-filtered, javascript, xml
641                         //      sync: Boolean?
642                         //              false is default. Indicates whether the request should
643                         //              be a synchronous (blocking) request.
644                         //      headers: Object?
645                         //              Additional HTTP headers to send in the request.
646                         this.handleAs = handleAs;
647                         this.sync = sync;
648                         this.headers = headers;
649                 }
650         });
651         =====*/
652
653         dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){
654                 //      summary: 
655                 //              Sends an HTTP request with the given method. If the request has an 
656                 //              HTTP body, then pass true for hasBody. The method argument should be uppercase.
657                 //              Also look at dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts
658                 //              for those HTTP methods. There are also methods for "raw" PUT and POST methods
659                 //              via dojo.rawXhrPut() and dojo.rawXhrPost() respectively.
660                 var dfd = _makeXhrDeferred(args);
661                 if(!hasBody){
662                         _d._ioAddQueryToUrl(dfd.ioArgs);
663                 }
664                 return _doIt(method, dfd); // dojo.Deferred
665         }
666
667         dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){
668                 //      summary: 
669                 //              Sends an HTTP GET request to the server.
670                 return _d.xhr("GET", args); //dojo.Deferred
671         }
672
673         dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){
674                 //summary: 
675                 //              Sends an HTTP POST request to the server.
676                 return _d.xhr("POST", args, true); // dojo.Deferred
677         }
678
679         dojo.rawXhrPost = function(/*dojo.__XhrArgs*/ args){
680                 //      summary:
681                 //              Sends an HTTP POST request to the server. In addtion to the properties
682                 //              listed for the dojo.__XhrArgs type, the following property is allowed:
683                 //      postData:
684                 //              String. The raw data to send in the body of the POST request.
685                 var dfd = _makeXhrDeferred(args);
686                 dfd.ioArgs.query = args.postData;
687                 return _doIt("POST", dfd); // dojo.Deferred
688         }
689
690         dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){
691                 //      summary:
692                 //              Sends an HTTP PUT request to the server.
693                 return _d.xhr("PUT", args, true); // dojo.Deferred
694         }
695
696         dojo.rawXhrPut = function(/*dojo.__XhrArgs*/ args){
697                 //      summary:
698                 //              Sends an HTTP PUT request to the server. In addtion to the properties
699                 //              listed for the dojo.__XhrArgs type, the following property is allowed:
700                 //      putData:
701                 //              String. The raw data to send in the body of the PUT request.
702                 var dfd = _makeXhrDeferred(args);
703                 var ioArgs = dfd.ioArgs;
704                 if(args.putData){
705                         ioArgs.query = args.putData;
706                         args.putData = null;
707                 }
708                 return _doIt("PUT", dfd); // dojo.Deferred
709         }
710
711         dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){
712                 //      summary:
713                 //              Sends an HTTP DELETE request to the server.
714                 return _d.xhr("DELETE", args); //dojo.Deferred
715         }
716
717         /*
718         dojo.wrapForm = function(formNode){
719                 //summary:
720                 //              A replacement for FormBind, but not implemented yet.
721
722                 // FIXME: need to think harder about what extensions to this we might
723                 // want. What should we allow folks to do w/ this? What events to
724                 // set/send?
725                 throw new Error("dojo.wrapForm not yet implemented");
726         }
727         */
728 })();
729
730 }