actually the question is as simple as the topic says. Is there any way to give different ajax handlers a higher/lower priority (which means here, that they will fire earlier) ?
What do I mean? Well, I have to deal with a fairly huge web-app. Tons of Ajax requests are fired in different modules. Now, my goal is to implement a simple session-timeout mechanism. Each request sends the current session-id as parameter, if the session-id is not valid anymore, my backend script returns the request with a custom response-header set (value is a uri).
So I'm basically going like this
window.jQuery && jQuery( document ).ajaxComplete(function( event, xhr, settings ) {
var redirectto = xhr.getResponseHeader( 'new_ajax_location' );
if( redirectto ) {
location.href = redirectto;
}
});
This does work like a charm of course, but my problem is that this global ajax event actually needs to get fired first 100% of the time, which is not the case. Some of those original ajax-requests handlers will throw an error because of missing or unexpected data, in that case, the global handler never gets executed.
Now, I'd be kinda happy if I wouldn't need to go through every single request handler and make it failsafe for invalid session data responses. I'd much more prefer to do the job at one particular place. But how can I make sure my global handler will get executed first ?
If I understand you correctly, you want to add your own callback to json requests everywhere. You could use the ajaxPrefilter hook.
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
options.complete = function (jqXHR2, settings) {
var redirectto = jqXHR2.getResponseHeader( 'new_ajax_location' );
if( redirectto ) {
location.href = redirectto;
}
}
});
You can also change the options there to add the session-id to the request params.
jQuery allows you to "patch" it's methods, such as .post/.ajax.
You might be able to patch the appropriate method(s) so your special AJAX event is always triggered before the one your code wants to call.
This is some "pseudo-jQuery-code" which should help you get started. I doubt it works as is, but it demonstrates the basic concept and it should get you started.
(function( $ ){
var originalPost = $.post;
var calledSpecialOne = false;
$.post = function(url, data, success, dataType) {
if (!calledSpecialOne) {
originalPost( ... your special AJAX query ... ).then( function() {
originalPost(url, data, success, dataType);
}
calledSpecialOne = true;
} else {
originalPost(url, data, success, dataType);
}
}
})( jQuery );
The above is based on some other, unrelated, code I have tested, which makes $.each() work for undefined/null arrays):
(function($){
var originalEach = $.each;
$.each = function(collection, callback) {
return collection? originalEach(collection, callback) : [];
}
})(jQuery);
Note that many exposed functions are called internally by jQuery too, so be VERY carefull using this trick; you might break a different part of jQuery or cause other trouble.
In case of the AJAX functions, you should probably patch the innermost function only, to prevent infinite recursion.
(FWIW, I haven't found any side-effects for the $.each() patch so far).
I'm not sure if this is even what you meant, but I figured I might need it as some point anyway.
It patches jQuery's ajax function to accept an object with the property priority. If there isn't a priority set, its priority becomes 1 (highest priority 0). If there is a priority, it does one of two things. If the priority is something like 5, it checks to see if the ajax call with the previous priority (in this case 4) was called. If not, it adds it to an array of ajax calls with that priority (in this case 5). If the previous priority has been called, it calls the request normally. When a request is called with a priority, it calls any outgoing requests with the next highest priority.
In other words, if there's no priority, it waits for priority 0, if there is a priority, it waits for the priority below it to get called. If you want calls with no priority to be sent immediately, comment out the line with the comment I put saying default action?
(function( $ ){
var oldAjax=$.ajax;
var outgoing=[];
var sent=[];
$.ajax=function(url, settings) {
var pr = (typeof url==='object'?url:settings).priority;
pr = pr===undefined ? 1 : pr; // default action?
if(pr!==undefined){
if(pr==0 || sent[pr-1]!==undefined){
oldAjax(url,settings);
sent[pr]=true;
if(outgoing[pr]){
var rq=outgoing[pr].splice(0,1)[0];
$.ajax(rq[0],rq[1]);
}
if(outgoing[pr+1]){
var rq=outgoing[pr+1].splice(0,1)[0];
$.ajax(rq[0],rq[1]);
}
}
else{
if(outgoing[pr]!==undefined){
outgoing[pr].push([url,settings]);
}
else{
outgoing[pr]=[[url,settings]];
}
}
}
else{
oldAjax(url, settings);
}
};
})( jQuery );
Patches for get and post. You will have to include those extra arguments, or change this yourself to get the priority working with post and get requests. This is straight from jQuery 1.7, except for the extra argument, and the two lines I added comments to.
jQuery.each( [ "get", "post" ], function( i, method ) {
jQuery[ method ] = function( url, data, callback, type, priority ) {
// shift arguments if data argument was omitted
if ( jQuery.isFunction( data ) ) {
priority = type; // my change
type = type || callback;
callback = data;
data = undefined;
}
return jQuery.ajax({
type: method,
url: url,
data: data,
success: callback,
dataType: type,
priority: priority // my change
});
};
});
I setup an example here: http://jsfiddle.net/tk39z/
I actually created some ajax requests targeting google, so you can check the network panel in Chrome to see the requests.
I recently implemented something very similar to the functionality that I believe you're looking for.
I set up a filter on the server side to test for a valid session and, if not, return a 401 error with custom status text for a more specific error detail.
The ajax calls I made to these pages were written in the normal convention using the 'success' attribute.
For situations where there was an error, I added a global callback to the error function that would test for the specific error and redirect accordingly. such as:
$( document ).ajaxError(function(e, xhr) {
var redirectto = xhr.getResponseHeader( 'new_ajax_location' );
if( redirectto ) {
location.href = redirectto;
}
});
Related
I am trying to find out how to identify the function name which initiated the Ajax call programmatically using JavaScript or jQuery for an existing code base.
I am working on the instrumentation of an existing large code base and hence trying to identify the function name which correlates with the AJAX requests. So, there is already a lot of code and modifying each method doesn't seem to be the best approach. So, I am trying to think about a generic approach which can work for all the methods. The code already has a generic wrapper around the AJAX call, so I can hook into the entry and exit points of the method.
For e.g. in below code, in the always function, I need to know the initiator function name or the call stack.
function ajaxWrapper(){
var jqxhr = $.ajax( "https://jsonplaceholder.typicode.com/posts/1" )
.done(function() {
console.log( "success" );
})
.fail(function() {
console.log( "error" );
})
.always(function() {
console.log( "complete" );
// TODO: Who Initiated the call ?
});
}
function initiator1(){
initiator2();
}
function initiator2(){
ajaxWrapper();
}
initiator1();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
What have I tried?
I know one approach is using setting the custom headers in the beforeSend method and the always callback receives the jqXHR object as the parameter. But this doesn't seem to be suitable approach as I can't modify all the functions to send the method name as a parameter.
arguments.callee.caller approach, but it doesn't seem to work in case of the Asynchronous methods. Also, this feature seems to be deprecated because of performance and security reasons.
Note: I am not looking this information for debugging purpose. I am already aware of the Async call stack, Initiator and console.trace solutions available in developers tool.
One option is to generate the call stack inside your wrapper, because up to that point you are in a sync function call chain.
var err = new Error();
var stack = err.stack;
And use it later in the callback, because it's available as part of a higher scope. (Scopes don't care about async.)
You'll need to do some parsing (browser specific unfortunately) on that call stack. I don't know what you intend to do with the initiator, but you may want to keep all the chain, not just the first one.
Also note that this technique is unreliable and you shouldn't use it for anything critical.
function ajaxWrapper(){
var err = new Error();
var stack = err.stack;
var jqxhr = $.ajax( "https://jsonplaceholder.typicode.com/posts/1" )
.done(function() {
console.log( "success" );
})
.fail(function() {
console.log( "error" );
})
.always(function() {
console.log( "complete" );
// TODO: Who Initiated the call ?
console.log(stack);
});
}
function initiator1(){
initiator2();
}
function initiator2(){
ajaxWrapper();
}
initiator1();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
What kind of handler/hook do I need to set from Greasemonkey script to capture small changes like adding new page elements (thinking of FB messages..)?
Can I change the style and innerhtml before the element is drawn?
You can override many native functions. If it was the element creation, you'd override document.createElement:
//Remember the old function refference
var old_createElement = document.createElement;
//Override the native function
document.createElement = function(tagName) {
//Our own script for the function
if(!confirm("Element "+tagName+" is being created. Allow?"))
throw new Error("User denied creation of a element.");
else
//And eventual call for the original function
return old_createElement(tagName);
}
Regarding the DOM elements, there seems to be no means of capturing the DOM parser element creation. (creation from HTML string)
Similarly, you can override AJAX methods and in fact I have done this on facebook to see how messages are sent - and I noticed they're sent with tons of other data.
Here's a part of my greasemonkey script for this purpose:
function addXMLRequestCallback(event, callback){
var oldSend, i;
if( XMLHttpRequest.callbacks!=null ) {
// we've already overridden send() so just add the callback
//XMLHttpRequest.callbacks.push( callback );
if(XMLHttpRequest.callbacks[event]!=null)
XMLHttpRequest.callbacks[event].push(callback);
} else {
// create a callback queue
XMLHttpRequest.callbacks = {send:[], readystatechange:[]};
if(XMLHttpRequest.callbacks[event]!=null)
XMLHttpRequest.callbacks[event].push(callback);
// store the native send()
oldSend = XMLHttpRequest.prototype.send;
// override the native send()
XMLHttpRequest.prototype.send = function() {
// process the callback queue
// the xhr instance is passed into each callback but seems pretty useless
// you can't tell what its destination is or call abort() without an error
// so only really good for logging that a request has happened
// I could be wrong, I hope so...
// EDIT: I suppose you could override the onreadystatechange handler though
for( i = 0; i < XMLHttpRequest.callbacks.send.length; i++ ) {
XMLHttpRequest.callbacks.send[i].apply( this, arguments );
}
/*if(typeof this.onreadystatechange == "function")
callbacks.readystatechange.push(this.onreadystatechange);*/
var old_onreadystatechange = this.onreadystatechange;
this.onreadystatechange = function(event) {
for( i = 0; i < XMLHttpRequest.callbacks.readystatechange.length; i++ ) {
try {
XMLHttpRequest.callbacks.readystatechange[i].apply( this, arguments );
}
catch(error) {
console.error(error);
}
}
if(typeof old_onreadystatechange == "function") {
old_onreadystatechange.apply(this, arguments)
}
}
// call the native send()
oldSend.apply(this, arguments);
}
}
}
//Usage
addXMLRequestCallback( "send", function(data) {
console.log(data);
});
addXMLRequestCallback( "onreadystatechange", ...);
I also often use the MutationObserver in userscripts. At allows to to watch over either properties or children and calls a callback for every added/removed node.
I'm not sure how good the performance is and how easy will it be to hook up the correct node.
If you had eventually succeded in capturing creation of facebook chat message containers and/or posts on wall, I'd really love to see how you did it.
For a long time, I'm thinking of adding Markdown on facebook. Many friends share sourcecode here but it's barely readable.
I have my own custom error code inside of a backbone ajax success method in case the server returns an error. The problem is that this code is repeated throughout my app and I wanted to edit the success function in one place so I don't have to constantly repeat this error handler in every ajax success. I want to edit the success function to include this error check wrapper. Do you know how to do that?
Here is an example of my success method in one of my views:
"success" : function success(model, data)
{
if(data['error'] !== undefined && data['error'].length === 0)
{
message('error', 'Whoops! System Error. Please refresh your page.');
}
else if(data['error'] !== undefined)
{
message('error', data['error']);
}
else
{
//add templates and do stuff here
}
},
Ideally I'd like to set that in a config somewhere and then I'd just be able to use:
"success" : function success(model, data)
{
// add templates and do stuff here
}
Is this possible? I tried using ajaxSetup but that didn't seem to work for me.
UPDATED CODE STILL NOT WORKING:
That does get me a little further along but the error handler isn't functioning as a wrapper. The data is not being passed into my ajax calls. In fact, my success methods on my ajax calls aren't running at all anymore. I tried console.log("some text") in my ajax calls but nothing is being output. Do you know what is wrong with this?
// Save the original Sync method
defaultSync = Backbone.sync;
//Over ride Backbone async
Backbone.sync = function(method,
model,
options)
{
success = options.success
options.success = function(data)
{
if(data['error'] !== undefined && data['error'].length === 0)
{
message('error', 'Whoops! System Error. Please refresh your page.');
}
else if(data['error'] !== undefined)
{
message('error', data['error']);
}
else
{
success(model,
data);
}
}
return defaultSync(method,
model,
options)
}
There are two ways to solve this:
Inheriting Backbone Model
You could create your own custom model which inherits from Backbone Model. In it you could override the save method. Read Backbone docs on how to extend their model
In your custom save method, you will call the save method of super, check the responseText, if it's success then you'll call the success callback. (please do read backbone docs on how to call a method of your parent model in Javascript)
Override Backbone.Sync
Backbone has a Sync module which basically by default makes all ajax requests, parses the response and then calls the success/error callbacks you specified when calling save on your model. It's pretty simple. Take a look at this doc . Again you could override this, do exactly what Backbone is doing by default but only call the success/error callbacks based on responseText you received.
UPDATE: Sample Code (warning code not tested)
//Over ride Backbone async
defaultSync = Backbone.Sync // Save the original Sync method. We'll be needing that.
Backbone.Sync = function(method, model, options) {
success = options.success
error = options.error
options.success = function(model, data, options) {
if (/% all your custom checks are true */) {
success (model, data, options);
}
else {
error(model,data,options);
}
}
return defaultSync(method, model, options);
}
Please make sure with this strategy the Sync method will be overriden for ALL your Backbone sync. If you don't want that then use Model#save override.
Take a look at this code where I am overriding Backbone.Sync to make it work with Parse.com API.
I have a application where there are numerous number of ajax calls to the server.
Now I want to audit the response that comes from the server (This requirement poped up after the ajax code was laid).
So I have a function that would audit the response data, only problem is how can I get the data to be sent to the function which now sits separately.
I don't want to do the laborious work of adding the line of code for calling the function in each ajax call.
Is there easier and general way out. Somehow I could detect when a response come back and then process the response.
Using both traditional javascript method as well as jquery ajax calls in the system. (The app has been getting changes from a long time and has changed hands a lot so the new things get added and the older ones never get removed)
Wrap your ajax calls with a helper function and use it throughout your code.
An (untested) example:
MyApp = MyApp || {
logRequest: function _logRequest(settings, response) {
// Log your response
},
ajax: function _ajax (settings) {
var that = this;
// Log attempt request here?
// Example of logging the success callback (do similar for error or complete)
if (settings.success) {
// A success handler is already specified
settings.success = function (data) {
that.logRequest(settings, data); // Log the response
settings.success(data); // Call the original complete handler
};
} else {
// No success handler is specified
settings.success = function (data) {
that.logRequest(settings, data);
};
}
return jQuery.ajax(settings);
}
};
I favour this mechanism for lots situations where I want to reduce boilerplate. I only have to modify the state of the MyApp object which is my own (named appropriately for the application), so it is sort of an interface that allows you to intercept function calls without modifying other global objects. You can also swap this functionality out with something else very easily without having to update your references everywhere, which could be useful in a lot of other situations as well.
Using .ajaxComplete() should be enough to catch the onComplete event for all AJAX requests made through jQuery. Isn´t that what you´re asking for?
$('.ajaxRequest').click(function(event) {
event.preventDefault();
$.getJSON(
'/echo/json/',
this.id,
function(data, textStatus, jqXHR) {
console.log(data, textStatus, jqXHR);
}
);
});
// Listen to all ajax requests
$("#log").ajaxComplete(function(event, request, settings) {
console.log(event, request, settings);
});
View demo.
I have an application that uses Ajax.Request and its onSuccess event handler in lots of places.
I need to call a function (that will check the response) before all these onSuccess events fire. I tried using Ajax.Responders.register with onComplete event but it fires after Ajax.Request's onSuccess event. Any suggestions?
similar to Aleksander Krzywinski's answer, but I believe this would prevent you from having to sprinkle the use of "wrap" everywhere, by consolidating it to the onCreate Responder.
Ajax.Responders.register({
onCreate: function(request) {
request.options['onSuccess'] = request.options['onSuccess'].wrap(validateResponse);
}
});
There are several events to chose from. Here is the event chain for Ajax.Request:
onCreate
onUninitialized
onLoading
onLoaded
onInteractive
onXYZ, onSuccess or onFailure
onComplete
onLoading, onLoaded, onInteractive sound interesting, but according to the spec they are not guaranteed to happen. That leaves you with the possibility to hook on to onCreate, which is called just after the request object is build, but before the request is actually made.
This might be a little late, but for the benefit of anyone else wondering about the same problem I will propose this solution:
You can use Prototypes own implementation of aspect-oriented programming to do this. Granted you will have to modify all your onSuccess-parameters, but it can be done with a simple search-and-replace, instead of updating all your callback functions. Here is an example Ajax.Request creation:
new Ajax.Request('example.html', {
parameters: {action: 'update'},
onSuccess: this.updateSuccessful
});
Say you have similar code snippets spread all over your code, and you want to preceed them all with a certain function that validates the response before the actual function is run(or even prevented from running at all). By using Funtion.wrap supplied in Prototype we can do this by extending the code above:
new Ajax.Request('example.html', {
parameters: {action: 'update'},
onSuccess: this.updateSuccessful.wrap(validateResponse)
});
Where 'validateResponse' is a function similar to this:
// If you use the X-JSON-header of the response for JSON, add the third param
function validateResponse(originalFn, transport /*, json */) {
// Validate the transport
if (someConditionMet) {
originalFn(transport /*, json */);
}
}
Thus you have extended your onSuccess-functions in one central place with just a quick search for onSuccess and pasting in 'wrap(validateResponse)'. This also gives you the option of having several wrapper-functions depending on the needs of the particular Ajax-request.
You can run your method before the other code in onSuccess and return false if something is wrong.
Don't know if this is the cleanest solution, but for me it did the trick.
var tmp = Ajax.Request;
Ajax.Request = function(url, args) {
// stuff to do before the request is sent
var c = Object.clone(args);
c.onSuccess = function(transport){
// stuff to do when request is successful, before the callback
args.onSuccess(transport);
}
var a = new tmp(url, c);
return a;
}
Ajax.Request.protoype = new tmp();
Ajax.Request.Events = tmp.Events;
delete tmp;
"General solution" - independent upon JS framework (kind of)
var oldFunc = Ajax.Request.onSuccess;
Ajax.Request.onSuccess = function foo() {
alert('t');
oldFunc.apply(this, arguments);
}
This will "extend" your JS function making it do exactly what it used to do except show an alert box every time before it executes...