diff options
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); |