source: Dev/branches/rest-dojo-ui/client/dojo/_base/xhr.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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