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