1
0
mirror of https://tt-rss.org/git/tt-rss.git synced 2024-07-06 13:30:51 +02:00
ttrss/lib/dojo/_base/xhr.js.uncompressed.js
2013-03-18 10:26:26 +04:00

711 lines
23 KiB
JavaScript

define("dojo/_base/xhr", [
"./kernel",
"./sniff",
"require",
"../io-query",
/*===== "./declare", =====*/
"../dom",
"../dom-form",
"./Deferred",
"./config",
"./json",
"./lang",
"./array",
"../on",
"../aspect",
"../request/watch",
"../request/xhr",
"../request/util"
], function(dojo, has, require, ioq, /*===== declare, =====*/ dom, domForm, Deferred, config, json, lang, array, on, aspect, watch, _xhr, util){
// module:
// dojo/_base/xhr
/*=====
dojo._xhrObj = function(){
// summary:
// does the work of portably generating a new XMLHTTPRequest object.
};
=====*/
dojo._xhrObj = _xhr._create;
var cfg = dojo.config;
// mix in io-query and dom-form
dojo.objectToQuery = ioq.objectToQuery;
dojo.queryToObject = ioq.queryToObject;
dojo.fieldToObject = domForm.fieldToObject;
dojo.formToObject = domForm.toObject;
dojo.formToQuery = domForm.toQuery;
dojo.formToJson = domForm.toJson;
// 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 = dojo._contentHandlers = dojo.contentHandlers = {
// summary:
// A map of available XHR transport handle types. Name matches the
// `handleAs` attribute passed to XHR calls.
// description:
// A map of available 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:
// | xhr.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 json.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(!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 json.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 dojo.eval(xhr.responseText);
},
"xml": function(xhr){
// summary:
// A contentHandler returning an XML Document parsed from the response data
var result = xhr.responseXML;
if(has("ie")){
if((!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)];
array.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);
}
}
};
/*=====
// kwargs function parameter definitions. Assigning to dojo namespace rather than making them local variables
// because they are used by dojo/io modules too
dojo.__IoArgs = declare(null, {
// 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/_base/xhr.rawXhrPost and dojo/_base/xhr.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/topic.publish() for different phases of an IO operation.
// See dojo/main.__IoPublish for a list of topics that are published.
load: function(response, ioArgs){
// summary:
// This function will be
// called on a successful HTTP response code.
// ioArgs: dojo/main.__IoCallbackArgs
// Provides additional information about the request.
// response: Object
// The response in the format as defined with handleAs.
},
error: function(response, ioArgs){
// summary:
// 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.
// ioArgs: dojo/main.__IoCallbackArgs
// Provides additional information about the request.
// response: Object
// The response in the format as defined with handleAs.
},
handle: function(loadOrError, response, ioArgs){
// summary:
// This function will
// be called at the end of every request, whether or not an error occurs.
// 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/main.__IoCallbackArgs
// Provides additional information about the request.
}
});
dojo.__IoCallbackArgs = declare(null, {
// 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.
});
dojo.__IoPublish = declare(null, {
// 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.
});
=====*/
dojo._ioSetArgs = function(/*dojo/main.__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 requested.
var formObject = null;
if(args.form){
var form = dom.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 = domForm.toObject(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 = ioq.objectToQuery(lang.mixin.apply(null, miArgs));
// .. and the real work of getting the deferred in order, etc.
ioArgs.handleAs = args.handleAs || "text";
var d = new Deferred(function(dfd){
dfd.canceled = true;
canceller && canceller(dfd);
var err = dfd.ioArgs.error;
if(!err){
err = new Error("request cancelled");
err.dojoType="cancel";
dfd.ioArgs.error = err;
}
return err;
});
d.addCallback(okHandler);
//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 && lang.isFunction(ld)){
d.addCallback(function(value){
return ld.call(args, value, ioArgs);
});
}
var err = args.error;
if(err && lang.isFunction(err)){
d.addErrback(function(value){
return err.call(args, value, ioArgs);
});
}
var handle = args.handle;
if(handle && lang.isFunction(handle)){
d.addBoth(function(value){
return handle.call(args, value, ioArgs);
});
}
// Attach error handler last (not including topic publishing)
// to catch any errors that may have been generated from load
// or handle functions.
d.addErrback(function(error){
return errHandler(error, d);
});
//Plug in topic publishing, if dojo.publish is loaded.
if(cfg.ioPublish && dojo.publish && ioArgs.args.ioPublish !== false){
d.addCallbacks(
function(res){
dojo.publish("/dojo/io/load", [d, res]);
return res;
},
function(res){
dojo.publish("/dojo/io/error", [d, res]);
return res;
}
);
d.addBoth(function(res){
dojo.publish("/dojo/io/done", [d, res]);
return res;
});
}
d.ioArgs = ioArgs;
// FIXME: need to wire up the xhr object's abort method to something
// analogous in the Deferred
return d;
};
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;
};
//Use a separate count for knowing if we are starting/stopping io calls.
var _checkPubCount = function(dfd){
if(_pubCount <= 0){
_pubCount = 0;
if(cfg.ioPublish && dojo.publish && (!dfd || dfd && dfd.ioArgs.args.ioPublish !== false)){
dojo.publish("/dojo/io/stop");
}
}
};
var _pubCount = 0;
aspect.after(watch, "_onAction", function(){
_pubCount -= 1;
});
aspect.after(watch, "_onInFlight", _checkPubCount);
dojo._ioCancelAll = watch.cancelAll;
/*=====
dojo._ioCancelAll = function(){
// summary:
// Cancels all pending IO requests, regardless of IO type
// (xhr, script, iframe).
};
=====*/
dojo._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.
//
// Used by IO transports. An IO transport should
// call this method before making the network connection.
if(cfg.ioPublish && dojo.publish && dfd.ioArgs.args.ioPublish !== false){
if(!_pubCount){
dojo.publish("/dojo/io/start");
}
_pubCount += 1;
dojo.publish("/dojo/io/send", [dfd]);
}
};
dojo._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.options = dfd.ioArgs.args;
lang.mixin(dfd, {
response: dfd.ioArgs,
isValid: function(response){
return validCheck(dfd);
},
isReady: function(response){
return ioCheck(dfd);
},
handleResponse: function(response){
return resHandle(dfd);
}
});
watch(dfd);
_checkPubCount(dfd);
};
var _defaultContentType = "application/x-www-form-urlencoded";
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.__XhrArgs = declare(dojo.__IoArgs, {
// 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/_base/xhr.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)
// contentType: String|Boolean
// "application/x-www-form-urlencoded" is default. Set to false to
// prevent a Content-Type header from being sent, or to a string
// to send a different Content-Type.
});
=====*/
dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){
// summary:
// Deprecated. Use dojo/request instead.
// 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.
var rDfd;
//Make the Deferred object for this xhr request.
var dfd = dojo._ioSetArgs(args, function(dfd){
rDfd && rDfd.cancel();
}, _deferredOk, _deferError);
var ioArgs = dfd.ioArgs;
//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.
dojo._ioAddQueryToUrl(ioArgs);
}
var options = {
method: method,
handleAs: "text",
timeout: args.timeout,
withCredentials: args.withCredentials,
ioArgs: ioArgs
};
if(typeof args.headers !== 'undefined'){
options.headers = args.headers;
}
if(typeof args.contentType !== 'undefined'){
if(!options.headers){
options.headers = {};
}
options.headers['Content-Type'] = args.contentType;
}
if(typeof ioArgs.query !== 'undefined'){
options.data = ioArgs.query;
}
if(typeof args.sync !== 'undefined'){
options.sync = args.sync;
}
dojo._ioNotifyStart(dfd);
try{
rDfd = _xhr(ioArgs.url, options, true);
}catch(e){
// If XHR creation fails, dojo/request/xhr throws
// When this happens, cancel the deferred
dfd.cancel();
return dfd;
}
// sync ioArgs
dfd.ioArgs.xhr = rDfd.response.xhr;
rDfd.then(function(){
dfd.resolve(dfd);
}).otherwise(function(error){
ioArgs.error = error;
if(error.response){
error.status = error.response.status;
error.responseText = error.response.text;
error.xhr = error.response.xhr;
}
dfd.reject(error);
});
return dfd; // dojo/_base/Deferred
};
dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){
// summary:
// Sends an HTTP GET request to the server.
return dojo.xhr("GET", args); // dojo/_base/Deferred
};
dojo.rawXhrPost = dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){
// summary:
// Sends an HTTP POST request to the server. In addition 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 dojo.xhr("POST", args, true); // dojo/_base/Deferred
};
dojo.rawXhrPut = dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){
// summary:
// Sends an HTTP PUT request to the server. In addition 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 dojo.xhr("PUT", args, true); // dojo/_base/Deferred
};
dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){
// summary:
// Sends an HTTP DELETE request to the server.
return dojo.xhr("DELETE", args); // dojo/_base/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");
}
*/
dojo._isDocumentOk = function(x){
return util.checkStatus(x.status);
};
dojo._getText = function(url){
var result;
dojo.xhrGet({url:url, sync:true, load:function(text){
result = text;
}});
return result;
};
// Add aliases for static functions to dojo.xhr since dojo.xhr is what's returned from this module
lang.mixin(dojo.xhr, {
_xhrObj: dojo._xhrObj,
fieldToObject: domForm.fieldToObject,
formToObject: domForm.toObject,
objectToQuery: ioq.objectToQuery,
formToQuery: domForm.toQuery,
formToJson: domForm.toJson,
queryToObject: ioq.queryToObject,
contentHandlers: handlers,
_ioSetArgs: dojo._ioSetArgs,
_ioCancelAll: dojo._ioCancelAll,
_ioNotifyStart: dojo._ioNotifyStart,
_ioWatch: dojo._ioWatch,
_ioAddQueryToUrl: dojo._ioAddQueryToUrl,
_isDocumentOk: dojo._isDocumentOk,
_getText: dojo._getText,
get: dojo.xhrGet,
post: dojo.xhrPost,
put: dojo.xhrPut,
del: dojo.xhrDelete // because "delete" is a reserved word
});
return dojo.xhr;
});