Any way to simulate a *synchronous* XDomainRequest (XDR) request - javascript

We're making a cross domain call to the google maps geocode API. This was and is working all fine and dandy in modern browsers, but it wasn't working at all in IE8. Looks like it would fail in IE9 as well (partial CORS support). This led to including a XDomainRequest (XDR) to take care of IE8-9. Doing that worked fine in my standalone test to get data back in IE8.
The problem I'm running into now is XDR only works asynchronously so my geocode function returns before my xdr.onload fires.
In my search function, I call the geocode function:
var location = Geocode(city, state);
if (!location) {
alert('Unable to determine the location of the city and state you entered');
StopLoading();
return;
}
//function then uses location.lat and location.lng coordinates
I'm hitting the "Unable to determine location" alert above in IE8.
Here's my geocode function:
Geocode = function (address, state) {
var protocol = location.protocol,
url = '//maps.googleapis.com/maps/api/geocode/json?sensor=false&address=',
param = encodeURIComponent(address + ', ' + state),
json = {};
if ('XDomainRequest' in window && window.XDomainRequest !== null) {
//IEs that do not support cross domain xhr requests
var xdr = new XDomainRequest();
xdr.open('get', protocol + url + param);
xdr.onload = function() {
json = jQuery.parseJSON(xdr.responseText);
};
xdr.send();
} else {
//good browsers
jQuery.ajax({
url: protocol + url + param,
type: 'get',
dataType: 'json',
async: false,
success: function(data) {
json = data;
}
});
}
//alert(json);
if (json.status !== 'OK') {
alert('Unable to determine the location of the city and state you entered');
return null;
}
return json.results[0].geometry.location;
};
If I comment out the alert(json) in the geocode function, I get my results in IE8 because that's a blocking operation so the request has time to finish and populates my json object. When it's run uncommented, the json object isn't populated.
Anyone have any ideas how I can get this working in IE?

asynchron is asynchron. If you want to do something after the request is finished u have to put it into the xdr.onload function.
There is no "wait" function in javascript. You could build one and do a setTimeout loop to check the variable all x-miliseconds. But that would not help u in this case (and its very ugly).
In your case you can use the onerror and ontimeout to check if the server had a problem, and the onload to check if the city is in the json.
xdr.onload = function() {
json = jQuery.parseJSON(xdr.responseText);
//check if a city is loaded, go on if true
};
xdr.onerror = function() {
alert('Unable to determine the location of the city and state you entered');
//do whatever u wanna do if something went wrong
xdr.ontimeout = function() {
alert('404 Server');
//do whatever u wanna do if something went wrong
}
i hope this helps you find the way (and by the way, the use of async requests is a much a better way then block the hole javascript/browser ;)
The jQuery doc says:
As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred)
is deprecated; you must use the success/error/complete callback
options instead of the corresponding methods of the jqXHR object such
as jqXHR.done() or the deprecated jqXHR.success()

Related

How to track http requests/responses using JavaScript? [duplicate]

I'm trying to intercept all AJAX calls in order to check if that AJAX response contains specific error code that I send as JSON from my PHP script (codes: ACCESS_DENIED, SYSTEM_ERROR, NOT_FOUND).
I know one can do something like this:
$('.log').ajaxSuccess(function(e, xhr, settings) {
});
But - does this work only if "ajaxSuccess" event bubble up to .log div? Am I correct? Can I achieve what I want by binding "ajaxSuccess" event to document?
$(document).ajaxSuccess(function(e, xhr, settings) {
});
I can do this in either jQuery or raw JavaScript.
If you're using jQuery, $.ajaxSuccess is a good option, but here's a generic option that will intercept XHR calls from all frameworks (I've tested it with ExtJS and jQuery - it should work even if multiple frameworks are loaded concurrently). It's been tested to work with IE8, Chrome and Firefox.
(function(XHR) {
"use strict";
var open = XHR.prototype.open;
var send = XHR.prototype.send;
XHR.prototype.open = function(method, url, async, user, pass) {
this._url = url;
open.call(this, method, url, async, user, pass);
};
XHR.prototype.send = function(data) {
var self = this;
var oldOnReadyStateChange;
var url = this._url;
function onReadyStateChange() {
if(self.readyState == 4 /* complete */) {
/* This is where you can put code that you want to execute post-complete*/
/* URL is kept in this._url */
}
if(oldOnReadyStateChange) {
oldOnReadyStateChange();
}
}
/* Set xhr.noIntercept to true to disable the interceptor for a particular call */
if(!this.noIntercept) {
if(this.addEventListener) {
this.addEventListener("readystatechange", onReadyStateChange, false);
} else {
oldOnReadyStateChange = this.onreadystatechange;
this.onreadystatechange = onReadyStateChange;
}
}
send.call(this, data);
}
})(XMLHttpRequest);
I've posted a more specific example on github which intercepts AJAX calls and posts the AJAX call durations back to the server for statistical analysis.
From http://api.jquery.com/ajaxSuccess/ :
Whenever an Ajax request completes successfully, jQuery triggers the ajaxSuccess event. Any and all handlers that have been registered with the .ajaxSuccess() method are executed at this time.
So the selector doesn't define the position where you are "catching" the event (because, honestly, ajax event by its nature doesn't start from a DOM element), but rather defines a scope to which the handling will be defaulted (i.e. this will poitn to that/those element(s)).
In summary - it should be exactly what you wish for
The best way, which I found https://lowrey.me/intercept-2/
const intercept = (urlmatch, callback) => {
let send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
this.addEventListener('readystatechange', function() {
if (this.responseURL.includes(urlmatch) && this.readyState === 4) {
callback(this);
}
}, false);
send.apply(this, arguments);
};
};
Try using Mockjax.js http://code.appendto.com/plugins/jquery-mockjax
It lets you hijack AJAX calls to the server and mock the location.

Prototype validation not working correctly

I use Prototype.js to validate a form. For one of the fields, I have the prototype script ajax a request to a file. The file is a simple PHP file and will return '1' if the value is OK and '0' if the value is not OK. I have the script as below, which should work perfectly. The prototype validation is supposed to show a validation error message when a field does not pass validation, and not display / remove the message once the field passes validation. But in this case, even when the ajax file returns '1', the validation will display the error message anyway. Anyone able to help would be greatly appreciated!
['validate-number3', numessage3, function(v) {
new Ajax.Request('test.php?nr='+v, {
method:'get',
onSuccess: function(transport) {
var response = transport.responseText;
if(response == '1'){return true;}else{return false};
}
});
}],
the return value from Ajax.Request is the Ajax.Request object and returns as soon as the request is setup - the onsuccess callback is called after the request has been completed - so checking the results of Ajax.Request is not useful for what you want to accomplish.
The reason that this doesn't work as you expect, this is an asynchronous call which means it will start the call and then return control to the script while it is processing and then run the callbacks when it is completed.
Try it this way
new Ajax.Request('test.php?nr='+v, {
method:'get',
onSuccess: handleResponse
});
function handleResponse( transport ){
var response = transport.responseText;
if(response == '1'){
//everything is OK
}else{
//value is not OK
};
}
I was able to solve my question!
Thanks to this teriffic page: http://inchoo.net/ecommerce/magento/magento-frontend/magento-form-field-ajax-validation/ it was no problem. This is what I ended up with:
var ok = false;
new Ajax.Request('test.php?nr='+v, {
method:'get',
asynchronous: false,
onSuccess: function(transport) {
var response = transport.responseText;
if(response == '1'){ok = true;}else{ok = false;};
},
onComplete: function() {
if ($('advice-validate-number-pay_bank_no')) {
$('advice-validate-number-pay_bank_no').remove();
}
}
});
return ok;

How to set my local javascript variable as a json data on remote website

I have a javascript code on my website, there is a variable:
var remoteJsonVar;
On the other hand there is a json file on a remote website
https://graph.facebook.com/?ids=http://www.stackoverflow.com
I need to set the variable remoteJsonVar to this remote jason data.
I am sure that it is very simple, but I can't find the solution.
A small working example would be nice.
Because you're trying to get the data from a different origin, if you want to do this entirely client-side, you'd use JSON-P rather than just JSON because of the Same Origin Policy. Facebook supports this if you just add a callback parameter to your query string, e.g.:
https://graph.facebook.com/?ids=http://www.stackoverflow.com?callback=foo
Then you define a function in your script (at global scope) which has the name you give in that callback parameter, like this:
function foo(data) {
remoteJsonVar = data;
}
You trigger it by creating a script element and setting the src to the desired URL, e.g.:
var script = document.createElement('script');
script.src = "https://graph.facebook.com/?ids=http://www.stackoverflow.com?callback=foo";
document.documentElement.appendChild(script);
Note that the call to your function will be asynchronous.
Now, since you may want to have more than one outstanding request, and you probably don't want to leave that callback lying around when you're done, you may want to be a bit more sophisticated and create a random callback name, etc. Here's a complete example:
Live copy | Live source
(function() {
// Your variable; if you prefer, it could be a global,
// but I try to avoid globals where I can
var responseJsonVar;
// Hook up the button
hookEvent(document.getElementById("theButton"),
"click",
function() {
var callbackName, script;
// Get a random name for our callback
callbackName = "foo" + new Date().getTime() + Math.floor(Math.random() * 10000);
// Create it
window[callbackName] = function(data) {
responseJsonVar = data;
display("Got the data, <code>shares = " +
data["http://www.stackoverflow.com"].shares +
"</code>");
// Remove our callback (`delete` with `window` properties
// fails on some versions of IE, so we fall back to setting
// the property to `undefined` if that happens)
try {
delete window[callbackName];
}
catch (e) {
window[callbackName] = undefined;
}
}
// Do the JSONP request
script = document.createElement('script');
script.src = "https://graph.facebook.com/?ids=http://www.stackoverflow.com&callback=" + callbackName;
document.documentElement.appendChild(script);
display("Request started");
});
// === Basic utility functions
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
function hookEvent(element, eventName, handler) {
// Very quick-and-dirty, recommend using a proper library,
// this is just for the purposes of the example.
if (typeof element.addEventListener !== "undefined") {
element.addEventListener(eventName, handler, false);
}
else if (typeof element.attachEvent !== "undefined") {
element.attachEvent("on" + eventName, function(event) {
return handler(event || window.event);
});
}
else {
throw "Browser not supported.";
}
}
})();
Note that when you use JSONP, you're putting a lot of trust in the site at the other end. Technically, JSONP isn't JSON at all, it's giving the remote site the opportunity to run code on your page. If you trust the other end, great, but just remember the potential for abuse.
You haven't mentioned using any libraries, so I haven't used any above, but I would recommend looking at a good JavaScript library like jQuery, Prototype, YUI, Closure, or any of several others. A lot of the code above has already been written for you with a good library. For instance, here's the above using jQuery:
Live copy | Live source
jQuery(function($) {
// Your variable
var responseJsonVar;
$("#theButton").click(function() {
display("Sending request");
$.get("https://graph.facebook.com/?ids=http://www.stackoverflow.com&callback=?",
function(data) {
responseJsonVar = data;
display("Got the data, <code>shares = " +
data["http://www.stackoverflow.com"].shares +
"</code>");
},
"jsonp");
});
function display(msg) {
$("<p>").html(msg).appendTo(document.body);
}
});

Intercept all ajax calls?

I'm trying to intercept all AJAX calls in order to check if that AJAX response contains specific error code that I send as JSON from my PHP script (codes: ACCESS_DENIED, SYSTEM_ERROR, NOT_FOUND).
I know one can do something like this:
$('.log').ajaxSuccess(function(e, xhr, settings) {
});
But - does this work only if "ajaxSuccess" event bubble up to .log div? Am I correct? Can I achieve what I want by binding "ajaxSuccess" event to document?
$(document).ajaxSuccess(function(e, xhr, settings) {
});
I can do this in either jQuery or raw JavaScript.
If you're using jQuery, $.ajaxSuccess is a good option, but here's a generic option that will intercept XHR calls from all frameworks (I've tested it with ExtJS and jQuery - it should work even if multiple frameworks are loaded concurrently). It's been tested to work with IE8, Chrome and Firefox.
(function(XHR) {
"use strict";
var open = XHR.prototype.open;
var send = XHR.prototype.send;
XHR.prototype.open = function(method, url, async, user, pass) {
this._url = url;
open.call(this, method, url, async, user, pass);
};
XHR.prototype.send = function(data) {
var self = this;
var oldOnReadyStateChange;
var url = this._url;
function onReadyStateChange() {
if(self.readyState == 4 /* complete */) {
/* This is where you can put code that you want to execute post-complete*/
/* URL is kept in this._url */
}
if(oldOnReadyStateChange) {
oldOnReadyStateChange();
}
}
/* Set xhr.noIntercept to true to disable the interceptor for a particular call */
if(!this.noIntercept) {
if(this.addEventListener) {
this.addEventListener("readystatechange", onReadyStateChange, false);
} else {
oldOnReadyStateChange = this.onreadystatechange;
this.onreadystatechange = onReadyStateChange;
}
}
send.call(this, data);
}
})(XMLHttpRequest);
I've posted a more specific example on github which intercepts AJAX calls and posts the AJAX call durations back to the server for statistical analysis.
From http://api.jquery.com/ajaxSuccess/ :
Whenever an Ajax request completes successfully, jQuery triggers the ajaxSuccess event. Any and all handlers that have been registered with the .ajaxSuccess() method are executed at this time.
So the selector doesn't define the position where you are "catching" the event (because, honestly, ajax event by its nature doesn't start from a DOM element), but rather defines a scope to which the handling will be defaulted (i.e. this will poitn to that/those element(s)).
In summary - it should be exactly what you wish for
The best way, which I found https://lowrey.me/intercept-2/
const intercept = (urlmatch, callback) => {
let send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
this.addEventListener('readystatechange', function() {
if (this.responseURL.includes(urlmatch) && this.readyState === 4) {
callback(this);
}
}, false);
send.apply(this, arguments);
};
};
Try using Mockjax.js http://code.appendto.com/plugins/jquery-mockjax
It lets you hijack AJAX calls to the server and mock the location.

jquery ajax success result is null

I am doing an ajax call using jquery to get data in json format. the success callback function is called but the data is empty.
$(document).ready(function () {
$.ajax({
url: "http://apps.sungardhe.com/StudentResearch/public/Research.svc/Schools",
type: "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: cbSchools
});
});
function cbSchools(data) {
if (data == null) {
alert("data is null");
return;
}
for (var school in data) {
$("#ddSchool").append("<option value='" + data[school].ShortName + "'>" + data[school].ShortName + "</option>");
}
}
using fiddler I see that the response is actually returning the json data but for some reason the jquery result object is null. can anyone tell me why?
You're being blocked by the same-origin policy which prevents cross-domain XMLHttpRequests. Since you need to set headers to get JSON back from a .Net web service like this, you're in a tough spot, you just can't make that kind of request from a browser, not from a different domain.
Fiddler may be showing the content, but the browser isn't going to let the page see it, for security reasons it'll always be null. The one way around this is JSONP, but unfortunately it doesn't look like that service is setup to support it.
I believe you can make your calls generic (reason as marduk indicates)
To handle this, and to make calls generic (works with data and data.d), I use the following in my ajax calls (with my asp.net stuff) so that it works with older as well as newer services:
dataFilter: function(data)
{
var msg;
if (typeof (JSON) !== 'undefined' &&
typeof (JSON.parse) === 'function')
msg = JSON.parse(data);
else
msg = eval('(' + data + ')');
if (msg.hasOwnProperty('d'))
return msg.d;
else
return msg;
},
EDIT: IF it is truly null AND NOT "undefined" then the cross domain issue might be in play here.
try this
if (data.d == null) {
alert("data.d is null");
return;
}
since your return datatype is json, the data is in the data, "d", variable in the response object.

Categories