summaryrefslogtreecommitdiff
path: root/lib/dojo/_base/xhr.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dojo/_base/xhr.js')
-rw-r--r--lib/dojo/_base/xhr.js1348
1 files changed, 926 insertions, 422 deletions
diff --git a/lib/dojo/_base/xhr.js b/lib/dojo/_base/xhr.js
index 5823371cf..818f8e418 100644
--- a/lib/dojo/_base/xhr.js
+++ b/lib/dojo/_base/xhr.js
@@ -5,433 +5,937 @@
*/
-if(!dojo._hasResource["dojo._base.xhr"]){
-dojo._hasResource["dojo._base.xhr"]=true;
+if(!dojo._hasResource["dojo._base.xhr"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.xhr"] = true;
dojo.provide("dojo._base.xhr");
dojo.require("dojo._base.Deferred");
dojo.require("dojo._base.json");
dojo.require("dojo._base.lang");
dojo.require("dojo._base.query");
+
(function(){
-var _1=dojo,_2=_1.config;
-function _3(_4,_5,_6){
-if(_6===null){
-return;
-}
-var _7=_4[_5];
-if(typeof _7=="string"){
-_4[_5]=[_7,_6];
-}else{
-if(_1.isArray(_7)){
-_7.push(_6);
-}else{
-_4[_5]=_6;
-}
-}
-};
-dojo.fieldToObject=function(_8){
-var _9=null;
-var _a=_1.byId(_8);
-if(_a){
-var _b=_a.name;
-var _c=(_a.type||"").toLowerCase();
-if(_b&&_c&&!_a.disabled){
-if(_c=="radio"||_c=="checkbox"){
-if(_a.checked){
-_9=_a.value;
-}
-}else{
-if(_a.multiple){
-_9=[];
-_1.query("option",_a).forEach(function(_d){
-if(_d.selected){
-_9.push(_d.value);
-}
-});
-}else{
-_9=_a.value;
-}
-}
-}
-}
-return _9;
-};
-dojo.formToObject=function(_e){
-var _f={};
-var _10="file|submit|image|reset|button|";
-_1.forEach(dojo.byId(_e).elements,function(_11){
-var _12=_11.name;
-var _13=(_11.type||"").toLowerCase();
-if(_12&&_13&&_10.indexOf(_13)==-1&&!_11.disabled){
-_3(_f,_12,_1.fieldToObject(_11));
-if(_13=="image"){
-_f[_12+".x"]=_f[_12+".y"]=_f[_12].x=_f[_12].y=0;
-}
-}
-});
-return _f;
-};
-dojo.objectToQuery=function(map){
-var enc=encodeURIComponent;
-var _14=[];
-var _15={};
-for(var _16 in map){
-var _17=map[_16];
-if(_17!=_15[_16]){
-var _18=enc(_16)+"=";
-if(_1.isArray(_17)){
-for(var i=0;i<_17.length;i++){
-_14.push(_18+enc(_17[i]));
-}
-}else{
-_14.push(_18+enc(_17));
-}
-}
-}
-return _14.join("&");
-};
-dojo.formToQuery=function(_19){
-return _1.objectToQuery(_1.formToObject(_19));
-};
-dojo.formToJson=function(_1a,_1b){
-return _1.toJson(_1.formToObject(_1a),_1b);
-};
-dojo.queryToObject=function(str){
-var ret={};
-var qp=str.split("&");
-var dec=decodeURIComponent;
-_1.forEach(qp,function(_1c){
-if(_1c.length){
-var _1d=_1c.split("=");
-var _1e=dec(_1d.shift());
-var val=dec(_1d.join("="));
-if(typeof ret[_1e]=="string"){
-ret[_1e]=[ret[_1e]];
-}
-if(_1.isArray(ret[_1e])){
-ret[_1e].push(val);
-}else{
-ret[_1e]=val;
-}
-}
-});
-return ret;
-};
-dojo._blockAsync=false;
-var _1f=_1._contentHandlers=dojo.contentHandlers={text:function(xhr){
-return xhr.responseText;
-},json:function(xhr){
-return _1.fromJson(xhr.responseText||null);
-},"json-comment-filtered":function(xhr){
-if(!dojo.config.useCommentedJson){
-console.warn("Consider using the standard mimetype:application/json."+" json-commenting can introduce security issues. To"+" decrease the chances of hijacking, use the standard the 'json' handler and"+" prefix your json with: {}&&\n"+"Use djConfig.useCommentedJson=true to turn off this message.");
-}
-var _20=xhr.responseText;
-var _21=_20.indexOf("/*");
-var _22=_20.lastIndexOf("*/");
-if(_21==-1||_22==-1){
-throw new Error("JSON was not comment filtered");
-}
-return _1.fromJson(_20.substring(_21+2,_22));
-},javascript:function(xhr){
-return _1.eval(xhr.responseText);
-},xml:function(xhr){
-var _23=xhr.responseXML;
-if(_1.isIE&&(!_23||!_23.documentElement)){
-var ms=function(n){
-return "MSXML"+n+".DOMDocument";
-};
-var dp=["Microsoft.XMLDOM",ms(6),ms(4),ms(3),ms(2)];
-_1.some(dp,function(p){
-try{
-var dom=new ActiveXObject(p);
-dom.async=false;
-dom.loadXML(xhr.responseText);
-_23=dom;
-}
-catch(e){
-return false;
-}
-return true;
-});
-}
-return _23;
-},"json-comment-optional":function(xhr){
-if(xhr.responseText&&/^[^{\[]*\/\*/.test(xhr.responseText)){
-return _1f["json-comment-filtered"](xhr);
-}else{
-return _1f["json"](xhr);
-}
-}};
-dojo._ioSetArgs=function(_24,_25,_26,_27){
-var _28={args:_24,url:_24.url};
-var _29=null;
-if(_24.form){
-var _2a=_1.byId(_24.form);
-var _2b=_2a.getAttributeNode("action");
-_28.url=_28.url||(_2b?_2b.value:null);
-_29=_1.formToObject(_2a);
-}
-var _2c=[{}];
-if(_29){
-_2c.push(_29);
-}
-if(_24.content){
-_2c.push(_24.content);
-}
-if(_24.preventCache){
-_2c.push({"dojo.preventCache":new Date().valueOf()});
-}
-_28.query=_1.objectToQuery(_1.mixin.apply(null,_2c));
-_28.handleAs=_24.handleAs||"text";
-var d=new _1.Deferred(_25);
-d.addCallbacks(_26,function(_2d){
-return _27(_2d,d);
-});
-var ld=_24.load;
-if(ld&&_1.isFunction(ld)){
-d.addCallback(function(_2e){
-return ld.call(_24,_2e,_28);
-});
-}
-var err=_24.error;
-if(err&&_1.isFunction(err)){
-d.addErrback(function(_2f){
-return err.call(_24,_2f,_28);
-});
-}
-var _30=_24.handle;
-if(_30&&_1.isFunction(_30)){
-d.addBoth(function(_31){
-return _30.call(_24,_31,_28);
-});
-}
-if(_2.ioPublish&&_1.publish&&_28.args.ioPublish!==false){
-d.addCallbacks(function(res){
-_1.publish("/dojo/io/load",[d,res]);
-return res;
-},function(res){
-_1.publish("/dojo/io/error",[d,res]);
-return res;
-});
-d.addBoth(function(res){
-_1.publish("/dojo/io/done",[d,res]);
-return res;
-});
-}
-d.ioArgs=_28;
-return d;
-};
-var _32=function(dfd){
-dfd.canceled=true;
-var xhr=dfd.ioArgs.xhr;
-var _33=typeof xhr.abort;
-if(_33=="function"||_33=="object"||_33=="unknown"){
-xhr.abort();
-}
-var err=dfd.ioArgs.error;
-if(!err){
-err=new Error("xhr cancelled");
-err.dojoType="cancel";
-}
-return err;
-};
-var _34=function(dfd){
-var ret=_1f[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);
-return ret===undefined?null:ret;
-};
-var _35=function(_36,dfd){
-if(!dfd.ioArgs.args.failOk){
-console.error(_36);
-}
-return _36;
-};
-var _37=null;
-var _38=[];
-var _39=0;
-var _3a=function(dfd){
-if(_39<=0){
-_39=0;
-if(_2.ioPublish&&_1.publish&&(!dfd||dfd&&dfd.ioArgs.args.ioPublish!==false)){
-_1.publish("/dojo/io/stop");
-}
-}
-};
-var _3b=function(){
-var now=(new Date()).getTime();
-if(!_1._blockAsync){
-for(var i=0,tif;i<_38.length&&(tif=_38[i]);i++){
-var dfd=tif.dfd;
-var _3c=function(){
-if(!dfd||dfd.canceled||!tif.validCheck(dfd)){
-_38.splice(i--,1);
-_39-=1;
-}else{
-if(tif.ioCheck(dfd)){
-_38.splice(i--,1);
-tif.resHandle(dfd);
-_39-=1;
-}else{
-if(dfd.startTime){
-if(dfd.startTime+(dfd.ioArgs.args.timeout||0)<now){
-_38.splice(i--,1);
-var err=new Error("timeout exceeded");
-err.dojoType="timeout";
-dfd.errback(err);
-dfd.cancel();
-_39-=1;
-}
-}
-}
-}
-};
-if(dojo.config.debugAtAllCosts){
-_3c.call(this);
-}else{
-try{
-_3c.call(this);
-}
-catch(e){
-dfd.errback(e);
-}
-}
-}
-}
-_3a(dfd);
-if(!_38.length){
-clearInterval(_37);
-_37=null;
-return;
-}
-};
-dojo._ioCancelAll=function(){
-try{
-_1.forEach(_38,function(i){
-try{
-i.dfd.cancel();
-}
-catch(e){
-}
-});
-}
-catch(e){
-}
-};
-if(_1.isIE){
-_1.addOnWindowUnload(_1._ioCancelAll);
-}
-_1._ioNotifyStart=function(dfd){
-if(_2.ioPublish&&_1.publish&&dfd.ioArgs.args.ioPublish!==false){
-if(!_39){
-_1.publish("/dojo/io/start");
-}
-_39+=1;
-_1.publish("/dojo/io/send",[dfd]);
-}
-};
-_1._ioWatch=function(dfd,_3d,_3e,_3f){
-var _40=dfd.ioArgs.args;
-if(_40.timeout){
-dfd.startTime=(new Date()).getTime();
-}
-_38.push({dfd:dfd,validCheck:_3d,ioCheck:_3e,resHandle:_3f});
-if(!_37){
-_37=setInterval(_3b,50);
-}
-if(_40.sync){
-_3b();
-}
-};
-var _41="application/x-www-form-urlencoded";
-var _42=function(dfd){
-return dfd.ioArgs.xhr.readyState;
-};
-var _43=function(dfd){
-return 4==dfd.ioArgs.xhr.readyState;
-};
-var _44=function(dfd){
-var xhr=dfd.ioArgs.xhr;
-if(_1._isDocumentOk(xhr)){
-dfd.callback(dfd);
-}else{
-var err=new Error("Unable to load "+dfd.ioArgs.url+" status:"+xhr.status);
-err.status=xhr.status;
-err.responseText=xhr.responseText;
-dfd.errback(err);
-}
-};
-dojo._ioAddQueryToUrl=function(_45){
-if(_45.query.length){
-_45.url+=(_45.url.indexOf("?")==-1?"?":"&")+_45.query;
-_45.query=null;
-}
-};
-dojo.xhr=function(_46,_47,_48){
-var dfd=_1._ioSetArgs(_47,_32,_34,_35);
-var _49=dfd.ioArgs;
-var xhr=_49.xhr=_1._xhrObj(_49.args);
-if(!xhr){
-dfd.cancel();
-return dfd;
-}
-if("postData" in _47){
-_49.query=_47.postData;
-}else{
-if("putData" in _47){
-_49.query=_47.putData;
-}else{
-if("rawBody" in _47){
-_49.query=_47.rawBody;
-}else{
-if((arguments.length>2&&!_48)||"POST|PUT".indexOf(_46.toUpperCase())==-1){
-_1._ioAddQueryToUrl(_49);
-}
-}
-}
-}
-xhr.open(_46,_49.url,_47.sync!==true,_47.user||undefined,_47.password||undefined);
-if(_47.headers){
-for(var hdr in _47.headers){
-if(hdr.toLowerCase()==="content-type"&&!_47.contentType){
-_47.contentType=_47.headers[hdr];
-}else{
-if(_47.headers[hdr]){
-xhr.setRequestHeader(hdr,_47.headers[hdr]);
-}
-}
-}
-}
-xhr.setRequestHeader("Content-Type",_47.contentType||_41);
-if(!_47.headers||!("X-Requested-With" in _47.headers)){
-xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");
-}
-_1._ioNotifyStart(dfd);
-if(dojo.config.debugAtAllCosts){
-xhr.send(_49.query);
-}else{
-try{
-xhr.send(_49.query);
-}
-catch(e){
-_49.error=e;
-dfd.cancel();
-}
-}
-_1._ioWatch(dfd,_42,_43,_44);
-xhr=null;
-return dfd;
-};
-dojo.xhrGet=function(_4a){
-return _1.xhr("GET",_4a);
-};
-dojo.rawXhrPost=dojo.xhrPost=function(_4b){
-return _1.xhr("POST",_4b,true);
-};
-dojo.rawXhrPut=dojo.xhrPut=function(_4c){
-return _1.xhr("PUT",_4c,true);
-};
-dojo.xhrDelete=function(_4d){
-return _1.xhr("DELETE",_4d);
-};
+ var _d = dojo, cfg = _d.config;
+
+ function setValue(/*Object*/obj, /*String*/name, /*String*/value){
+ //summary:
+ // For the named property in object, set the value. If a value
+ // already exists and it is a string, convert the value to be an
+ // array of values.
+
+ //Skip it if there is no value
+ if(value === null){
+ return;
+ }
+
+ var val = obj[name];
+ if(typeof val == "string"){ // inline'd type check
+ obj[name] = [val, value];
+ }else if(_d.isArray(val)){
+ val.push(value);
+ }else{
+ obj[name] = value;
+ }
+ }
+
+ dojo.fieldToObject = function(/*DOMNode||String*/ inputNode){
+ // summary:
+ // Serialize a form field to a JavaScript object.
+ //
+ // description:
+ // Returns the value encoded in a form field as
+ // as a string or an array of strings. Disabled form elements
+ // and unchecked radio and checkboxes are skipped. Multi-select
+ // elements are returned as an array of string values.
+ var ret = null;
+ var item = _d.byId(inputNode);
+ if(item){
+ var _in = item.name;
+ var type = (item.type||"").toLowerCase();
+ if(_in && type && !item.disabled){
+ if(type == "radio" || type == "checkbox"){
+ if(item.checked){ ret = item.value }
+ }else if(item.multiple){
+ ret = [];
+ _d.query("option", item).forEach(function(opt){
+ if(opt.selected){
+ ret.push(opt.value);
+ }
+ });
+ }else{
+ ret = item.value;
+ }
+ }
+ }
+ return ret; // Object
+ }
+
+ dojo.formToObject = function(/*DOMNode||String*/ formNode){
+ // summary:
+ // Serialize a form node to a JavaScript object.
+ // description:
+ // Returns the values encoded in an HTML form as
+ // string properties in an object which it then returns. Disabled form
+ // elements, buttons, and other non-value form elements are skipped.
+ // Multi-select elements are returned as an array of string values.
+ //
+ // example:
+ // This form:
+ // | <form id="test_form">
+ // | <input type="text" name="blah" value="blah">
+ // | <input type="text" name="no_value" value="blah" disabled>
+ // | <input type="button" name="no_value2" value="blah">
+ // | <select type="select" multiple name="multi" size="5">
+ // | <option value="blah">blah</option>
+ // | <option value="thud" selected>thud</option>
+ // | <option value="thonk" selected>thonk</option>
+ // | </select>
+ // | </form>
+ //
+ // yields this object structure as the result of a call to
+ // formToObject():
+ //
+ // | {
+ // | blah: "blah",
+ // | multi: [
+ // | "thud",
+ // | "thonk"
+ // | ]
+ // | };
+
+ var ret = {};
+ var exclude = "file|submit|image|reset|button|";
+ _d.forEach(dojo.byId(formNode).elements, function(item){
+ var _in = item.name;
+ var type = (item.type||"").toLowerCase();
+ if(_in && type && exclude.indexOf(type) == -1 && !item.disabled){
+ setValue(ret, _in, _d.fieldToObject(item));
+ if(type == "image"){
+ ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0;
+ }
+ }
+ });
+ return ret; // Object
+ }
+
+ dojo.objectToQuery = function(/*Object*/ map){
+ // summary:
+ // takes a name/value mapping object and returns a string representing
+ // a URL-encoded version of that object.
+ // example:
+ // this object:
+ //
+ // | {
+ // | blah: "blah",
+ // | multi: [
+ // | "thud",
+ // | "thonk"
+ // | ]
+ // | };
+ //
+ // yields the following query string:
+ //
+ // | "blah=blah&multi=thud&multi=thonk"
+
+ // FIXME: need to implement encodeAscii!!
+ var enc = encodeURIComponent;
+ var pairs = [];
+ var backstop = {};
+ for(var name in map){
+ var value = map[name];
+ if(value != backstop[name]){
+ var assign = enc(name) + "=";
+ if(_d.isArray(value)){
+ for(var i=0; i < value.length; i++){
+ pairs.push(assign + enc(value[i]));
+ }
+ }else{
+ pairs.push(assign + enc(value));
+ }
+ }
+ }
+ return pairs.join("&"); // String
+ }
+
+ dojo.formToQuery = function(/*DOMNode||String*/ formNode){
+ // summary:
+ // Returns a URL-encoded string representing the form passed as either a
+ // node or string ID identifying the form to serialize
+ return _d.objectToQuery(_d.formToObject(formNode)); // String
+ }
+
+ dojo.formToJson = function(/*DOMNode||String*/ formNode, /*Boolean?*/prettyPrint){
+ // summary:
+ // Create a serialized JSON string from a form node or string
+ // ID identifying the form to serialize
+ return _d.toJson(_d.formToObject(formNode), prettyPrint); // String
+ }
+
+ dojo.queryToObject = function(/*String*/ str){
+ // summary:
+ // Create an object representing a de-serialized query section of a
+ // URL. Query keys with multiple values are returned in an array.
+ //
+ // example:
+ // This string:
+ //
+ // | "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&"
+ //
+ // results in this object structure:
+ //
+ // | {
+ // | foo: [ "bar", "baz" ],
+ // | thinger: " spaces =blah",
+ // | zonk: "blarg"
+ // | }
+ //
+ // Note that spaces and other urlencoded entities are correctly
+ // handled.
+
+ // FIXME: should we grab the URL string if we're not passed one?
+ var ret = {};
+ var qp = str.split("&");
+ var dec = decodeURIComponent;
+ _d.forEach(qp, function(item){
+ if(item.length){
+ var parts = item.split("=");
+ var name = dec(parts.shift());
+ var val = dec(parts.join("="));
+ if(typeof ret[name] == "string"){ // inline'd type check
+ ret[name] = [ret[name]];
+ }
+
+ if(_d.isArray(ret[name])){
+ ret[name].push(val);
+ }else{
+ ret[name] = val;
+ }
+ }
+ });
+ return ret; // Object
+ }
+
+ // need to block async callbacks from snatching this thread as the result
+ // of an async callback might call another sync XHR, this hangs khtml forever
+ // must checked by watchInFlight()
+
+ dojo._blockAsync = false;
+
+ // MOW: remove dojo._contentHandlers alias in 2.0
+ var handlers = _d._contentHandlers = dojo.contentHandlers = {
+ // summary:
+ // A map of availble XHR transport handle types. Name matches the
+ // `handleAs` attribute passed to XHR calls.
+ //
+ // description:
+ // A map of availble XHR transport handle types. Name matches the
+ // `handleAs` attribute passed to XHR calls. Each contentHandler is
+ // called, passing the xhr object for manipulation. The return value
+ // from the contentHandler will be passed to the `load` or `handle`
+ // functions defined in the original xhr call.
+ //
+ // example:
+ // Creating a custom content-handler:
+ // | dojo.contentHandlers.makeCaps = function(xhr){
+ // | return xhr.responseText.toUpperCase();
+ // | }
+ // | // and later:
+ // | dojo.xhrGet({
+ // | url:"foo.txt",
+ // | handleAs:"makeCaps",
+ // | load: function(data){ /* data is a toUpper version of foo.txt */ }
+ // | });
+
+ text: function(xhr){
+ // summary: A contentHandler which simply returns the plaintext response data
+ return xhr.responseText;
+ },
+ json: function(xhr){
+ // summary: A contentHandler which returns a JavaScript object created from the response data
+ return _d.fromJson(xhr.responseText || null);
+ },
+ "json-comment-filtered": function(xhr){
+ // summary: A contentHandler which expects comment-filtered JSON.
+ // description:
+ // A contentHandler which expects comment-filtered JSON.
+ // the json-comment-filtered option was implemented to prevent
+ // "JavaScript Hijacking", but it is less secure than standard JSON. Use
+ // standard JSON instead. JSON prefixing can be used to subvert hijacking.
+ //
+ // Will throw a notice suggesting to use application/json mimetype, as
+ // json-commenting can introduce security issues. To decrease the chances of hijacking,
+ // use the standard `json` contentHandler, and prefix your "JSON" with: {}&&
+ //
+ // use djConfig.useCommentedJson = true to turn off the notice
+ if(!dojo.config.useCommentedJson){
+ console.warn("Consider using the standard mimetype:application/json."
+ + " json-commenting can introduce security issues. To"
+ + " decrease the chances of hijacking, use the standard the 'json' handler and"
+ + " prefix your json with: {}&&\n"
+ + "Use djConfig.useCommentedJson=true to turn off this message.");
+ }
+
+ var value = xhr.responseText;
+ var cStartIdx = value.indexOf("\/*");
+ var cEndIdx = value.lastIndexOf("*\/");
+ if(cStartIdx == -1 || cEndIdx == -1){
+ throw new Error("JSON was not comment filtered");
+ }
+ return _d.fromJson(value.substring(cStartIdx+2, cEndIdx));
+ },
+ javascript: function(xhr){
+ // summary: A contentHandler which evaluates the response data, expecting it to be valid JavaScript
+
+ // FIXME: try Moz and IE specific eval variants?
+ return _d.eval(xhr.responseText);
+ },
+ xml: function(xhr){
+ // summary: A contentHandler returning an XML Document parsed from the response data
+ var result = xhr.responseXML;
+ if(_d.isIE && (!result || !result.documentElement)){
+ //WARNING: this branch used by the xml handling in dojo.io.iframe,
+ //so be sure to test dojo.io.iframe if making changes below.
+ var ms = function(n){ return "MSXML" + n + ".DOMDocument"; }
+ var dp = ["Microsoft.XMLDOM", ms(6), ms(4), ms(3), ms(2)];
+ _d.some(dp, function(p){
+ try{
+ var dom = new ActiveXObject(p);
+ dom.async = false;
+ dom.loadXML(xhr.responseText);
+ result = dom;
+ }catch(e){ return false; }
+ return true;
+ });
+ }
+ return result; // DOMDocument
+ },
+ "json-comment-optional": function(xhr){
+ // summary: A contentHandler which checks the presence of comment-filtered JSON and
+ // alternates between the `json` and `json-comment-filtered` contentHandlers.
+ if(xhr.responseText && /^[^{\[]*\/\*/.test(xhr.responseText)){
+ return handlers["json-comment-filtered"](xhr);
+ }else{
+ return handlers["json"](xhr);
+ }
+ }
+ };
+
+ /*=====
+ dojo.__IoArgs = function(){
+ // url: String
+ // URL to server endpoint.
+ // content: Object?
+ // Contains properties with string values. These
+ // properties will be serialized as name1=value2 and
+ // passed in the request.
+ // timeout: Integer?
+ // Milliseconds to wait for the response. If this time
+ // passes, the then error callbacks are called.
+ // form: DOMNode?
+ // DOM node for a form. Used to extract the form values
+ // and send to the server.
+ // preventCache: Boolean?
+ // Default is false. If true, then a
+ // "dojo.preventCache" parameter is sent in the request
+ // with a value that changes with each request
+ // (timestamp). Useful only with GET-type requests.
+ // handleAs: String?
+ // Acceptable values depend on the type of IO
+ // transport (see specific IO calls for more information).
+ // rawBody: String?
+ // Sets the raw body for an HTTP request. If this is used, then the content
+ // property is ignored. This is mostly useful for HTTP methods that have
+ // a body to their requests, like PUT or POST. This property can be used instead
+ // of postData and putData for dojo.rawXhrPost and dojo.rawXhrPut respectively.
+ // ioPublish: Boolean?
+ // Set this explicitly to false to prevent publishing of topics related to
+ // IO operations. Otherwise, if djConfig.ioPublish is set to true, topics
+ // will be published via dojo.publish for different phases of an IO operation.
+ // See dojo.__IoPublish for a list of topics that are published.
+ // load: Function?
+ // This function will be
+ // called on a successful HTTP response code.
+ // error: Function?
+ // This function will
+ // be called when the request fails due to a network or server error, the url
+ // is invalid, etc. It will also be called if the load or handle callback throws an
+ // exception, unless djConfig.debugAtAllCosts is true. This allows deployed applications
+ // to continue to run even when a logic error happens in the callback, while making
+ // it easier to troubleshoot while in debug mode.
+ // handle: Function?
+ // This function will
+ // be called at the end of every request, whether or not an error occurs.
+ this.url = url;
+ this.content = content;
+ this.timeout = timeout;
+ this.form = form;
+ this.preventCache = preventCache;
+ this.handleAs = handleAs;
+ this.ioPublish = ioPublish;
+ this.load = function(response, ioArgs){
+ // ioArgs: dojo.__IoCallbackArgs
+ // Provides additional information about the request.
+ // response: Object
+ // The response in the format as defined with handleAs.
+ }
+ this.error = function(response, ioArgs){
+ // ioArgs: dojo.__IoCallbackArgs
+ // Provides additional information about the request.
+ // response: Object
+ // The response in the format as defined with handleAs.
+ }
+ this.handle = function(loadOrError, response, ioArgs){
+ // loadOrError: String
+ // Provides a string that tells you whether this function
+ // was called because of success (load) or failure (error).
+ // response: Object
+ // The response in the format as defined with handleAs.
+ // ioArgs: dojo.__IoCallbackArgs
+ // Provides additional information about the request.
+ }
+ }
+ =====*/
+
+ /*=====
+ dojo.__IoCallbackArgs = function(args, xhr, url, query, handleAs, id, canDelete, json){
+ // args: Object
+ // the original object argument to the IO call.
+ // xhr: XMLHttpRequest
+ // For XMLHttpRequest calls only, the
+ // XMLHttpRequest object that was used for the
+ // request.
+ // url: String
+ // The final URL used for the call. Many times it
+ // will be different than the original args.url
+ // value.
+ // query: String
+ // For non-GET requests, the
+ // name1=value1&name2=value2 parameters sent up in
+ // the request.
+ // handleAs: String
+ // The final indicator on how the response will be
+ // handled.
+ // id: String
+ // For dojo.io.script calls only, the internal
+ // script ID used for the request.
+ // canDelete: Boolean
+ // For dojo.io.script calls only, indicates
+ // whether the script tag that represents the
+ // request can be deleted after callbacks have
+ // been called. Used internally to know when
+ // cleanup can happen on JSONP-type requests.
+ // json: Object
+ // For dojo.io.script calls only: holds the JSON
+ // response for JSONP-type requests. Used
+ // internally to hold on to the JSON responses.
+ // You should not need to access it directly --
+ // the same object should be passed to the success
+ // callbacks directly.
+ this.args = args;
+ this.xhr = xhr;
+ this.url = url;
+ this.query = query;
+ this.handleAs = handleAs;
+ this.id = id;
+ this.canDelete = canDelete;
+ this.json = json;
+ }
+ =====*/
+
+
+ /*=====
+ dojo.__IoPublish = function(){
+ // summary:
+ // This is a list of IO topics that can be published
+ // if djConfig.ioPublish is set to true. IO topics can be
+ // published for any Input/Output, network operation. So,
+ // dojo.xhr, dojo.io.script and dojo.io.iframe can all
+ // trigger these topics to be published.
+ // start: String
+ // "/dojo/io/start" is sent when there are no outstanding IO
+ // requests, and a new IO request is started. No arguments
+ // are passed with this topic.
+ // send: String
+ // "/dojo/io/send" is sent whenever a new IO request is started.
+ // It passes the dojo.Deferred for the request with the topic.
+ // load: String
+ // "/dojo/io/load" is sent whenever an IO request has loaded
+ // successfully. It passes the response and the dojo.Deferred
+ // for the request with the topic.
+ // error: String
+ // "/dojo/io/error" is sent whenever an IO request has errored.
+ // It passes the error and the dojo.Deferred
+ // for the request with the topic.
+ // done: String
+ // "/dojo/io/done" is sent whenever an IO request has completed,
+ // either by loading or by erroring. It passes the error and
+ // the dojo.Deferred for the request with the topic.
+ // stop: String
+ // "/dojo/io/stop" is sent when all outstanding IO requests have
+ // finished. No arguments are passed with this topic.
+ this.start = "/dojo/io/start";
+ this.send = "/dojo/io/send";
+ this.load = "/dojo/io/load";
+ this.error = "/dojo/io/error";
+ this.done = "/dojo/io/done";
+ this.stop = "/dojo/io/stop";
+ }
+ =====*/
+
+
+ dojo._ioSetArgs = function(/*dojo.__IoArgs*/args,
+ /*Function*/canceller,
+ /*Function*/okHandler,
+ /*Function*/errHandler){
+ // summary:
+ // sets up the Deferred and ioArgs property on the Deferred so it
+ // can be used in an io call.
+ // args:
+ // The args object passed into the public io call. Recognized properties on
+ // the args object are:
+ // canceller:
+ // The canceller function used for the Deferred object. The function
+ // will receive one argument, the Deferred object that is related to the
+ // canceller.
+ // okHandler:
+ // The first OK callback to be registered with Deferred. It has the opportunity
+ // to transform the OK response. It will receive one argument -- the Deferred
+ // object returned from this function.
+ // errHandler:
+ // The first error callback to be registered with Deferred. It has the opportunity
+ // to do cleanup on an error. It will receive two arguments: error (the
+ // Error object) and dfd, the Deferred object returned from this function.
+
+ var ioArgs = {args: args, url: args.url};
+
+ //Get values from form if requestd.
+ var formObject = null;
+ if(args.form){
+ var form = _d.byId(args.form);
+ //IE requires going through getAttributeNode instead of just getAttribute in some form cases,
+ //so use it for all. See #2844
+ var actnNode = form.getAttributeNode("action");
+ ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null);
+ formObject = _d.formToObject(form);
+ }
+
+ // set up the query params
+ var miArgs = [{}];
+
+ if(formObject){
+ // potentially over-ride url-provided params w/ form values
+ miArgs.push(formObject);
+ }
+ if(args.content){
+ // stuff in content over-rides what's set by form
+ miArgs.push(args.content);
+ }
+ if(args.preventCache){
+ miArgs.push({"dojo.preventCache": new Date().valueOf()});
+ }
+ ioArgs.query = _d.objectToQuery(_d.mixin.apply(null, miArgs));
+
+ // .. and the real work of getting the deferred in order, etc.
+ ioArgs.handleAs = args.handleAs || "text";
+ var d = new _d.Deferred(canceller);
+ d.addCallbacks(okHandler, function(error){
+ return errHandler(error, d);
+ });
+
+ //Support specifying load, error and handle callback functions from the args.
+ //For those callbacks, the "this" object will be the args object.
+ //The callbacks will get the deferred result value as the
+ //first argument and the ioArgs object as the second argument.
+ var ld = args.load;
+ if(ld && _d.isFunction(ld)){
+ d.addCallback(function(value){
+ return ld.call(args, value, ioArgs);
+ });
+ }
+ var err = args.error;
+ if(err && _d.isFunction(err)){
+ d.addErrback(function(value){
+ return err.call(args, value, ioArgs);
+ });
+ }
+ var handle = args.handle;
+ if(handle && _d.isFunction(handle)){
+ d.addBoth(function(value){
+ return handle.call(args, value, ioArgs);
+ });
+ }
+
+ //Plug in topic publishing, if dojo.publish is loaded.
+ if(cfg.ioPublish && _d.publish && ioArgs.args.ioPublish !== false){
+ d.addCallbacks(
+ function(res){
+ _d.publish("/dojo/io/load", [d, res]);
+ return res;
+ },
+ function(res){
+ _d.publish("/dojo/io/error", [d, res]);
+ return res;
+ }
+ );
+ d.addBoth(function(res){
+ _d.publish("/dojo/io/done", [d, res]);
+ return res;
+ });
+ }
+
+ d.ioArgs = ioArgs;
+
+ // FIXME: need to wire up the xhr object's abort method to something
+ // analagous in the Deferred
+ return d;
+ }
+
+ var _deferredCancel = function(/*Deferred*/dfd){
+ // summary: canceller function for dojo._ioSetArgs call.
+
+ dfd.canceled = true;
+ var xhr = dfd.ioArgs.xhr;
+ var _at = typeof xhr.abort;
+ if(_at == "function" || _at == "object" || _at == "unknown"){
+ xhr.abort();
+ }
+ var err = dfd.ioArgs.error;
+ if(!err){
+ err = new Error("xhr cancelled");
+ err.dojoType="cancel";
+ }
+ return err;
+ }
+ var _deferredOk = function(/*Deferred*/dfd){
+ // summary: okHandler function for dojo._ioSetArgs call.
+
+ var ret = handlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);
+ return ret === undefined ? null : ret;
+ }
+ var _deferError = function(/*Error*/error, /*Deferred*/dfd){
+ // summary: errHandler function for dojo._ioSetArgs call.
+
+ if(!dfd.ioArgs.args.failOk){
+ console.error(error);
+ }
+ return error;
+ }
+
+ // avoid setting a timer per request. It degrades performance on IE
+ // something fierece if we don't use unified loops.
+ var _inFlightIntvl = null;
+ var _inFlight = [];
+
+
+ //Use a separate count for knowing if we are starting/stopping io calls.
+ //Cannot use _inFlight.length since it can change at a different time than
+ //when we want to do this kind of test. We only want to decrement the count
+ //after a callback/errback has finished, since the callback/errback should be
+ //considered as part of finishing a request.
+ var _pubCount = 0;
+ var _checkPubCount = function(dfd){
+ if(_pubCount <= 0){
+ _pubCount = 0;
+ if(cfg.ioPublish && _d.publish && (!dfd || dfd && dfd.ioArgs.args.ioPublish !== false)){
+ _d.publish("/dojo/io/stop");
+ }
+ }
+ };
+
+ var _watchInFlight = function(){
+ //summary:
+ // internal method that checks each inflight XMLHttpRequest to see
+ // if it has completed or if the timeout situation applies.
+
+ var now = (new Date()).getTime();
+ // make sure sync calls stay thread safe, if this callback is called
+ // during a sync call and this results in another sync call before the
+ // first sync call ends the browser hangs
+ if(!_d._blockAsync){
+ // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating
+ // note: the second clause is an assigment on purpose, lint may complain
+ for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){
+ var dfd = tif.dfd;
+ var func = function(){
+ if(!dfd || dfd.canceled || !tif.validCheck(dfd)){
+ _inFlight.splice(i--, 1);
+ _pubCount -= 1;
+ }else if(tif.ioCheck(dfd)){
+ _inFlight.splice(i--, 1);
+ tif.resHandle(dfd);
+ _pubCount -= 1;
+ }else if(dfd.startTime){
+ //did we timeout?
+ if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){
+ _inFlight.splice(i--, 1);
+ var err = new Error("timeout exceeded");
+ err.dojoType = "timeout";
+ dfd.errback(err);
+ //Cancel the request so the io module can do appropriate cleanup.
+ dfd.cancel();
+ _pubCount -= 1;
+ }
+ }
+ };
+ if(dojo.config.debugAtAllCosts){
+ func.call(this);
+ }else{
+ try{
+ func.call(this);
+ }catch(e){
+ dfd.errback(e);
+ }
+ }
+ }
+ }
+
+ _checkPubCount(dfd);
+
+ if(!_inFlight.length){
+ clearInterval(_inFlightIntvl);
+ _inFlightIntvl = null;
+ return;
+ }
+ }
+
+ dojo._ioCancelAll = function(){
+ //summary: Cancels all pending IO requests, regardless of IO type
+ //(xhr, script, iframe).
+ try{
+ _d.forEach(_inFlight, function(i){
+ try{
+ i.dfd.cancel();
+ }catch(e){/*squelch*/}
+ });
+ }catch(e){/*squelch*/}
+ }
+
+ //Automatically call cancel all io calls on unload
+ //in IE for trac issue #2357.
+ if(_d.isIE){
+ _d.addOnWindowUnload(_d._ioCancelAll);
+ }
+
+ _d._ioNotifyStart = function(/*Deferred*/dfd){
+ // summary:
+ // If dojo.publish is available, publish topics
+ // about the start of a request queue and/or the
+ // the beginning of request.
+ // description:
+ // Used by IO transports. An IO transport should
+ // call this method before making the network connection.
+ if(cfg.ioPublish && _d.publish && dfd.ioArgs.args.ioPublish !== false){
+ if(!_pubCount){
+ _d.publish("/dojo/io/start");
+ }
+ _pubCount += 1;
+ _d.publish("/dojo/io/send", [dfd]);
+ }
+ }
+
+ _d._ioWatch = function(dfd, validCheck, ioCheck, resHandle){
+ // summary:
+ // Watches the io request represented by dfd to see if it completes.
+ // dfd: Deferred
+ // The Deferred object to watch.
+ // validCheck: Function
+ // Function used to check if the IO request is still valid. Gets the dfd
+ // object as its only argument.
+ // ioCheck: Function
+ // Function used to check if basic IO call worked. Gets the dfd
+ // object as its only argument.
+ // resHandle: Function
+ // Function used to process response. Gets the dfd
+ // object as its only argument.
+ var args = dfd.ioArgs.args;
+ if(args.timeout){
+ dfd.startTime = (new Date()).getTime();
+ }
+
+ _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle});
+ if(!_inFlightIntvl){
+ _inFlightIntvl = setInterval(_watchInFlight, 50);
+ }
+ // handle sync requests
+ //A weakness: async calls in flight
+ //could have their handlers called as part of the
+ //_watchInFlight call, before the sync's callbacks
+ // are called.
+ if(args.sync){
+ _watchInFlight();
+ }
+ }
+
+ var _defaultContentType = "application/x-www-form-urlencoded";
+
+ var _validCheck = function(/*Deferred*/dfd){
+ return dfd.ioArgs.xhr.readyState; //boolean
+ }
+ var _ioCheck = function(/*Deferred*/dfd){
+ return 4 == dfd.ioArgs.xhr.readyState; //boolean
+ }
+ var _resHandle = function(/*Deferred*/dfd){
+ var xhr = dfd.ioArgs.xhr;
+ if(_d._isDocumentOk(xhr)){
+ dfd.callback(dfd);
+ }else{
+ var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status);
+ err.status = xhr.status;
+ err.responseText = xhr.responseText;
+ dfd.errback(err);
+ }
+ }
+
+ dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){
+ //summary: Adds query params discovered by the io deferred construction to the URL.
+ //Only use this for operations which are fundamentally GET-type operations.
+ if(ioArgs.query.length){
+ ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query;
+ ioArgs.query = null;
+ }
+ }
+
+ /*=====
+ dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, {
+ constructor: function(){
+ // summary:
+ // In addition to the properties listed for the dojo._IoArgs type,
+ // the following properties are allowed for dojo.xhr* methods.
+ // handleAs: String?
+ // Acceptable values are: text (default), json, json-comment-optional,
+ // json-comment-filtered, javascript, xml. See `dojo.contentHandlers`
+ // sync: Boolean?
+ // false is default. Indicates whether the request should
+ // be a synchronous (blocking) request.
+ // headers: Object?
+ // Additional HTTP headers to send in the request.
+ // failOk: Boolean?
+ // false is default. Indicates whether a request should be
+ // allowed to fail (and therefore no console error message in
+ // the event of a failure)
+ this.handleAs = handleAs;
+ this.sync = sync;
+ this.headers = headers;
+ this.failOk = failOk;
+ }
+ });
+ =====*/
+
+ dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){
+ // summary:
+ // Sends an HTTP request with the given method.
+ // description:
+ // Sends an HTTP request with the given method.
+ // See also dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts
+ // for those HTTP methods. There are also methods for "raw" PUT and POST methods
+ // via dojo.rawXhrPut() and dojo.rawXhrPost() respectively.
+ // method:
+ // HTTP method to be used, such as GET, POST, PUT, DELETE. Should be uppercase.
+ // hasBody:
+ // If the request has an HTTP body, then pass true for hasBody.
+
+ //Make the Deferred object for this xhr request.
+ var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError);
+ var ioArgs = dfd.ioArgs;
+
+ //Pass the args to _xhrObj, to allow alternate XHR calls based specific calls, like
+ //the one used for iframe proxies.
+ var xhr = ioArgs.xhr = _d._xhrObj(ioArgs.args);
+ //If XHR factory fails, cancel the deferred.
+ if(!xhr){
+ dfd.cancel();
+ return dfd;
+ }
+
+ //Allow for specifying the HTTP body completely.
+ if("postData" in args){
+ ioArgs.query = args.postData;
+ }else if("putData" in args){
+ ioArgs.query = args.putData;
+ }else if("rawBody" in args){
+ ioArgs.query = args.rawBody;
+ }else if((arguments.length > 2 && !hasBody) || "POST|PUT".indexOf(method.toUpperCase()) == -1){
+ //Check for hasBody being passed. If no hasBody,
+ //then only append query string if not a POST or PUT request.
+ _d._ioAddQueryToUrl(ioArgs);
+ }
+
+ // IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open).
+ // workaround for IE6's apply() "issues"
+ xhr.open(method, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined);
+ if(args.headers){
+ for(var hdr in args.headers){
+ if(hdr.toLowerCase() === "content-type" && !args.contentType){
+ args.contentType = args.headers[hdr];
+ }else if(args.headers[hdr]){
+ //Only add header if it has a value. This allows for instnace, skipping
+ //insertion of X-Requested-With by specifying empty value.
+ xhr.setRequestHeader(hdr, args.headers[hdr]);
+ }
+ }
+ }
+ // FIXME: is this appropriate for all content types?
+ xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType);
+ if(!args.headers || !("X-Requested-With" in args.headers)){
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ }
+ // FIXME: set other headers here!
+ _d._ioNotifyStart(dfd);
+ if(dojo.config.debugAtAllCosts){
+ xhr.send(ioArgs.query);
+ }else{
+ try{
+ xhr.send(ioArgs.query);
+ }catch(e){
+ ioArgs.error = e;
+ dfd.cancel();
+ }
+ }
+ _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle);
+ xhr = null;
+ return dfd; // dojo.Deferred
+ }
+
+ dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){
+ // summary:
+ // Sends an HTTP GET request to the server.
+ return _d.xhr("GET", args); // dojo.Deferred
+ }
+
+ dojo.rawXhrPost = dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){
+ // summary:
+ // Sends an HTTP POST request to the server. In addtion to the properties
+ // listed for the dojo.__XhrArgs type, the following property is allowed:
+ // postData:
+ // String. Send raw data in the body of the POST request.
+ return _d.xhr("POST", args, true); // dojo.Deferred
+ }
+
+ dojo.rawXhrPut = dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){
+ // summary:
+ // Sends an HTTP PUT request to the server. In addtion to the properties
+ // listed for the dojo.__XhrArgs type, the following property is allowed:
+ // putData:
+ // String. Send raw data in the body of the PUT request.
+ return _d.xhr("PUT", args, true); // dojo.Deferred
+ }
+
+ dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){
+ // summary:
+ // Sends an HTTP DELETE request to the server.
+ return _d.xhr("DELETE", args); //dojo.Deferred
+ }
+
+ /*
+ dojo.wrapForm = function(formNode){
+ //summary:
+ // A replacement for FormBind, but not implemented yet.
+
+ // FIXME: need to think harder about what extensions to this we might
+ // want. What should we allow folks to do w/ this? What events to
+ // set/send?
+ throw new Error("dojo.wrapForm not yet implemented");
+ }
+ */
})();
+
}