package com.google.gwt.query.client.plugins.ajax;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.dom.client.Element;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.Response;
import com.google.gwt.query.client.IsProperties;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQ;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.Promise;
import com.google.gwt.query.client.builders.JsonBuilder;
import com.google.gwt.query.client.js.JsUtils;
import com.google.gwt.query.client.plugins.Plugin;
import com.google.gwt.user.client.ui.FormPanel;
/**
* Ajax class for GQuery.
*
* The jQuery library has a full suite of AJAX capabilities, but GWT is plenty of classes to get
* data from server side: RPC, XHR, RF, etc.
*
* This class is not a substitute for the GWT utilities, but a complement to get server data in a
* jquery way, specially when querying non java backends.
*
* We do not pretend to clone all the jquery Ajax API inside gquery, just take its syntax and to
* implement the most popular usage of it. This implementation is almost thought to be used as an
* alternative to the GWT-XHR, GWT-XML and GWT-JSON modules.
*
*/
public class Ajax extends GQuery {
public static final String JSON_CONTENT_TYPE = "application/json";
public static final String JSON_CONTENT_TYPE_UTF8 = JSON_CONTENT_TYPE + "; charset=utf-8";
public static interface AjaxTransport {
Promise getJsonP(Settings settings);
Promise getLoadScript(Settings settings);
Promise getXhr(Settings settings);
}
/**
* Ajax Settings object
*/
public interface Settings extends JsonBuilder {
String getContentType();
Element getContext();
IsProperties getData();
String getDataString();
String getDataType();
Function getError();
IsProperties getHeaders();
String getPassword();
Function getSuccess();
int getTimeout();
String getType();
String getUrl();
String getUsername();
boolean getWithCredentials();
Settings setContentType(String t);
Settings setContext(Element e);
Settings setData(Object p);
Settings setDataString(String d);
Settings setDataType(String t);
Settings setError(Function f);
Settings setHeaders(IsProperties p);
Settings setPassword(String p);
Settings setSuccess(Function f);
Settings setTimeout(int t);
Settings setType(String t);
Settings setUrl(String u);
Settings setUsername(String u);
Settings setWithCredentials(boolean b);
}
public static final Class<Ajax> Ajax = registerPlugin(Ajax.class, new Plugin<Ajax>() {
public Ajax init(GQuery gq) {
return new Ajax(gq);
}
});
public static Promise ajax(IsProperties p) {
Settings s = createSettings();
s.load(p);
return ajax(s);
}
/**
* Perform an ajax request to the server.
*
*
* Example:
*
* <pre>
import static com.google.gwt.query.client.GQ.*
...
Properties properties = $$("dataType: xml, type: post; data: {q: 'gwt'}, headers: {X-Powered-By: GQuery}");
ajax("test.php", new Function() {
public void f() {
Element xmlElem = getData()[0];
System.out.println($("message", xmlElem));
}
}, new Function(){
public void f() {
System.err.println("Ajax Error: " + getData()[1]);
}
}, properties);
* </pre>
*
* @param url The url to connect
* @param onSuccess a function to execute in the case of success
* @param onError the function to execute on error
* @param settings a Properties object with the configuration of the Ajax request.
*/
public static Promise ajax(Settings settings) {
resolveSettings(settings);
final Function onSuccess = settings.getSuccess();
if (onSuccess != null) {
onSuccess.setElement(settings.getContext());
}
final Function onError = settings.getError();
if (onError != null) {
onError.setElement(settings.getContext());
}
final String dataType = settings.getDataType();
Promise ret = null;
if ("jsonp".equalsIgnoreCase(dataType)) {
ret = GQ.getAjaxTransport().getJsonP(settings);
} else if ("loadscript".equalsIgnoreCase(dataType)){
ret = GQ.getAjaxTransport().getLoadScript(settings);
} else {
ret = GQ.getAjaxTransport().getXhr(settings)
.then(new Function() {
public Object f(Object...args) {
Response response = arguments(0);
Request request = arguments(1);
Object retData = response.getText();
if (retData != null && !"".equals(retData)) {
try {
if ("xml".equalsIgnoreCase(dataType)) {
retData = JsUtils.parseXML(response.getText());
} else if ("json".equalsIgnoreCase(dataType)) {
retData = GQ.create(response.getText());
} else {
retData = response.getText();
if ("script".equalsIgnoreCase(dataType)) {
ScriptInjector.fromString((String)retData).setWindow(window).inject();
}
}
} catch (Exception e) {
if (GWT.isClient() && GWT.getUncaughtExceptionHandler() != null) {
GWT.getUncaughtExceptionHandler().onUncaughtException(e);
} else {
e.printStackTrace();
}
}
}
return new Object[]{retData, "success", request, response};
}
}, new Function() {
public Object f(Object...args) {
Throwable exception = arguments(0);
Request request = getArgument(1, Request.class);
String msg = String.valueOf(exception);
return new Object[]{null, msg, request, null, exception};
}
});
}
if (onSuccess != null) {
ret.done(onSuccess);
}
if (onError != null) {
ret.fail(onError);
}
return ret;
}
private static void resolveSettings(Settings settings) {
String url = settings.getUrl();
assert settings != null && settings.getUrl() != null: "no url found in settings";
String type = "POST";
if (settings.getType() != null) {
type = settings.getType().toUpperCase();
}
if ("jsonp".equalsIgnoreCase(settings.getDataType())) {
type = "GET";
}
settings.setType(type);
IsProperties data = settings.getData();
if (data != null) {
String dataString = null, contentType = null;
if (data.getDataImpl() instanceof JavaScriptObject && JsUtils.isFormData(data.<JavaScriptObject>getDataImpl())) {
dataString = null;
contentType = FormPanel.ENCODING_URLENCODED;
} else if (settings.getType().matches("(POST|PUT)") && "json".equalsIgnoreCase(settings.getDataType())) {
dataString = data.toJson();
contentType = JSON_CONTENT_TYPE_UTF8;
} else {
dataString = data.toQueryString();
contentType = FormPanel.ENCODING_URLENCODED;
}
settings.setDataString(dataString);
settings.setContentType(contentType);
}
if ("GET".equals(settings.getType()) && settings.getDataString() != null) {
url += (url.contains("?") ? "&" : "?") + settings.getDataString();
settings.setUrl(url);
}
}
public static Promise ajax(String url, Function onSuccess, Function onError) {
return ajax(url, onSuccess, onError, (Settings) null);
}
public static Promise ajax(String url, Function onSuccess, Function onError, Settings s) {
if (s == null) {
s = createSettings();
}
s.setUrl(url).setSuccess(onSuccess).setError(onError);
return ajax(s);
}
public static Promise ajax(String url, IsProperties p) {
Settings s = createSettings();
s.load(p);
s.setUrl(url);
return ajax(s);
}
public static Promise ajax(String url, Settings settings) {
return ajax(settings.setUrl(url));
}
public static Settings createSettings() {
return createSettings("");
}
public static Settings createSettings(String prop) {
Settings s = GQ.create(Settings.class);
if (prop != null && !prop.isEmpty())
s.parse(prop);
return s;
}
public static Settings createSettings(IsProperties p) {
Settings s = GQ.create(Settings.class);
s.load(p.getDataImpl());
return s;
}
public static Promise get(String url) {
return get(url, null);
}
public static Promise get(String url, IsProperties data) {
return get(url, (IsProperties)data, null);
}
public static Promise get(String url, IsProperties data, Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
s.setDataType("txt");
s.setType("get");
s.setData(data);
s.setSuccess(onSuccess);
return ajax(s);
}
public static Promise getJSON(String url, IsProperties data) {
return getJSON(url, data, null);
}
public static Promise getJSON(String url, IsProperties data, Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
s.setDataType("json");
s.setType("post");
s.setData(data);
s.setSuccess(onSuccess);
return ajax(s);
}
public static Promise getJSONP(String url) {
return getJSONP(url, null);
}
public static Promise getJSONP(String url, IsProperties data) {
return getJSONP(url, (IsProperties)data, null);
}
public static Promise getJSONP(String url, IsProperties data, Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
s.setDataType("jsonp");
s.setType("get");
s.setData(data);
s.setSuccess(onSuccess);
return ajax(s);
}
public static Promise getJSONP(String url, Function success, Function error, int timeout) {
return ajax(createSettings()
.setUrl(url)
.setDataType("jsonp")
.setType("get")
.setTimeout(timeout)
.setSuccess(success)
.setError(error)
);
}
/**
* Get a JavaScript file from the server using a GET HTTP request, then execute it.
*/
public static Promise getScript(String url) {
return getScript(url, null);
}
public static Promise getScript(final String url, Function success) {
return ajax(createSettings()
.setUrl(url)
.setType("get")
.setDataType("script")
.setSuccess(success)
);
}
/**
* Load a JavaScript file from any url using the script tag mechanism
*/
public static Promise loadScript(String url) {
return loadScript(url, null);
}
public static Promise loadScript(final String url, Function success) {
return ajax(createSettings()
.setUrl(url)
.setType("get")
.setDataType("loadscript")
.setSuccess(success)
);
}
public static Promise post(String url, IsProperties data) {
return post(url, (IsProperties)data, null);
}
public static Promise post(String url, IsProperties data, final Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
s.setDataType("txt");
s.setType("post");
s.setData(data);
s.setSuccess(onSuccess);
return ajax(s);
}
protected Ajax(GQuery gq) {
super(gq);
}
public Ajax load(String url, IsProperties data) {
return load(url, data);
}
public Ajax load(String url, IsProperties data, final Function onSuccess) {
Settings s = createSettings();
final String filter = url.contains(" ") ? url.replaceFirst("^[^\\s]+\\s+", "") : "";
s.setUrl(url.replaceAll("\\s+.+$", ""));
s.setDataType("html");
s.setType("get");
s.setData(data);
s.setSuccess(new Function() {
public void f() {
try {
// We clean up the returned string to smoothly append it to our document
// Note: using '\s\S' instead of '.' because gwt String emulation does
// not support java embedded flag expressions (?s) and javascript does
// not have multidot flag.
String s = arguments(0).toString().replaceAll("<![^>]+>\\s*", "")
.replaceAll("</?html[\\s\\S]*?>\\s*", "")
.replaceAll("<head[\\s\\S]*?</head>\\s*", "")
.replaceAll("<script[\\s\\S]*?</script>\\s*", "")
.replaceAll("</?body[\\s\\S]*?>\\s*", "");
// We wrap the results in a div
s = "<div>" + s + "</div>";
Ajax.this.empty().append(filter.isEmpty() ? $(s) : $(s).find(filter));
if (onSuccess != null) {
onSuccess.setElement(Ajax.this.get(0));
onSuccess.f();
}
} catch (Exception e) {
if (GWT.getUncaughtExceptionHandler() != null) {
GWT.getUncaughtExceptionHandler().onUncaughtException(e);
}
}
}
});
ajax(s);
return this;
}
}