From d7a1782e837ee89af7c3d1855b07cf55b9272df3 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Mon, 4 Apr 2011 12:20:03 +0000 Subject: [PATCH] Implement live and die methods, fixes issue#69. Little changes in .js package --- .../com/google/gwt/query/client/GQuery.java | 44 ++++++++---- .../google/gwt/query/client/LazyGQuery.java | 15 +++- .../google/gwt/query/client/Properties.java | 14 +++- .../google/gwt/query/client/js/JsCache.java | 58 ++++++++++++++- .../com/google/gwt/query/client/js/JsMap.java | 5 ++ .../gwt/query/client/js/JsObjectArray.java | 10 ++- .../gwt/query/client/plugins/Events.java | 71 ++++++++++++++++++- .../gwt/query/client/plugins/LazyEvents.java | 15 ++++ .../plugins/effects/PropertiesAnimation.java | 2 +- .../gwt/query/client/GQueryCoreTest.java | 6 +- .../gwt/query/client/GQueryEventsTest.java | 26 ++++++- 11 files changed, 240 insertions(+), 26 deletions(-) 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 f26e70e3..cfbc6be0 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 @@ -107,7 +107,7 @@ public class GQuery implements Lazy { */ public static final Element window = window(); - private static JsCache dataCache = null; + protected static JsCache dataCache = null; private static SelectorEngine engine; @@ -115,7 +115,7 @@ public class GQuery implements Lazy { FUNC_BEFORE = 3; private static final String OLD_DATA_PREFIX = "old-"; - + private static JsMap, Plugin> plugins; private static DocumentStyleImpl styleImpl = GWT.create(DocumentStyleImpl.class); @@ -216,7 +216,7 @@ public class GQuery implements Lazy { : ctx.getOwnerDocument(); return $(cleanHtmlString(selectorOrHtml, doc)); } - return new GQuery(select(selectorOrHtml, ctx)); + return new GQuery(select(selectorOrHtml, ctx)).setSelector(selectorOrHtml); } /** @@ -231,7 +231,7 @@ public class GQuery implements Lazy { try { if (plugins != null) { T gquery = (T) plugins.get(plugin).init( - new GQuery(select(selector, context))); + new GQuery(select(selector, context))).setSelector(selector); return gquery; } throw new RuntimeException("No plugin for class " + plugin); @@ -348,7 +348,7 @@ public class GQuery implements Lazy { n = n.getLastChild(); } // TODO: add fixes for IE TBODY issue - return $((NodeList) n.getChildNodes().cast()); + return $((NodeList) n.getChildNodes().cast()).as(Events).addLiveEvents(); } protected static Object data(Element item, String name, S value) { @@ -463,7 +463,7 @@ public class GQuery implements Lazy { return $wnd; }-*/; - private String currentSelector; + protected String currentSelector; private NodeList elements = JavaScriptObject.createArray().cast(); @@ -772,7 +772,7 @@ public class GQuery implements Lazy { } else if (plugins != null) { Plugin p = plugins.get(plugin); if (p != null) { - return (T) p.init(this); + return (T) p.init(this).setSelector(currentSelector); } } throw new RuntimeException("No plugin registered for class " @@ -788,7 +788,7 @@ public class GQuery implements Lazy { public GQuery attr(Properties properties) { for (Element e : elements()) { for (String name : properties.keys()) { - e.setAttribute(name, properties.get(name)); + e.setAttribute(name, properties.getStr(name)); } } return this; @@ -957,7 +957,7 @@ public class GQuery implements Lazy { for (Element e : elements()) { result.addNode(e.cloneNode(true)); } - return new GQuery(result); + return new GQuery(result).as(Events).addLiveEvents(); } /** @@ -1067,7 +1067,7 @@ public class GQuery implements Lazy { */ public GQuery css(Properties properties) { for (String property : properties.keys()) { - css(property, properties.get(property)); + css(property, properties.getStr(property)); } return this; } @@ -1283,6 +1283,15 @@ public class GQuery implements Lazy { return remove(filter, false); } + /** + * Remove all event handlers previously attached using live() + * The selector used with it must match exactly the selector initially + * used with live(). + */ + public GQuery die(int eventbits) { + return as(Events).die(eventbits); + } + /** * Run one or more Functions over each element of the GQuery. You have to * override one of these funcions: public void f(Element e) public String @@ -1626,7 +1635,7 @@ public class GQuery implements Lazy { } e.setInnerHTML(html); } - return this; + return as(Events).addLiveEvents(); } /** @@ -1795,6 +1804,14 @@ public class GQuery implements Lazy { public int length() { return size(); } + + /** + * Add events to all elements which match the current selector, + * now and in the future. + */ + public GQuery live(int eventBits, Function... funcs) { + return as(Events).live(eventBits, funcs); + } /** * Bind a function to the load event of each matched element. @@ -1818,6 +1835,7 @@ public class GQuery implements Lazy { */ @SuppressWarnings("unchecked") public List map(Function f) { + @SuppressWarnings("rawtypes") ArrayList ret = new ArrayList(); for (int i = 0; i < elements().length; i++) { Object o = f.f(elements()[i], i); @@ -2594,8 +2612,9 @@ public class GQuery implements Lazy { this.previousObject = previousObject; } - public void setSelector(String selector) { + public GQuery setSelector(String selector) { this.currentSelector = selector; + return this; } /** @@ -3398,4 +3417,5 @@ public class GQuery implements Lazy { dataCache.delete(id); } } + } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java index b9417e28..402d782f 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java @@ -621,6 +621,13 @@ public interface LazyGQuery extends LazyBase{ */ LazyGQuery detach(String filter); + /** + * Remove all event handlers previously attached using live() + * The selector used with it must match exactly the selector initially + * used with live(). + */ + LazyGQuery die(int eventbits); + /** * Run one or more Functions over each element of the GQuery. You have to * override one of these funcions: public void f(Element e) public String @@ -938,6 +945,12 @@ public interface LazyGQuery extends LazyBase{ */ int length(); + /** + * Add events to all elements which match the current selector, + * now and in the future. + */ + LazyGQuery live(int eventBits, Function... funcs); + /** * Bind a function to the load event of each matched element. */ @@ -1387,7 +1400,7 @@ public interface LazyGQuery extends LazyBase{ void setPreviousObject(GQuery previousObject); - void setSelector(String selector); + LazyGQuery setSelector(String selector); /** * Make all matched elements visible 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 9c5f3a90..c95d4005 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 @@ -34,6 +34,10 @@ public class Properties extends JavaScriptObject { return (Properties) createImpl("({})"); } } + + public static Properties create() { + return (Properties) createImpl("({})"); + } public static final native JavaScriptObject createImpl(String properties) /*-{ return eval(properties); @@ -66,10 +70,14 @@ public class Properties extends JavaScriptObject { return this[name] != undefined; }-*/; - public final native String get(String name) /*-{ + public final native T get(String name) /*-{ return this[name]; }-*/; + public final native String getStr(String name) /*-{ + return String(this[name]); + }-*/; + public final native float getFloat(String name) /*-{ return this[name]; }-*/; @@ -99,14 +107,14 @@ public class Properties extends JavaScriptObject { return keys; }-*/; - public final native void set(String key, String val) /*-{ + public final native void set(String key, Object val) /*-{ this[key]=val; }-*/; public final String tostring() { String ret = ""; for (String k : keys()){ - ret += k + ": '" + get(k) + "', "; + ret += k + ": '" + getStr(k) + "', "; } return "({" + ret.replaceAll("[, ]+$","") + "})"; } 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 1848f349..a805bc6c 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 @@ -1,6 +1,7 @@ package com.google.gwt.query.client.js; import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.JsArrayString; /** * A Lightweight JSO class to store data. @@ -9,6 +10,10 @@ public class JsCache extends JavaScriptObject { protected JsCache() { } + + public static JsCache create() { + return createObject().cast(); + } public final native void concat(Object ary) /*-{ if (ary) this.concat(ary); @@ -29,15 +34,19 @@ public class JsCache extends JavaScriptObject { delete this[name]; }-*/; + public final native boolean exists(String name) /*-{ + return !!this[name]; + }-*/; + public final native boolean exists(int id) /*-{ return !!this[id]; }-*/; - public final native Object get(int id) /*-{ + public final native T get(int id) /*-{ return this[id] || null; }-*/; - public final native Object get(String id) /*-{ + public final native T get(String id) /*-{ return this[id] || null; }-*/; @@ -87,4 +96,49 @@ public class JsCache extends JavaScriptObject { public final native int length() /*-{ return this.length; }-*/; + + public final int[] indexes() { + JsArrayString a = keysImpl(); + int[] ret = new int[a.length()]; + for (int i = 0; i < a.length(); i++) { + try { + ret[i] = Integer.valueOf(a.get(i)); + } catch (Exception e) { + ret[i] = i; + } + } + return ret; + } + + public final String[] keys() { + JsArrayString a = keysImpl(); + String[] ret = new String[a.length()]; + for (int i = 0; i < a.length(); i++) { + ret[i] = a.get(i); + } + return ret; + } + + public final Object[] elements() { + String[] keys = keys(); + Object[] ret = new Object[keys.length]; + int i=0; + for (String s: keys) { + ret[i++] = get(s); + } + return ret; + } + + public final native Object[] elemImpl() /*-{ + var key, ret=[]; + for(key in this) ret.push(this[key]); + return ret; + }-*/; + + + public final native JsArrayString keysImpl() /*-{ + var key, keys=[]; + for(key in this) keys.push("" + key); + return keys; + }-*/; } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsMap.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsMap.java index 2931a66a..41ab52d8 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsMap.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsMap.java @@ -16,6 +16,7 @@ package com.google.gwt.query.client.js; import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.JsArrayString; /** @@ -42,5 +43,9 @@ final public class JsMap extends JavaScriptObject { public void put(S key, T val) { c().put(key.hashCode(), val); } + + public final String[] keys() { + return c().keys(); + } } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsObjectArray.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsObjectArray.java index 839292d3..81a7290f 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsObjectArray.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsObjectArray.java @@ -15,6 +15,9 @@ */ package com.google.gwt.query.client.js; +import java.util.ArrayList; +import java.util.Arrays; + import com.google.gwt.core.client.JavaScriptObject; /** @@ -33,8 +36,10 @@ public final class JsObjectArray extends JavaScriptObject { return cast(); } - public void add(T val) { - c().put(length(), val); + public void add(T...vals) { + for (T t: vals) { + c().put(length(), t); + } } public void add(int i, T val) { @@ -61,4 +66,5 @@ public final class JsObjectArray extends JavaScriptObject { public void pushAll(JavaScriptObject prevElem) { c().pushAll(prevElem); } + } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java index 2dc8f785..d8e5d52b 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java @@ -19,6 +19,8 @@ import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.query.client.Function; import com.google.gwt.query.client.GQuery; +import com.google.gwt.query.client.js.JsCache; +import com.google.gwt.query.client.js.JsObjectArray; import com.google.gwt.query.client.plugins.events.EventsListener; import com.google.gwt.user.client.Event; @@ -28,6 +30,8 @@ import com.google.gwt.user.client.Event; public class Events extends GQuery { public static final Class Events = Events.class; + + protected static final String LIVE_ID_DATA = "_lid_"; static { GQuery.registerPlugin(Events.class, new Plugin() { @@ -98,6 +102,53 @@ public class Events extends GQuery { return this; } + /** + * Remove all event handlers previously attached using live() + * The selector used with it must match exactly the selector initially + * used with live(). + */ + public GQuery die(int eventbits) { + JsCache d = dataCache.get(LIVE_ID_DATA); + if (d != null) { + JsCache cache = d.get(currentSelector); + if (cache != null) { + cache.delete(eventbits); + } + } + unbind(eventbits); + return this; + } + + /** + * Add events to all elements which match the current selector, + * now and in the future. + */ + public GQuery live(int eventBits, Function... funcs) { + if (currentSelector == null || currentSelector.isEmpty()) { + return this; + } + JsCache d = dataCache.get(LIVE_ID_DATA); + if (d == null) { + d = JsCache.create(); + dataCache.put(LIVE_ID_DATA, d); + } + + JsCache cache = d.get(currentSelector); + if (cache == null) { + cache = JsCache.create(); + d.put(currentSelector, cache); + } + + JsObjectArray functions = cache.get(eventBits); + if (functions == null) { + functions = JsObjectArray.create().cast(); + cache.put(eventBits, functions); + } + functions.add(funcs); + bind(eventBits, null, funcs); + return this; + } + /** * Binds a handler to a particular Event (like Event.ONCLICK) for each matched * element. The handler is executed only once for each element. @@ -167,7 +218,6 @@ public class Events extends GQuery { triggerHtmlEvent("submit"); return this; } - /** * Trigger a html event in all matched elements. @@ -224,4 +274,23 @@ public class Events extends GQuery { e.dispatchEvent(evt); } } + + public GQuery addLiveEvents() { + if (dataCache.exists(LIVE_ID_DATA)) { + JsCache d = dataCache.get(LIVE_ID_DATA); + for (String selector : d.keys()) { + GQuery g = find(selector).add(filter(selector)); + if (g.size() > 0) { + JsCache cache = d.get(selector); + for (int eventBits : cache.indexes()) { + JsObjectArray functions = cache.get(eventBits); + for (int j = 0; j extends LazyBase{ */ LazyEvents bind(String event, Object data, Function...funcs); + /** + * Remove all event handlers previously attached using live() + * The selector used with it must match exactly the selector initially + * used with live(). + */ + GQuery die(int eventbits); + + /** + * Add events to all elements which match the current selector, + * now and in the future. + */ + GQuery live(int eventBits, Function... funcs); + /** * Binds a handler to a particular Event (like Event.ONCLICK) for each matched * element. The handler is executed only once for each element. diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java index a5669535..e681da13 100755 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java @@ -187,7 +187,7 @@ public class PropertiesAnimation extends Animation { Effect fx; //g.show(); for (String key : prps.keys()) { - String val = prps.get(key); + String val = prps.getStr(key); if ((fx = computeFxProp(e, key, val, hidden)) != null) { effects.add(fx); resize = resize || "height".equals(key) || "width".equals(key); diff --git a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java index 2dcd4517..b3f04364 100644 --- a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java +++ b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java @@ -490,11 +490,11 @@ public class GQueryCoreTest extends GWTTestCase { public void testProperties() { Properties p = $$("border:'1px solid black'"); assertEquals(1, p.keys().length); - assertNotNull(p.get("border")); + assertNotNull(p.getStr("border")); p = $$("({border:'1px solid black'})"); assertEquals(1, p.keys().length); - assertNotNull(p.get("border")); + assertNotNull(p.getStr("border")); } public void testRelativeMethods() { @@ -1163,5 +1163,5 @@ public class GQueryCoreTest extends GWTTestCase { assertEquals(expectedHtml, $(e).html()); } - + } diff --git a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTest.java b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTest.java index a9883362..eb4a9e80 100644 --- a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTest.java +++ b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTest.java @@ -185,6 +185,30 @@ public class GQueryEventsTest extends GWTTestCase { assertEquals("abc", $("input", e).val()); } + public void testLive() { + $(e).html("
Content 1
"); + $(".clickMe").live(Event.ONCLICK, new Function(){ + public void f(Element e) { + $(e).css("color", "red"); + } + }); + $(e).append("

Content 2

"); + assertEquals("", $("#d1").css("color")); + + $(".clickMe", e).click(); + assertEquals("red", $("div", e).css("color")); + assertEquals("red", $("p", e).css("color")); + + $(".clickMe", e).css("color", "yellow"); + $(".clickMe").die(Event.ONCLICK); + $(e).append("Content 3"); + + $(".clickMe", e).click(); + assertEquals("yellow", $("div", e).css("color")); + assertEquals("yellow", $("p", e).css("color")); + assertEquals("", $("span", e).css("color")); + } + public void testLazyMethods() { $(e).css(CSS.COLOR.with(RGBColor.WHITE)); assertEquals("white", $(e).css("color")); @@ -354,6 +378,6 @@ public class GQueryEventsTest extends GWTTestCase { $("#test").focus(new Function(){}); assertEquals($("#test").attr("tabIndex"), "2"); - } + } -- 2.39.5