]> git.wh0rd.org Git - tt-rss.git/blob - lib/dojo/_base/xhr.js.uncompressed.js
update dojo to 1.7.3
[tt-rss.git] / lib / dojo / _base / xhr.js.uncompressed.js
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 });