aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--devtest/pom.xml29
-rw-r--r--devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml1
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java236
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java68
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/Promise.java98
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/Properties.java4
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsCache.java4
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Deferred.java272
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java92
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/callbacks/Callbacks.java38
-rw-r--r--gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryDeferredTestGwt.java81
-rw-r--r--gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java2
12 files changed, 797 insertions, 128 deletions
diff --git a/devtest/pom.xml b/devtest/pom.xml
index 7c32ffee..f966c5ca 100644
--- a/devtest/pom.xml
+++ b/devtest/pom.xml
@@ -31,6 +31,11 @@
<version>${gwtversion}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-codeserver</artifactId>
+ <version>${gwtversion}</version>
+ </dependency>
</dependencies>
<build>
<resources>
@@ -89,6 +94,30 @@
</additionalProjectnatures>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <mainClass>com.google.gwt.dev.codeserver.CodeServer</mainClass>
+ <arguments>
+ <argument>-src</argument>
+ <argument>src/main/java</argument>
+ <argument>-bindAddress</argument>
+ <argument>0.0.0.0</argument>
+ <argument>com.google.gwt.query.DevTestRunner</argument>
+ </arguments>
+ <classpathScope>compile</classpathScope>
+ </configuration>
+ </plugin>
+
</plugins>
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory>
</build>
diff --git a/devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml b/devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml
index 401fde05..b3ce809f 100644
--- a/devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml
+++ b/devtest/src/main/java/com/google/gwt/query/DevTestRunner.gwt.xml
@@ -1,5 +1,6 @@
<module rename-to="test">
<inherits name='com.google.gwt.query.Query'/>
<entry-point class='com.google.gwt.query.client.DevTestRunner'/>
+ <set-property name="compiler.useSourceMaps" value="false"/>
</module>
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java
index 361eea1f..8c170b8c 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java
@@ -29,69 +29,218 @@ public abstract class Function {
private com.google.gwt.dom.client.Element element = null;
private Event event = null;
private int index = -1;
- private Object[] data = new Object[0];
+ protected Object[] arguments = new Object[0];
public <T extends com.google.gwt.dom.client.Element> T getElement() {
return element.<T>cast();
}
- public <T extends com.google.gwt.dom.client.Element> void setElement(T e) {
+ public <T extends com.google.gwt.dom.client.Element> Function setElement(T e) {
element = e;
+ return this;
}
- public void setEvent(Event e) {
+ public Function setEvent(Event e) {
event = e;
element = e != null ? e.getCurrentEventTarget().<com.google.gwt.dom.client.Element>cast() : null;
+ return this;
}
public Event getEvent() {
return event;
}
- public void setIndex(int i) {
+ public Function setIndex(int i) {
index = i;
+ return this;
}
-
+
+ /**
+ * @deprecated use getArguments instead.
+ */
+ @Deprecated
public Object[] getData() {
- return data;
+ return getArguments();
}
- public void setData(Object...data) {
- this.data = data;
+ public Object[] getArguments() {
+ return arguments;
+ }
+
+ /**
+ * Set the list of arguments to be passed to the function
+ */
+ public Function setArguments(Object...arguments) {
+ this.arguments = arguments;
+ return this;
}
+ /**
+ * Return the first element of the arguments list
+ * @deprecated use getArgument(idx) instead.
+ */
+ @Deprecated
public Object getDataObject() {
- return getDataObject(0);
+ return getArgument(0);
}
+ /**
+ * @deprecated use getArgument instead
+ */
+ @Deprecated
public Object getDataObject(int idx) {
- return data != null && data.length > idx ? data[idx] : null;
+ return getArgument(0);
+ }
+
+ /**
+ * Utility method for safety getting a JavaScriptObject present at a certain
+ * position in the list of arguments composed by arrays.
+ *
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends JavaScriptObject> T getArgumentJSO(int argIdx, int pos) {
+ return (T)getArgument(argIdx, pos, JavaScriptObject.class);
+ }
+
+ /**
+ * Utility method for safety getting a JavaScriptObject present at a certain
+ * position in the list of arguments.
+ */
+ public <T extends JavaScriptObject> T getArgumentJSO(int idx) {
+ return getArgumentJSO(-1, idx);
+ }
+
+ /**
+ * Utility method for safety getting an array present at a certain
+ * position in the list of arguments.
+ *
+ * Useful for Deferred chains where result of each resolved
+ * promise is set as an array in the arguments list.
+ *
+ * Always returns an array.
+ */
+ public Object[] getArgumentArray(int idx) {
+ Object o = idx < 0 ? arguments: getArgument(idx);
+ if (o != null && o.getClass().isArray()) {
+ return (Object[])o;
+ } else if (idx == 0) {
+ return arguments;
+ }
+ return new Object[0];
+ }
+
+ /**
+ * Return the argument in the position idx or null if it doesn't exist.
+ *
+ * Note: if the return type doesn't match the object, you
+ * will get a casting exception.
+ */
+ public <T> T getArgument(int idx) {
+ return getArgument(-1, idx, null);
+ }
+
+ /**
+ * Safety return the argument in the position idx.
+ *
+ * If the element class is not of the requested type it returns null and
+ * you don't get casting exeption.
+ */
+ public <T> T getArgument(int argIdx, int pos) {
+ return getArgument(argIdx, pos, null);
+ }
+
+ /**
+ * Safety return the argument in the position idx.
+ *
+ * If the element class is not of the requested type it returns null and
+ * you don't get casting exeption.
+ */
+ public <T> T getArgument(int idx, Class<? extends T> type) {
+ return getArgument(-1, idx, type);
+ }
+
+ /**
+ * Utility method for safety getting an object present at a certain
+ * position in the list of arguments composed by arrays.
+ *
+ * Useful for Deferred chains where result of each resolved
+ * promise is set as an array in the arguments list.
+ *
+ * When the object found in the array doesn't match the type required it returns a null.
+ *
+ * Note: If type is null, we don't check the class of the object found andd you could
+ * eventually get a casting exception.
+ *
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T getArgument(int argIdx, int pos, Class<? extends T> type) {
+ Object[] objs = getArgumentArray(argIdx);
+ Object o = objs.length > pos ? objs[pos] : null;
+ if (o != null && (
+ // When type is null we don't safety check
+ type == null ||
+ // The object is an instance of the type requested
+ o.getClass() == type ||
+ // Overlay types
+ type == JavaScriptObject.class && o instanceof JavaScriptObject
+ )) {
+ return (T)o;
+ }
+ return null;
}
+
+ /**
+ * @deprecated use: getArgument()
+ */
+ @Deprecated
public Properties getDataProperties() {
return getDataProperties(0);
}
+ /**
+ * @deprecated use: getArgument(idx)
+ */
+ @Deprecated
public Properties getDataProperties(int idx) {
- Object o = getDataObject(idx);
- if (o != null && o instanceof JavaScriptObject) {
- return (Properties)o;
- }
- return null;
+ return getArgumentJSO(idx);
}
+ /**
+ * @deprecated use: setArguments()
+ */
+ @Deprecated
public void setData(boolean b) {
- setData(Boolean.valueOf(b));
+ setArguments(b);
}
+ /**
+ * @deprecated use: setArguments()
+ */
+ @Deprecated
public void setData(double b) {
- setData(Double.valueOf(b));
+ setArguments(b);
+ }
+
+ /**
+ * @deprecated use use setArguments instead
+ */
+ @Deprecated
+ public void setData(Object...arguments) {
+ setArguments(arguments);
}
- public void setDataObject(Object data) {
- setData(data);
+ /**
+ * @deprecated use: setArguments()
+ */
+ @Deprecated
+ public void setDataObject(Object arg) {
+ setArguments(arg);
}
+ /**
+ * Return the index in a loop execution. Used in GQuery.each()
+ */
public int getIndex() {
return index;
}
@@ -177,29 +326,31 @@ public abstract class Function {
/**
* Override this method for bound callbacks
*/
- public void f(Object... data) {
- fe(data);
+ public Object f(Object... args) {
+ setArguments(args);
+ f();
+ return true;
}
/**
* Override this method for bound callbacks
*/
- public void f(int i, Object data) {
- f(i, new Object[]{data});
+ public void f(int i, Object arg) {
+ f(i, new Object[]{arg});
}
/**
* Override this method for bound callbacks
*/
- public void f(int i, Object... data) {
+ public void f(int i, Object... args) {
setIndex(i);
- setData(data);
- if (data.length == 1 && data[0] instanceof JavaScriptObject) {
- if (JsUtils.isElement((JavaScriptObject)data[0])) {
- setElement((com.google.gwt.dom.client.Element)data[0]);
+ setArguments(args);
+ if (args.length == 1 && args[0] instanceof JavaScriptObject) {
+ if (JsUtils.isElement((JavaScriptObject)args[0])) {
+ setElement((com.google.gwt.dom.client.Element)args[0]);
f(getElement(), i);
- } else if (JsUtils.isEvent((JavaScriptObject)data[0])) {
- setEvent((Event)data[0]);
+ } else if (JsUtils.isEvent((JavaScriptObject)args[0])) {
+ setEvent((Event)args[0]);
f(getEvent());
} else {
f();
@@ -211,8 +362,8 @@ public abstract class Function {
* Override this method for bound event handlers if you wish to deal with
* per-handler user data.
*/
- public boolean f(Event e, Object data) {
- setData(data);
+ public boolean f(Event e, Object arg) {
+ setArguments(arg);
setEvent(e);
return f(e);
}
@@ -282,7 +433,7 @@ public abstract class Function {
* They are intentionally final to avoid override them
*/
public final void fe() {
- fe(data);
+ fe(arguments);
}
/**
@@ -290,8 +441,8 @@ public abstract class Function {
* catch the exception and send it to the GWT UncaughtExceptionHandler
* They are intentionally final to avoid override them
*/
- public final void fe(Object data) {
- fe(new Object[]{data});
+ public final void fe(Object arg) {
+ fe(new Object[]{arg});
}
/**
@@ -299,17 +450,16 @@ public abstract class Function {
* catch the exception and send it to the GWT UncaughtExceptionHandler
* They are intentionally final to avoid override them
*/
- public final void fe(Object... data) {
- setData(data);
+ public final Object fe(Object... args) {
if (GWT.getUncaughtExceptionHandler() != null) {
try {
- f();
+ return f(args);
} catch (Exception e) {
GWT.getUncaughtExceptionHandler().onUncaughtException(e);
}
- return;
+ return true;
}
- f();
+ return f(args);
}
/**
@@ -317,16 +467,16 @@ public abstract class Function {
* catch the exception and send it to the GWT UncaughtExceptionHandler
* They are intentionally final to avoid override them
*/
- public final boolean fe(Event ev, Object data) {
+ public final boolean fe(Event ev, Object arg) {
if (GWT.getUncaughtExceptionHandler() != null) {
try {
- return f(ev, data);
+ return f(ev, arg);
} catch (Exception e) {
GWT.getUncaughtExceptionHandler().onUncaughtException(e);
}
return true;
}
- return f(ev, data);
+ return f(ev, arg);
}
/**
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java
index d2d0e486..bdd1e799 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java
@@ -51,6 +51,7 @@ import com.google.gwt.query.client.js.JsNodeArray;
import com.google.gwt.query.client.js.JsObjectArray;
import com.google.gwt.query.client.js.JsRegexp;
import com.google.gwt.query.client.js.JsUtils;
+import com.google.gwt.query.client.plugins.Deferred;
import com.google.gwt.query.client.plugins.Effects;
import com.google.gwt.query.client.plugins.Events;
import com.google.gwt.query.client.plugins.Plugin;
@@ -241,7 +242,8 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
return JsUtils.isWindow(jso) ? $(jso.<Element> cast()) :
JsUtils.isElement(jso) ? $(jso.<Element> cast()) :
JsUtils.isEvent(jso) ? $(jso.<Event> cast()) :
- JsUtils.isNodeList(jso) ? $(jso.<NodeList<Element>> cast()) : $();
+ JsUtils.isNodeList(jso) ? $(jso.<NodeList<Element>> cast()) :
+ $(jso.<Element> cast());
}
/**
@@ -413,25 +415,24 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
/**
* Perform an ajax request to the server.
*/
- public static void ajax(Properties p) {
- Ajax.ajax(p);
+ public static Promise ajax(Properties p) {
+ return Ajax.ajax(p);
}
/**
* Perform an ajax request to the server.
*/
- public static void ajax(Settings settings) {
- Ajax.ajax(settings);
+ public static Promise ajax(Settings settings) {
+ return Ajax.ajax(settings);
}
/**
* Perform an ajax request to the server.
*/
- public static void ajax(String url, Settings settings) {
- Ajax.ajax(url, settings);
+ public static Promise ajax(String url, Settings settings) {
+ return Ajax.ajax(url, settings);
}
- @SuppressWarnings("unchecked")
protected static GQuery cleanHtmlString(String elem, Document doc) {
String tag = tagNameRegex.exec(elem).get(1);
@@ -541,8 +542,8 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
/**
* Perform an ajax request to the server using GET.
*/
- public static void get(String url, Properties data, final Function onSuccess) {
- Ajax.get(url, data, onSuccess);
+ public static Promise get(String url, Properties data, final Function onSuccess) {
+ return Ajax.get(url, data, onSuccess);
}
/**
@@ -585,8 +586,8 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
/**
* Perform an ajax request to the server using POST and parsing the json response.
*/
- public static void getJSON(String url, Properties data, final Function onSuccess) {
- Ajax.getJSON(url, data, onSuccess);
+ public static Promise getJSON(String url, Properties data, final Function onSuccess) {
+ return Ajax.getJSON(url, data, onSuccess);
}
/**
@@ -611,8 +612,8 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
</pre>
*
*/
- public static void getJSONP(String url, Properties data, final Function onSuccess) {
- Ajax.getJSONP(url, data, onSuccess);
+ public static Promise getJSONP(String url, Properties data, final Function onSuccess) {
+ return Ajax.getJSONP(url, data, onSuccess);
}
protected static DocumentStyleImpl getStyleImpl() {
@@ -695,8 +696,8 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
/**
* Perform an ajax request to the server using POST.
*/
- public static void post(String url, Properties data, final Function onSuccess) {
- Ajax.post(url, data, onSuccess);
+ public static Promise post(String url, Properties data, final Function onSuccess) {
+ return Ajax.post(url, data, onSuccess);
}
public static <T extends GQuery> Class<T> registerPlugin(Class<T> plugin, Plugin<T> pluginFactory) {
@@ -707,6 +708,28 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
plugins.put(plugin, pluginFactory);
return plugin;
}
+
+ /**
+ * Provides a way to execute callback Functions based on one or more Promise objects
+ * that represent asynchronous events.
+ *
+ * Returns a new promise which will be finalized when all of its subordinates finish.
+ * In the case of all subordinates are resolved correctly the promise will be resolved
+ * otherwise it will be rejected.
+ *
+ */
+ public static Promise when(Promise... subordinates) {
+ return Deferred.when(subordinates);
+ }
+
+ /**
+ * A constructor function that returns a chainable utility object with methods to register
+ * multiple callbacks into callback queues, invoke callback queues, and relay the success
+ * or failure state of any synchronous or asynchronous function
+ */
+ public static Promise.Deferred Deferred() {
+ return new Deferred();
+ }
private static native void scrollIntoViewImpl(Node n) /*-{
if (n)
@@ -745,9 +768,12 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
}
protected GQuery(GQuery gq) {
- this(gq == null ? null : gq.get());
- currentSelector = gq.getSelector();
- currentContext = gq.getContext();
+ if (gq != null) {
+ elements = gq.elements;
+ nodeList = gq.nodeList;
+ currentSelector = gq.currentSelector;
+ currentContext = gq.currentContext;
+ }
}
private GQuery(JsNodeArray nodes) {
@@ -3483,7 +3509,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
assert key != null : "Key is null";
return isEmpty() ? null : JsUtils.<T>prop(get(0), key, clz);
}
-
+
/**
* Sets a property to a value on all matched elements.
*
@@ -4561,7 +4587,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
public boolean visible() {
return isVisible();
}
-
+
/**
* Return the first non null attached widget from the matched elements or null if there isn't any.
*/
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/Promise.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/Promise.java
new file mode 100644
index 00000000..7d14b439
--- /dev/null
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/Promise.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2013, The gwtquery team.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.query.client;
+
+import com.google.gwt.query.client.Function;
+
+/**
+ * Definition of jquery Promise interface used in gquery.
+ */
+public interface Promise {
+
+ /**
+ * Definition of jquery Deferred interface used in gquery.
+ */
+ public interface Deferred {
+ /**
+ * Call the progressCallbacks on a Deferred object with the given args.
+ */
+ Deferred notify(Object... o);
+
+ /**
+ * Return a Deferred’s Promise object.
+ */
+ Promise promise();
+
+ /**
+ * Reject a Deferred object and call any failCallbacks with the given args.
+ */
+ Deferred reject(Object... o);
+
+ /**
+ * Resolve a Deferred object and call any doneCallbacks with the given args.
+ */
+ Deferred resolve(Object... o);
+ }
+
+ public static final String PENDING = "pending";
+ public static final String REJECTED = "rejected";
+ public static final String RESOLVED = "resolved";
+
+ /**
+ * Add handlers to be called when the Deferred object is either resolved or rejected.
+ */
+ Promise always(Function... o);
+
+ /**
+ * Add handlers to be called when the Deferred object is resolved.
+ */
+ Promise done(Function... o);
+
+ /**
+ * Add handlers to be called when the Deferred object is rejected.
+ */
+ Promise fail(Function... o);
+
+ /**
+ * Utility method to filter and/or chain Deferreds.
+ *
+ * @deprecated use 'then' instead.
+ * it was deprecated in jquery, and we maintain it here for compatibility.
+ */
+ @Deprecated
+ Promise pipe(Function... f);
+
+ /**
+ * Utility method to filter and/or chain Deferreds.
+ */
+ Promise progress(Function... o);
+
+ /**
+ * Return the status of the deferred object.
+ *
+ * Valid values are: Promise.PENDING, Promise.REJECTED, Promise.RESOLVED
+ *
+ */
+ String state();
+
+ /**
+ * Add handlers to be called when the Deferred object is resolved, rejected, or still in progress.
+ *
+ * @param f a list of 1, 2, or 3 functions, which will be used in this way:
+ * 1st function will be called when the deferred is resolved.
+ * 2nd function that is called when the deferred is rejected.
+ * 3rd one will be called when progress notifications are sent.
+ */
+ Promise then(Function... f);
+}
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/Properties.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/Properties.java
index 7999e10d..d3dbda9b 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/Properties.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/Properties.java
@@ -132,6 +132,10 @@ public class Properties extends JavaScriptObject {
public final Object getObject(Object name) {
return c().get(String.valueOf(name));
}
+
+ public final Properties getProperties(Object name) {
+ return getJavaScriptObject(name);
+ }
@SuppressWarnings("unchecked")
public final <T extends JavaScriptObject> T getJavaScriptObject(Object name) {
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsCache.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsCache.java
index 5d564cc8..068bafbf 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsCache.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsCache.java
@@ -116,7 +116,11 @@ public class JsCache extends JavaScriptObject {
public final native JsArrayMixed getArray(Object id) /*-{
var r = this[id];
+<<<<<<< HEAD
+ if (r && Object.prototype.toString.call(r) == '[object Array]') {
+=======
if (Object.prototype.toString.call(r) == '[object Array]') {
+>>>>>>> bb71ac03afde8f01e14270b02438bf9fde997a71
return r;
}
return null;
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Deferred.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Deferred.java
new file mode 100644
index 00000000..2c60b104
--- /dev/null
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Deferred.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2013, The gwtquery team.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.query.client.plugins;
+
+import static com.google.gwt.query.client.Promise.PENDING;
+import static com.google.gwt.query.client.Promise.REJECTED;
+import static com.google.gwt.query.client.Promise.RESOLVED;
+
+import com.google.gwt.query.client.Function;
+import com.google.gwt.query.client.GQuery;
+import com.google.gwt.query.client.Promise;
+import com.google.gwt.query.client.plugins.callbacks.Callbacks;
+
+/**
+ * Implementation of jQuery.Deferred for gwtquery.
+ */
+public class Deferred extends GQuery implements Promise.Deferred {
+
+ /**
+ * public class used to create customized promises which can manipulate
+ * the asociated deferred object.
+ * <pre>
+ * Promise doSomething = new PromiseFunction() {
+ * @Override
+ * public void f(Deferred dfd) {
+ * dfd.notify("hi");
+ * dfd.resolve("done");
+ * }
+ * };
+ *
+ * doSomething.progress(new Function() {
+ * public void f() {
+ * Window.alert("" + arguments[0]);
+ * }
+ * }).done(new Function() {
+ * public void f() {
+ * Window.alert("" + arguments[0]);
+ * }
+ * });
+ * </pre>
+ */
+ public static abstract class PromiseFunction extends DeferredPromiseImpl {
+ public PromiseFunction() {
+ f(dfd);
+ }
+
+ /**
+ * This function is called once when the promise is created and the
+ * new deferred is available.
+ */
+ public abstract void f(Deferred dfd);
+ }
+
+ /**
+ * Implementation of the Promise interface which is used internally by Deferred.
+ */
+ private static class DeferredPromiseImpl implements Promise {
+ protected com.google.gwt.query.client.plugins.Deferred dfd;
+
+ protected DeferredPromiseImpl(com.google.gwt.query.client.plugins.Deferred o) {
+ dfd = o;
+ }
+
+ protected DeferredPromiseImpl(DeferredPromiseImpl o) {
+ dfd = o.dfd;
+ }
+
+ protected DeferredPromiseImpl() {
+ dfd = new com.google.gwt.query.client.plugins.Deferred();
+ }
+
+ public Promise always(Function... f) {
+ return done(f).fail(f);
+ }
+
+ public Promise done(Function... f) {
+ dfd.resolve.add(f);
+ return this;
+ }
+
+ public Promise fail(Function... f) {
+ dfd.reject.add(f);
+ return this;
+ }
+
+ public Promise pipe(Function... f) {
+ return then(f);
+ }
+
+ public Promise progress(Function... f) {
+ dfd.notify.add(f);
+ return this;
+ }
+
+ public String state() {
+ return dfd.state;
+ }
+
+ public Promise then(Function... f) {
+ assert f.length < 4 : "Promise.then: Too much arguments";
+ switch (f.length) {
+ case 3: progress(f[2]);
+ case 2: fail(f[1]);
+ case 1: done(f[0]);
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Internal Deferred class used to combine a set of subordinate promises.
+ */
+ private static class WhenDeferredImpl extends Deferred {
+ /**
+ * Internal function used to track whether all deferred
+ * subordinates are resolved.
+ */
+ private class DoneFnc extends Function {
+ final int idx;
+
+ public DoneFnc(int i, Deferred d) {
+ idx = i;
+ }
+
+ public Object f(Object... args) {
+ values[idx] = args;
+ if (--remaining == 0) {
+ WhenDeferredImpl.this.resolve(values);
+ }
+ return true;
+ }
+ }
+
+ private Function failFnc = new Function() {
+ public Object f(Object... o) {
+ WhenDeferredImpl.this.reject(o);
+ return true;
+ }
+ };
+
+ private Function progressFnc = new Function() {
+ public Object f(Object... o) {
+ WhenDeferredImpl.this.notify(o);
+ return true;
+ }
+ };
+
+ // Remaining counter
+ private int remaining;
+
+ // An indexed array with the fired values of all subordinated
+ private final Object[] values;
+
+ public WhenDeferredImpl(Promise[] sub) {
+ int l = remaining = sub.length;
+ values = new Object[l];
+ for (int i = 0; i < l; i++) {
+ sub[i].done(new DoneFnc(i, this)).progress(progressFnc).fail(failFnc);
+ }
+ }
+ }
+
+ // Register Deferred as a GQuery plugin
+ public static final Class<Deferred> Deferred = GQuery.registerPlugin(
+ Deferred.class, new Plugin<Deferred>() {
+ public Deferred init(GQuery gq) {
+ return new Deferred(gq);
+ }
+ });
+
+ public static Promise when(Promise... d) {
+ final int n = d.length;
+ switch (n) {
+ case 1:
+ return d[0];
+ case 0:
+ return new Deferred().resolve().promise();
+ default:
+ return new WhenDeferredImpl(d).promise();
+ }
+ }
+
+ private Callbacks notify = new Callbacks("memory");
+
+ private Promise promise = null;
+
+ private Callbacks reject = new Callbacks("once memory");
+
+ private Callbacks resolve = new Callbacks("once memory");
+
+ private String state = PENDING;
+
+ public Deferred() {
+ this(null);
+ }
+
+ protected Deferred(GQuery gq) {
+ super(gq);
+ resolve.add(new Function() {
+ public void f() {
+ state = RESOLVED;
+ resolve.disable();
+ notify.lock();
+ }
+ });
+
+ reject.add(new Function() {
+ public void f() {
+ state = REJECTED;
+ reject.disable();
+ notify.lock();
+ }
+ });
+ }
+
+ /**
+ * Call the progressCallbacks on a Deferred object with the given args.
+ */
+ public Deferred notify(Object... o) {
+ notify.fire(o);
+ return this;
+ }
+
+ /**
+ * Return a Deferred’s Promise object.
+ */
+ public Promise promise() {
+ if (promise == null) {
+ promise = new DeferredPromiseImpl(this);
+ }
+ return promise;
+ }
+
+ /**
+ * Reject a Deferred object and call any failCallbacks with the given args.
+ */
+ public Deferred reject(Object... o) {
+ reject.fire(o);
+ return this;
+ }
+
+ /**
+ * Resolve a Deferred object and call any doneCallbacks with the given args.
+ */
+ public Deferred resolve(Object... o) {
+ resolve.fire(o);
+ return this;
+ }
+
+ // private, used from jsni because it does not handles variable arguments
+ @SuppressWarnings("unused")
+ private void err(Object o) {
+ reject(o);
+ }
+
+ // private, used from jsni because it does not handles variable arguments
+ @SuppressWarnings("unused")
+ private void ok(Object o) {
+ resolve(o);
+ }
+}
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java
index 96a3d35b..86574b9a 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java
@@ -10,9 +10,11 @@ import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
+import com.google.gwt.query.client.Promise;
import com.google.gwt.query.client.Properties;
import com.google.gwt.query.client.builders.JsonBuilder;
import com.google.gwt.query.client.js.JsUtils;
+import com.google.gwt.query.client.plugins.Deferred;
import com.google.gwt.query.client.plugins.Plugin;
import com.google.gwt.user.client.ui.FormPanel;
@@ -31,7 +33,7 @@ import com.google.gwt.user.client.ui.FormPanel;
*
*/
public class Ajax extends GQuery {
-
+
/**
* Ajax Settings object
*/
@@ -70,10 +72,10 @@ public class Ajax extends GQuery {
}
});
- public static void ajax(Properties p) {
+ public static Promise ajax(Properties p) {
Settings s = createSettings();
s.load(p);
- ajax(s);
+ return ajax(s);
}
/**
@@ -103,16 +105,19 @@ public class Ajax extends GQuery {
* @param onError the function to execute on error
* @param settings a Properties object with the configuration of the Ajax request.
*/
- public static void ajax(Settings settings) {
+ public static Promise ajax(Settings settings) {
+ final Deferred dfd = $().as(Deferred.Deferred);
final Function onSuccess = settings.getSuccess();
if (onSuccess != null) {
onSuccess.setElement(settings.getContext());
+ dfd.promise().done(onSuccess);
}
final Function onError = settings.getError();
if (onError != null) {
onError.setElement(settings.getContext());
+ dfd.promise().fail(onError);
}
Method httpMethod = resolveHttpMethod(settings);
@@ -122,18 +127,15 @@ public class Ajax extends GQuery {
if ("jsonp".equalsIgnoreCase(dataType)) {
int timeout = settings.getTimeout();
- getJSONP(url, onSuccess, onError, timeout);
- return;
+ return getJSONP(url, onSuccess, onError, timeout);
}
final RequestBuilder requestBuilder = createRequestBuilder(settings, httpMethod, url, data);
requestBuilder.setCallback(new RequestCallback() {
public void onError(Request request, Throwable exception) {
- if (onError != null) {
- onError.f(null, exception.getMessage(), request, null, exception);
- }
+ dfd.reject(null, exception.getMessage(), request, null, exception);
}
-
+
public void onResponseReceived(Request request, Response response) {
int statusCode = response.getStatusCode();
if (statusCode <= 0 || statusCode >= 400) {
@@ -141,9 +143,7 @@ public class Ajax extends GQuery {
// Just warn the developer about the status code
GWT.log("GQuery.ajax error, the response.statusCode is 0, this usually happens when you try to access an external server without CORS enabled. url=" + url);
}
- if (onError != null) {
- onError.fe(response.getText(), "error", request, response);
- }
+ dfd.reject(response.getText(), "error", request, response);
} else if (onSuccess != null) {
Object retData = null;
try {
@@ -159,7 +159,7 @@ public class Ajax extends GQuery {
GWT.getUncaughtExceptionHandler().onUncaughtException(e);
}
}
- onSuccess.fe(retData, "success", request, response);
+ dfd.resolve(retData, "success", request, response);
}
}
});
@@ -167,12 +167,12 @@ public class Ajax extends GQuery {
try {
requestBuilder.send();
} catch (RequestException e) {
- if (onError != null) {
- onError.f(null, -1, null, null, e);
- }
+ dfd.reject(null, e.getMessage(), null, null, e);
}
- }
+ return dfd.promise();
+ }
+
private static RequestBuilder createRequestBuilder(Settings settings, Method httpMethod, String url, String data) {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, url);
@@ -255,27 +255,27 @@ public class Ajax extends GQuery {
return RequestBuilder.POST;
}
- public static void ajax(String url, Function onSuccess, Function onError) {
- ajax(url, onSuccess, onError, (Settings) null);
+ public static Promise ajax(String url, Function onSuccess, Function onError) {
+ return ajax(url, onSuccess, onError, (Settings) null);
}
- public static void ajax(String url, Function onSuccess, Function onError, Settings s) {
+ public static Promise ajax(String url, Function onSuccess, Function onError, Settings s) {
if (s == null) {
s = createSettings();
}
s.setUrl(url).setSuccess(onSuccess).setError(onError);
- ajax(s);
+ return ajax(s);
}
- public static void ajax(String url, Properties p) {
+ public static Promise ajax(String url, Properties p) {
Settings s = createSettings();
s.load(p);
s.setUrl(url);
- ajax(s);
+ return ajax(s);
}
- public static void ajax(String url, Settings settings) {
- ajax(settings.setUrl(url));
+ public static Promise ajax(String url, Settings settings) {
+ return ajax(settings.setUrl(url));
}
public static Settings createSettings() {
@@ -292,37 +292,41 @@ public class Ajax extends GQuery {
return s;
}
- public static void get(String url, Properties data, final Function onSuccess) {
+ public static Promise get(String url, Properties data, Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
s.setDataType("txt");
s.setType("get");
s.setData(data);
s.setSuccess(onSuccess);
- ajax(s);
+ return ajax(s);
}
- public static void getJSON(String url, Properties data, final Function onSuccess) {
+ public static Promise getJSON(String url, Properties data, Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
s.setDataType("json");
s.setType("post");
s.setData(data);
s.setSuccess(onSuccess);
- ajax(s);
+ return ajax(s);
}
-
- public static void getJSONP(String url, Properties data, Function onSuccess) {
+
+ public static Promise getJSONP(String url) {
+ return getJSONP(url, null, null, 0);
+ }
+
+ public static Promise getJSONP(String url, Properties data, Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
s.setDataType("jsonp");
s.setType("get");
s.setData(data);
s.setSuccess(onSuccess);
- ajax(s);
+ return ajax(s);
}
- public static void getJSONP(String url, Function success, Function error, int timeout) {
+ public static Promise getJSONP(String url, Function success, Function error, int timeout) {
if (!url.contains("=?") && !url.contains("callback=")) {
url += (url.contains("?") ? "&" : "?") + "callback=?";
}
@@ -331,17 +335,20 @@ public class Ajax extends GQuery {
if (e == null) {
e = document.getDocumentElement();
}
- getJsonpImpl(e, url, null, success, error == null ? success : error, timeout);
+ Deferred dfd = $().as(Deferred.Deferred);
+ dfd.promise().done(success).fail(error == null ? success : error);
+ getJsonpImpl(e, url, null, dfd, timeout);
+ return dfd.promise();
}
- public static void post(String url, Properties data, final Function onSuccess) {
+ public static Promise post(String url, Properties data, final Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
s.setDataType("txt");
s.setType("post");
s.setData(data);
s.setSuccess(onSuccess);
- ajax(s);
+ return ajax(s);
}
protected Ajax(GQuery gq) {
@@ -362,7 +369,7 @@ public class Ajax extends GQuery {
// 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 = getData()[0].toString().replaceAll("<![^>]+>\\s*", "")
+ String s = getArguments()[0].toString().replaceAll("<![^>]+>\\s*", "")
.replaceAll("</?html[\\s\\S]*?>\\s*", "")
.replaceAll("<head[\\s\\S]*?</head>\\s*", "")
.replaceAll("<script[\\s\\S]*?</script>\\s*", "")
@@ -388,22 +395,21 @@ public class Ajax extends GQuery {
private static int callBackCounter = 0;
- public static native void getJsonpImpl(Element elem, String url, String charset, Function success, Function error, int timeout) /*-{
+ private static native void getJsonpImpl(Element elem, String url, String charset, Deferred dfd, int timeout) /*-{
var fName = "__GQ_cb_" + @com.google.gwt.query.client.plugins.ajax.Ajax::callBackCounter ++;
var done = false;
$wnd[fName] = function(data) {
if (!done) {
done = true;
$wnd[fName] = null;
- success.@com.google.gwt.query.client.Function::fe(Ljava/lang/Object;)(data);
+ dfd.@com.google.gwt.query.client.plugins.Deferred::ok(*)(data)
}
}
function err() {
if (!done) {
done = true;
$wnd[fName] = null;
- var func = error ? error : success;
- func.@com.google.gwt.query.client.Function::fe(Ljava/lang/Object;)();
+ dfd.@com.google.gwt.query.client.plugins.Deferred::err(*)(null)
}
}
if (timeout) {
@@ -411,7 +417,7 @@ public class Ajax extends GQuery {
}
url = url.replace(/=\?/g,'=' + fName);
- var script = document.createElement("script" );
+ var script = document.createElement("script");
script.async = "async";
if (charset) script.charset = charset;
script.src = url;
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/callbacks/Callbacks.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/callbacks/Callbacks.java
index 04db71fd..9452d117 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/callbacks/Callbacks.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/callbacks/Callbacks.java
@@ -56,7 +56,7 @@ public class Callbacks {
return GWT.create(CallbackOptions.class);
}
- private JsObjectArray<Object> callbacks = JsObjectArray.create();
+ private JsObjectArray<Object> stack = JsObjectArray.create();
private boolean done = false;
@@ -119,10 +119,21 @@ public class Callbacks {
* Disable a callback list from doing anything more.
*/
public Callbacks disable() {
- callbacks = memory = null;
+ stack = memory = null;
done = true;
return this;
}
+
+ /**
+ * lock
+ */
+ public Callbacks lock() {
+ if (!opts.getMemory()) {
+ disable();
+ }
+ stack = null;
+ return this;
+ }
/**
* Call all of the callbacks with the given arguments.
@@ -130,7 +141,7 @@ public class Callbacks {
public Callbacks fire(Object... o) {
if (!done) {
done = opts.getOnce();
- for (Object c : callbacks.elements()) {
+ if (stack != null) for (Object c : stack.elements()) {
if (!run(c, o) && opts.getStopOnFalse()) {
break;
}
@@ -146,20 +157,18 @@ public class Callbacks {
* Remove a callback or a collection of callbacks from a callback list.
*/
public Callbacks remove(Object... o) {
- callbacks.remove(o);
+ stack.remove(o);
return this;
}
private void addAll(Object...o) {
- if (callbacks != null) {
- for (Object c : o) {
- if (!opts.getUnique() || !callbacks.contains(c)) {
- callbacks.add(c);
- }
- // In jQuery add always is run when memory is true even when unique is set
- if (opts.getMemory() && memory != null) {
- run(c, memory.elements());
- }
+ for (Object c : o) {
+ if (!done && (!opts.getUnique() || !stack.contains(c))) {
+ stack.add(c);
+ }
+ // In jQuery add always is run when memory is true even when unique is set
+ if (opts.getMemory() && memory != null) {
+ run(c, memory.elements());
}
}
}
@@ -169,7 +178,8 @@ public class Callbacks {
if (c instanceof Callback) {
return ((Callback)c).f(o);
} else if (c instanceof Function) {
- ((Function)c).f(o);
+ Object r = ((Function)c).f(o);
+ return (r instanceof Boolean) ? (Boolean)r : true;
} else if (c instanceof com.google.gwt.core.client.Callback) {
((com.google.gwt.core.client.Callback)c).onSuccess(o);
}
diff --git a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryDeferredTestGwt.java b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryDeferredTestGwt.java
index 9bcc4884..d685c1ca 100644
--- a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryDeferredTestGwt.java
+++ b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryDeferredTestGwt.java
@@ -17,8 +17,11 @@ package com.google.gwt.query.client;
import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.query.client.plugins.Deferred.PromiseFunction;
+import com.google.gwt.query.client.plugins.ajax.Ajax;
import com.google.gwt.query.client.plugins.callbacks.Callbacks;
import com.google.gwt.query.client.plugins.callbacks.Callbacks.Callback;
+import com.google.gwt.user.client.Timer;
/**
* Test class for testing deferred and callbacks stuff.
@@ -33,12 +36,13 @@ public class GQueryDeferredTestGwt extends GWTTestCase {
public void testCallbacks() {
Function fn1 = new Function() {
- public void f() {
+ public Object f(Object...arguments) {
String s = " f1:";
- for (Object o: getData()){
+ for (Object o: arguments){
s += " " + o;
}
result += s;
+ return false;
}
};
@@ -55,8 +59,7 @@ public class GQueryDeferredTestGwt extends GWTTestCase {
com.google.gwt.core.client.Callback<Object, Object> fn3 = new com.google.gwt.core.client.Callback<Object, Object>() {
public void onFailure(Object reason) {
- String s = " f3_fail: " + reason;
- System.out.println(s);
+ result += " f3_fail: " + reason;
}
public void onSuccess(Object objects) {
String s = " f3_success:";
@@ -112,10 +115,10 @@ public class GQueryDeferredTestGwt extends GWTTestCase {
result = "";
callbacks = new Callbacks("stopOnFalse");
- callbacks.add( fn2 );
callbacks.add( fn1 );
+ callbacks.add( fn2 );
callbacks.fire( "bar" );
- assertEquals(" f2: bar", result);
+ assertEquals(" f1: bar", result);
result = "";
callbacks.disable();
@@ -141,5 +144,71 @@ public class GQueryDeferredTestGwt extends GWTTestCase {
callbacks.add( fn1 );
assertEquals(" f1: bar f2: bar f2: bar f1: bar", result);
}
+
+ public void testDeferredAjaxWhenDone() {
+ String url = "https://www.googleapis.com/blogger/v2/blogs/user_id/posts/post_id?callback=?&key=NO-KEY";
+
+ delayTestFinish(5000);
+ GQuery.when(Ajax.getJSONP(url, null, null, 1000))
+ .done(new Function() {
+ public void f() {
+ Properties p = getArgument(0, 0);
+ assertEquals(400, p.getProperties("error").getInt("code"));
+ finishTest();
+ }
+ });
+ }
+
+ public void testDeferredAjaxWhenFail() {
+ String url1 = "https://www.googleapis.com/blogger/v2/blogs/user_id/posts/post_id?callback=?&key=NO-KEY";
+ String url2 = "https://localhost:4569/foo";
+
+ delayTestFinish(5000);
+ GQuery.when(
+ Ajax.getJSONP(url1),
+ Ajax.getJSONP(url2, null, null, 1000))
+ .done(new Function() {
+ public void f() {
+ fail();
+ }
+ })
+ .fail(new Function(){
+ public void f() {
+ finishTest();
+ }
+ });
+ }
+
+ int progress = 0;
+ public void testPromiseFunction() {
+ delayTestFinish(3000);
+ final Promise doSomething = new PromiseFunction() {
+ @Override
+ public void f(final Deferred dfd) {
+ new Timer() {
+ int count = 0;
+ public void run() {
+ dfd.notify(count ++);
+ if (count > 3) {
+ cancel();
+ dfd.resolve("done");
+ }
+ }
+ }.scheduleRepeating(50);
+ }
+ };
+
+ doSomething.progress(new Function() {
+ public void f() {
+ progress = getArgument(0);
+ }
+ }).done(new Function() {
+ public void f() {
+ assertEquals(3, progress);
+ assertEquals(Promise.RESOLVED, doSomething.state());
+ finishTest();
+ }
+ });
+ }
}
diff --git a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java
index c833c130..768a376d 100644
--- a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java
+++ b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java
@@ -953,7 +953,7 @@ public class GQueryEventsTestGwt extends GWTTestCase {
public void f(Element elem) {
$(elem).css(CSS.FONT_SIZE.with(Length.px(24)));
- assertEquals("red", getData()[0]);
+ assertEquals("red", getArgument(0));
}
});
$("p", e, Events.Events).trigger(Event.ONCLICK);