]> source.dussan.org Git - gwtquery.git/commitdiff
implement live() and die methods
authorJulien Dramaix <julien.dramaix@gmail.com>
Fri, 8 Apr 2011 18:35:54 +0000 (18:35 +0000)
committerJulien Dramaix <julien.dramaix@gmail.com>
Fri, 8 Apr 2011 18:35:54 +0000 (18:35 +0000)
gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java
gwtquery-core/src/main/java/com/google/gwt/query/client/LazyGQuery.java
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/LazyEvents.java
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/MousePlugin.java
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/UiPlugin.java
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/Event.java [new file with mode: 0644]
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java
gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java
gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTest.java

index e695872231a0c030ffe6cdf09aea99becffcf1a2..c8c95539c395ea1e3d21085c45cd2d3ba77da7fe 100644 (file)
@@ -352,7 +352,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
       n = n.getLastChild();\r
     }\r
     // TODO: add fixes for IE TBODY issue\r
-    return $((NodeList<Element>) n.getChildNodes().cast()).as(Events).addLiveEvents();\r
+    return $((NodeList<Element>) n.getChildNodes().cast());\r
   }\r
 \r
   protected static <S> Object data(Element item, String name, S value) {\r
@@ -972,7 +972,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
     GQuery ret = new GQuery(result);\r
     ret.currentContext = currentContext;\r
     ret.currentSelector = currentSelector;\r
-    return ret.as(Events).addLiveEvents();\r
+    return ret;\r
   }\r
   \r
   /**\r
@@ -987,67 +987,80 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
   }\r
   \r
   /**\r
-   * Returns a {@link Map} object as key a selector and as value the first ancestor elements matching this selectors, beginning at the\r
-   * first matched element and progressing up through the DOM. This method allows retrieving the list of closest ancestors matching \r
-   * many selectors and by reducing the number of DOM traversing.\r
+   * Returns a {@link Map} object as key a selector and as value the list of\r
+   * ancestor elements matching this selectors, beginning at the first matched\r
+   * element and progressing up through the DOM. This method allows retrieving\r
+   * the list of ancestors matching many selectors by traversing the DOM only\r
+   * one time.\r
    * \r
    * @param selector\r
    * @return\r
    */\r
-  public Map<String, Element> closest(String[] selectors){\r
+  public Map<String, List<Element>> closest(String[] selectors) {\r
     return closest(selectors, null);\r
   }\r
-  \r
+\r
   /**\r
-   * Returns a GQuery object containing the first ancestor elements matching each selectors, beginning at the\r
-   * first matched element and progressing up through the DOM tree until reach the <code>context</code> node..\r
-   * This method allows retrieving the list of closest ancestors matching many selectors and by reducing the number of DOM traversing.\r
+   * Returns a {@link Map} object as key a selector and as value the list of\r
+   * ancestor elements matching this selectors, beginning at the first matched\r
+   * element and progressing up through the DOM until reach the\r
+   * <code>context</code> node.. This method allows retrieving the list of\r
+   * ancestors matching many selectors by traversing the DOM only one time.\r
    * \r
    * @param selector\r
    * @return\r
    */\r
-  public Map<String, Element> closest(String[] selectors, Node context){\r
-    Map<String, Element> results = new HashMap<String, Element>();\r
-    \r
-    if (context == null){\r
+  public Map<String, List<Element>> closest(String[] selectors, Node context) {\r
+    Map<String, List<Element>> results = new HashMap<String, List<Element>>();\r
+\r
+    if (context == null) {\r
       context = currentContext;\r
     }\r
-    \r
+\r
     Element first = get(0);\r
-    \r
-    if (first != null && selectors != null && selectors.length > 0){\r
-      \r
+\r
+    if (first != null && selectors != null && selectors.length > 0) {\r
+\r
       Map<String, GQuery> matches = new HashMap<String, GQuery>();\r
-      \r
-      for (String selector : selectors){\r
-        if (!matches.containsKey(selector)){\r
-          matches.put(selector, selector.matches(POS_REGEX) ? $(selector, context) : null);\r
+\r
+      for (String selector : selectors) {\r
+        if (!matches.containsKey(selector)) {\r
+          matches.put(selector, selector.matches(POS_REGEX) ? $(selector,\r
+              context) : null);\r
         }\r
       }\r
-      \r
+\r
       Element current = first;\r
-      \r
-      while (current != null && current.getOwnerDocument() != null && current != context){\r
-        //for each selector, check if the current element match it.\r
-        for (String selector : matches.keySet()){\r
-          if (results.containsKey(selector)){\r
-            //first ancestors already found for this selector\r
-            continue;\r
-          }\r
+\r
+      while (current != null && current.getOwnerDocument() != null\r
+          && current != context) {\r
+        // for each selector, check if the current element match it.\r
+        for (String selector : matches.keySet()) {\r
+          \r
           GQuery pos = matches.get(selector);\r
-          boolean match = pos != null ? pos.index(current) > -1 : $(current).is(selector);\r
-          if (match){\r
-            results.put(selector, current);\r
+         \r
+          boolean match = pos != null ? pos.index(current) > -1\r
+              : $(current).is(selector);\r
+          \r
+          if (match) {\r
+            \r
+            List<Element> elementsMatchingSelector = results.get(selector);\r
+            \r
+            if (elementsMatchingSelector == null) {\r
+              elementsMatchingSelector = new ArrayList<Element>();\r
+              results.put(selector, elementsMatchingSelector);\r
+            }\r
+            \r
+            elementsMatchingSelector.add(current);\r
           }\r
         }\r
-        \r
-        current = current.getParentElement();  \r
+\r
+        current = current.getParentElement();\r
       }\r
-      \r
-      \r
+\r
     }\r
-    \r
-      return results;\r
+\r
+    return results;\r
   }\r
   \r
   /**\r
@@ -1406,16 +1419,27 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
   public GQuery detach(String filter) {\r
     return remove(filter, false);\r
   }\r
-  \r
+\r
   /**\r
-   * Remove all event handlers previously attached using live()\r
-   * The selector used with it must match exactly the selector initially\r
-   * used with live().\r
+   * Remove all event handlers previously attached using\r
+   * {@link #live(String, Function)}. In order for this method to function\r
+   * correctly, the selector used with it must match exactly the selector\r
+   * initially used with {@link #live(String, Function)}\r
    */\r
-  public GQuery die(int eventbits) {\r
-    return as(Events).die(eventbits);\r
+  public GQuery die() {\r
+    return as(Events).die(null);\r
   }\r
-  \r
+\r
+  /**\r
+   * Remove an event handlers previously attached using\r
+   * {@link #live(String, Function)} In order for this method to function\r
+   * correctly, the selector used with it must match exactly the selector\r
+   * initially used with {@link #live(String, Function)}\r
+   */\r
+  public GQuery die(String eventName) {\r
+    return as(Events).die(eventName);\r
+  }\r
+\r
   /**\r
    * Run one or more Functions over each element of the GQuery. You have to\r
    * override one of these funcions: public void f(Element e) public String\r
@@ -1763,7 +1787,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
       }\r
       e.setInnerHTML(html);\r
     }\r
-    return as(Events).addLiveEvents();\r
+    return this;\r
   }\r
 \r
   /**\r
@@ -1932,13 +1956,117 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
   public int length() {\r
     return size();\r
   }\r
+\r
+  /**\r
+   * <p>\r
+   * Attach a handler for this event to all elements which match the current\r
+   * selector, now and in the future.\r
+   * <p>\r
+   * <p>\r
+   * Ex :\r
+   * \r
+   * <pre>\r
+   * $(".clickable").live("click", new Function(){\r
+   *  public void f(Element e){\r
+   *    $(e).css(CSS.COLOR.with(RGBColor.RED));\r
+   *  }\r
+   * });\r
+   *  </pre>\r
+   * \r
+   * With this code, all elements with class "clickable" present in the DOM or\r
+   * added to the DOM in the future will be clickable. The text color will be\r
+   * changed to red when they will be clicked. So if after in the code, you add\r
+   * another element :\r
+   * \r
+   * <pre>\r
+   * $("body").append("<div class='clickable'>Click me and I will be red</div>");\r
+   * </pre>\r
+   * \r
+   * The click on this new element will also trigger the handler.\r
+   * </p>\r
+   * <p>\r
+   * In the same way, if you add "clickable" class on some existing element,\r
+   * these elements will be clickable also.\r
+   * </p>\r
+   * <p>\r
+   * <h3>important remarks</h3>\r
+   * <ul>\r
+   * <li>\r
+   * The live method should be always called after a selector</li>\r
+   * <li>\r
+   * Live events are bound to the context of the {@link GQuery} object :\r
+   * \r
+   * <pre>\r
+   * $(".clickable", myElement).live("click", new Function(){\r
+   *  public void f(Element e){\r
+   *    $(e).css(CSS.COLOR.with(RGBColor.RED));\r
+   *  }\r
+   * });\r
+   * </pre>\r
+   * The {@link Function} will be called only on elements having the class\r
+   * "clickable" and being descendant of myElement.</li>\r
+   * </ul>\r
+   * </p>\r
+   */\r
+  public GQuery live(String eventName, Function func) {\r
+    return as(Events).live(eventName, null, func);\r
+  }\r
   \r
+\r
   /**\r
-   * Add events to all elements which match the current selector,\r
-   * now and in the future.\r
+   * <p>\r
+   * Attach a handler for this event to all elements which match the current\r
+   * selector, now and in the future.\r
+   * The <code>data</code> parameter allows us to pass data to the handler.\r
+   * <p>\r
+   * <p>\r
+   * Ex :\r
+   * \r
+   * <pre>\r
+   * $(".clickable").live("click", new Function(){\r
+   *  public void f(Element e){\r
+   *    $(e).css(CSS.COLOR.with(RGBColor.RED));\r
+   *  }\r
+   * });\r
+   *  </pre>\r
+   * \r
+   * With this code, all elements with class "clickable" present in the DOM or\r
+   * added to the DOM in the future will be clickable. The text color will be\r
+   * changed to red when they will be clicked. So if after in the code, you add\r
+   * another element :\r
+   * \r
+   * <pre>\r
+   * $("body").append("<div class='clickable'>Click me and I will be red</div>");\r
+   * </pre>\r
+   * \r
+   * The click on this new element will also trigger the handler.\r
+   * </p>\r
+   * <p>\r
+   * In the same way, if you add "clickable" class on some existing element,\r
+   * these elements will be clickable also.\r
+   * </p>\r
+   * <p>\r
+   * <h3>important remarks</h3>\r
+   * <ul>\r
+   * <li>\r
+   * The live method should be always called after a selector</li>\r
+   * <li>\r
+   * Live events are bound to the context of the {@link GQuery} object :\r
+   * \r
+   * <pre>\r
+   * $(".clickable", myElement).live("click", new Function(){\r
+   *  public void f(Element e){\r
+   *    $(e).css(CSS.COLOR.with(RGBColor.RED));\r
+   *  }\r
+   * });\r
+   * </pre>\r
+   * The {@link Function} will be called only on elements having the class\r
+   * "clickable" and being descendant of myElement.</li>\r
+   * </ul>\r
+   * </p>\r
    */\r
-  public GQuery live(int eventBits, Function... funcs) {\r
-    return as(Events).live(eventBits, funcs);\r
+  public GQuery live(String eventName, Object data, Function func) {\r
+    return as(Events).live(eventName, data, func);\r
   }\r
 \r
   /**\r
index 402d782fc5bd818cb7002ee96408f4761d00d541..2a5b32ce53c58763db1cb0e6d51ac48f845a17a8 100644 (file)
@@ -57,6 +57,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import com.google.gwt.query.client.LazyBase;
 
 public interface LazyGQuery<T> extends LazyBase<T>{
@@ -399,6 +400,48 @@ public interface LazyGQuery<T> extends LazyBase<T>{
    */
   LazyGQuery<T> clone();
 
+  /**
+   * Get the first ancestor element that matches the selector (for each matched element), beginning at the
+   * current element and progressing up through the DOM tree.
+   * 
+   * @param selector
+   * @return
+   */
+  LazyGQuery<T> closest(String selector);
+
+  /**
+   * Returns a {@link Map} object as key a selector and as value the list of
+   * ancestor elements matching this selectors, beginning at the first matched
+   * element and progressing up through the DOM. This method allows retrieving
+   * the list of ancestors matching many selectors by traversing the DOM only
+   * one time.
+   * 
+   * @param selector
+   * @return
+   */
+  Map<String, List<Element>> closest(String[] selectors);
+
+  /**
+   * Returns a {@link Map} object as key a selector and as value the list of
+   * ancestor elements matching this selectors, beginning at the first matched
+   * element and progressing up through the DOM until reach the
+   * <code>context</code> node.. This method allows retrieving the list of
+   * ancestors matching many selectors by traversing the DOM only one time.
+   * 
+   * @param selector
+   * @return
+   */
+  Map<String, List<Element>> closest(String[] selectors, Node context);
+
+  /**
+   * Get the first ancestor element that matches the selector (for each matched element), beginning at the
+   * current element and progressing up through the DOM tree until reach the <code>context</code> node.
+   * 
+   * If no context is passed in then the context of the gQuery object will be used instead.
+   *
+   */
+  LazyGQuery<T> closest(String selector, Node context);
+
   /**
    * Filter the set of elements to those that contain the specified text.
    */
@@ -622,11 +665,20 @@ public interface LazyGQuery<T> extends LazyBase<T>{
   LazyGQuery<T> 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().
+   * Remove all event handlers previously attached using
+   * {@link #live(String, Function)}. In order for this method to function
+   * correctly, the selector used with it must match exactly the selector
+   * initially used with {@link #live(String, Function)}
+   */
+  LazyGQuery<T> die();
+
+  /**
+   * Remove an event handlers previously attached using
+   * {@link #live(String, Function)} In order for this method to function
+   * correctly, the selector used with it must match exactly the selector
+   * initially used with {@link #live(String, Function)}
    */
-  LazyGQuery<T> die(int eventbits);
+  LazyGQuery<T> die(String eventName);
 
   /**
    * Run one or more Functions over each element of the GQuery. You have to
@@ -760,6 +812,8 @@ public interface LazyGQuery<T> extends LazyBase<T>{
    */
   Element get(int i);
 
+  Node getContext();
+
   /**
    * Return the previous set of matched elements prior to the last destructive
    * operation (e.g. query)
@@ -946,10 +1000,111 @@ public interface LazyGQuery<T> extends LazyBase<T>{
   int length();
 
   /**
-   * Add events to all elements which match the current selector,
-   * now and in the future.
+   * <p>
+   * Attach a handler for this event to all elements which match the current
+   * selector, now and in the future.
+   * <p>
+   * <p>
+   * Ex :
+   * 
+   * <pre>
+   * $(".clickable").live("click", new Function(){
+   *  public void f(Element e){
+   *    $(e).css(CSS.COLOR.with(RGBColor.RED));
+   *  }
+   * });
+   *  </pre>
+   * 
+   * With this code, all elements with class "clickable" present in the DOM or
+   * added to the DOM in the future will be clickable. The text color will be
+   * changed to red when they will be clicked. So if after in the code, you add
+   * another element :
+   * 
+   * <pre>
+   * $("body").append("<div class='clickable'>Click me and I will be red</div>");
+   * </pre>
+   * 
+   * The click on this new element will also trigger the handler.
+   * </p>
+   * <p>
+   * In the same way, if you add "clickable" class on some existing element,
+   * these elements will be clickable also.
+   * </p>
+   * <p>
+   * <h3>important remarks</h3>
+   * <ul>
+   * <li>
+   * The live method should be always called after a selector</li>
+   * <li>
+   * Live events are bound to the context of the {@link GQuery} object :
+   * 
+   * <pre>
+   * $(".clickable", myElement).live("click", new Function(){
+   *  public void f(Element e){
+   *    $(e).css(CSS.COLOR.with(RGBColor.RED));
+   *  }
+   * });
+   * </pre>
+   * The {@link Function} will be called only on elements having the class
+   * "clickable" and being descendant of myElement.</li>
+   * </ul>
+   * </p>
+   */
+  LazyGQuery<T> live(String eventName, Function func);
+
+  /**
+   * <p>
+   * Attach a handler for this event to all elements which match the current
+   * selector, now and in the future.
+   * The <code>data</code> parameter allows us to pass data to the handler.
+   * <p>
+   * <p>
+   * Ex :
+   * 
+   * <pre>
+   * $(".clickable").live("click", new Function(){
+   *  public void f(Element e){
+   *    $(e).css(CSS.COLOR.with(RGBColor.RED));
+   *  }
+   * });
+   *  </pre>
+   * 
+   * With this code, all elements with class "clickable" present in the DOM or
+   * added to the DOM in the future will be clickable. The text color will be
+   * changed to red when they will be clicked. So if after in the code, you add
+   * another element :
+   * 
+   * <pre>
+   * $("body").append("<div class='clickable'>Click me and I will be red</div>");
+   * </pre>
+   * 
+   * The click on this new element will also trigger the handler.
+   * </p>
+   * <p>
+   * In the same way, if you add "clickable" class on some existing element,
+   * these elements will be clickable also.
+   * </p>
+   * <p>
+   * <h3>important remarks</h3>
+   * <ul>
+   * <li>
+   * The live method should be always called after a selector</li>
+   * <li>
+   * Live events are bound to the context of the {@link GQuery} object :
+   * 
+   * <pre>
+   * $(".clickable", myElement).live("click", new Function(){
+   *  public void f(Element e){
+   *    $(e).css(CSS.COLOR.with(RGBColor.RED));
+   *  }
+   * });
+   * </pre>
+   * The {@link Function} will be called only on elements having the class
+   * "clickable" and being descendant of myElement.</li>
+   * </ul>
+   * </p>
    */
-  LazyGQuery<T> live(int eventBits, Function... funcs);
+  LazyGQuery<T> live(String eventName, Object data, Function func);
 
   /**
    * Bind a function to the load event of each matched element.
index d8e5d52b7db48a5e704ea332cbb49c073043037b..fc58ce0f991bb144354594726047722880cad9ae 100644 (file)
@@ -19,8 +19,6 @@ 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;
 
@@ -30,8 +28,6 @@ import com.google.gwt.user.client.Event;
 public class Events extends GQuery {
 
   public static final Class<Events> Events = Events.class;
-  
-  protected static final String LIVE_ID_DATA = "_lid_";
 
   static {
     GQuery.registerPlugin(Events.class, new Plugin<Events>() {
@@ -44,6 +40,7 @@ public class Events extends GQuery {
   public Events(GQuery gq) {
     super(gq);
   }
+
   /**
    * Binds a set of handlers to a particular Event for each matched element.
    * 
@@ -55,7 +52,7 @@ public class Events extends GQuery {
    * parameter
    * 
    */
-  public Events bind(int eventbits, Object data, Function...funcs) {
+  public Events bind(int eventbits, Object data, Function... funcs) {
     for (Element e : elements()) {
       EventsListener.getInstance(e).bind(eventbits, data, funcs);
     }
@@ -65,8 +62,8 @@ public class Events extends GQuery {
   /**
    * Binds a set of handlers to a particular Event for each matched element.
    * 
-   * The namespace is a way to group events of the same type, making easier unbind
-   * specific handlers.
+   * The namespace is a way to group events of the same type, making easier
+   * unbind specific handlers.
    * 
    * The event handlers are passed as Functions that you can use to prevent
    * default behavior. To stop both default action and event bubbling, the
@@ -74,8 +71,9 @@ public class Events extends GQuery {
    * 
    * You can pass an additional Object data to your Function
    * 
-   */  
-  public Events bind(int eventbits, String namespace, Object data, Function...funcs) {
+   */
+  public Events bind(int eventbits, String namespace, Object data,
+      Function... funcs) {
     for (Element e : elements()) {
       EventsListener.getInstance(e).bind(eventbits, namespace, data, funcs);
     }
@@ -85,8 +83,8 @@ public class Events extends GQuery {
   /**
    * Binds a set of handlers to a particular Event for each matched element.
    * 
-   * The name could contain a namespace which is a way to group events of the same type, 
-   * making easier unbind specific handlers.
+   * The name could contain a namespace which is a way to group events of the
+   * same type, making easier unbind specific handlers.
    * 
    * The event handlers are passed as Functions that you can use to prevent
    * default behavior. To stop both default action and event bubbling, the
@@ -94,89 +92,68 @@ public class Events extends GQuery {
    * 
    * You can pass an additional Object data to your Function
    * 
-   */  
-  public Events bind(String event, Object data, Function...funcs) {
+   */
+  public Events bind(String event, Object data, Function... funcs) {
     for (Element e : elements()) {
       EventsListener.getInstance(e).bind(event, data, funcs);
     }
     return this;
   }
-  
+
   /**
-   * Remove all event handlers previously attached using live()
-   * The selector used with it must match exactly the selector initially
-   * used with live().
+   * Remove an event handlers previously attached using live() The selector used
+   * with it must match exactly the selector initially used with live(). if
+   * <code>eventName</code> is null, all event handlers corresponding of the
+   * GQuery selector will be removed
    */
-  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);
+  public GQuery die(String eventName) {
+    EventsListener.getInstance(
+        Element.is(currentContext) ? (Element) currentContext : body).die(
+        eventName, currentSelector);
     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<Function> functions = cache.get(eventBits);
-    if (functions == null) {
-      functions = JsObjectArray.create().cast();
-      cache.put(eventBits, functions);
-    }
-    functions.add(funcs);
-    bind(eventBits, null, funcs);
+  public GQuery live(String eventName, final Object data, Function func) {
+
+    // bind live delegating event to the current context
+    EventsListener.getInstance(
+        Element.is(currentContext) ? (Element) currentContext : body).live(
+        eventName, currentSelector, data, func);
+
     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.
-   *
+   * 
    * The event handler is passed as a Function that you can use to prevent
    * default behavior. To stop both default action and event bubbling, the
    * function event handler has to return false.
-   *
+   * 
    * You can pass an additional Object data to your Function as the second
    * parameter
-   */  
+   */
   public Events one(int eventbits, final Object data, final Function f) {
     for (Element e : elements()) {
       EventsListener.getInstance(e).bind(eventbits, data, f, 1);
     }
     return this;
   }
-  
+
   /**
-   * Execute all handlers and behaviors attached to the matched elements for the given event types.
+   * Execute all handlers and behaviors attached to the matched elements for the
+   * given event types.
    * 
-   * Different event types can be passed joining these using the or bit wise operator.
+   * Different event types can be passed joining these using the or bit wise
+   * operator.
    * 
-   * For keyboard events you can pass a second parameter which represents 
-   * the key-code of the pushed key. 
+   * For keyboard events you can pass a second parameter which represents the
+   * key-code of the pushed key.
    * 
-   * Example: fire(Event.ONCLICK | Event.ONFOCUS)
-   * Example: fire(Event.ONKEYDOWN. 'a');
+   * Example: fire(Event.ONCLICK | Event.ONFOCUS) Example: fire(Event.ONKEYDOWN.
+   * 'a');
    */
   @SuppressWarnings("deprecation")
   public Events trigger(int eventbits, int... keys) {
@@ -185,35 +162,46 @@ public class Events extends GQuery {
     if ((eventbits | Event.ONCHANGE) == Event.ONCHANGE)
       dispatchEvent(document.createChangeEvent());
     if ((eventbits | Event.ONCLICK) == Event.ONCLICK)
-      dispatchEvent(document.createClickEvent(0, 0, 0, 0, 0, false, false, false, false));
+      dispatchEvent(document.createClickEvent(0, 0, 0, 0, 0, false, false,
+          false, false));
     if ((eventbits | Event.ONDBLCLICK) == Event.ONDBLCLICK)
-      dispatchEvent(document.createDblClickEvent(0, 0, 0, 0, 0, false, false, false, false));
+      dispatchEvent(document.createDblClickEvent(0, 0, 0, 0, 0, false, false,
+          false, false));
     if ((eventbits | Event.ONFOCUS) == Event.ONFOCUS)
       dispatchEvent(document.createFocusEvent());
     if ((eventbits | Event.ONKEYDOWN) == Event.ONKEYDOWN)
-      dispatchEvent(document.createKeyDownEvent(false, false, false, false, keys[0], 0));
+      dispatchEvent(document.createKeyDownEvent(false, false, false, false,
+          keys[0], 0));
     if ((eventbits | Event.ONKEYPRESS) == Event.ONKEYPRESS)
-      dispatchEvent(document.createKeyPressEvent(false, false, false, false, keys[0], 0));
+      dispatchEvent(document.createKeyPressEvent(false, false, false, false,
+          keys[0], 0));
     if ((eventbits | Event.ONKEYUP) == Event.ONKEYUP)
-      dispatchEvent(document.createKeyUpEvent(false, false, false, false, keys[0], 0));
+      dispatchEvent(document.createKeyUpEvent(false, false, false, false,
+          keys[0], 0));
     if ((eventbits | Event.ONLOSECAPTURE) == Event.ONLOSECAPTURE)
       triggerHtmlEvent("losecapture");
     if ((eventbits | Event.ONMOUSEDOWN) == Event.ONMOUSEDOWN)
-      dispatchEvent(document.createMouseDownEvent(0, 0, 0, 0, 0, false, false, false, false, NativeEvent.BUTTON_LEFT));
+      dispatchEvent(document.createMouseDownEvent(0, 0, 0, 0, 0, false, false,
+          false, false, NativeEvent.BUTTON_LEFT));
     if ((eventbits | Event.ONMOUSEMOVE) == Event.ONMOUSEMOVE)
-      dispatchEvent(document.createMouseMoveEvent(0, 0, 0, 0, 0, false, false, false, false, NativeEvent.BUTTON_LEFT));
+      dispatchEvent(document.createMouseMoveEvent(0, 0, 0, 0, 0, false, false,
+          false, false, NativeEvent.BUTTON_LEFT));
     if ((eventbits | Event.ONMOUSEOUT) == Event.ONMOUSEOUT)
-      dispatchEvent(document.createMouseOutEvent(0, 0, 0, 0, 0, false, false, false, false, NativeEvent.BUTTON_LEFT, null));
+      dispatchEvent(document.createMouseOutEvent(0, 0, 0, 0, 0, false, false,
+          false, false, NativeEvent.BUTTON_LEFT, null));
     if ((eventbits | Event.ONMOUSEOVER) == Event.ONMOUSEOVER)
-      dispatchEvent(document.createMouseOverEvent(0, 0, 0, 0, 0, false, false, false, false, NativeEvent.BUTTON_LEFT, null));
+      dispatchEvent(document.createMouseOverEvent(0, 0, 0, 0, 0, false, false,
+          false, false, NativeEvent.BUTTON_LEFT, null));
     if ((eventbits | Event.ONMOUSEUP) == Event.ONMOUSEUP)
-      dispatchEvent(document.createMouseUpEvent(0, 0, 0, 0, 0, false, false, false, false, NativeEvent.BUTTON_LEFT));
+      dispatchEvent(document.createMouseUpEvent(0, 0, 0, 0, 0, false, false,
+          false, false, NativeEvent.BUTTON_LEFT));
     if ((eventbits | Event.ONSCROLL) == Event.ONSCROLL)
       dispatchEvent(document.createScrollEvent());
     if ((eventbits | Event.ONERROR) == Event.ONERROR)
       dispatchEvent(document.createErrorEvent());
     if ((eventbits | Event.ONMOUSEWHEEL) == Event.ONMOUSEWHEEL)
-      dispatchEvent(document.createMouseEvent("mousewheel", true, true, 0, 0, 0, 0, 0, false, false, false, false, NativeEvent.BUTTON_LEFT, null));
+      dispatchEvent(document.createMouseEvent("mousewheel", true, true, 0, 0,
+          0, 0, 0, false, false, false, false, NativeEvent.BUTTON_LEFT, null));
     if (eventbits == EventsListener.ONSUBMIT)
       triggerHtmlEvent("submit");
     return this;
@@ -222,14 +210,13 @@ public class Events extends GQuery {
   /**
    * Trigger a html event in all matched elements.
    * 
-   * @param htmlEvent
-   *    An string representing the html event desired 
+   * @param htmlEvent An string representing the html event desired
    */
   public Events triggerHtmlEvent(String htmlEvent) {
     dispatchEvent(document.createHtmlEvent(htmlEvent, true, true));
     return this;
   }
-  
+
   /**
    * Removes all handlers, that matches the events bits passed, from each
    * element.
@@ -242,7 +229,7 @@ public class Events extends GQuery {
     }
     return this;
   }
-  
+
   /**
    * Removes all handlers, that matches the events bits and the namespace
    * passed, from each element.
@@ -255,10 +242,10 @@ public class Events extends GQuery {
     }
     return this;
   }
-  
+
   /**
-   * Removes all handlers, that matches event name passed. This name
-   * could contain a namespace.
+   * Removes all handlers, that matches event name passed. This name could
+   * contain a namespace.
    * 
    * Example: unbind("click.my.namespace")
    */
@@ -268,29 +255,11 @@ public class Events extends GQuery {
     }
     return this;
   }
-  
+
   private void dispatchEvent(NativeEvent evt) {
     for (Element e : elements()) {
       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<Function> functions = cache.get(eventBits);
-            for (int j = 0; j<functions.length(); j++) {
-              g.bind(eventBits, null, functions.get(j));
-            }
-          }
-        }
-      }
-    }
-    return this;
-  }
+
 }
index 4b5108c970a41a0117c47b5a90d42318ef22bdf6..126f6b60399604bef700f8156fa999dfde248659 100644 (file)
@@ -18,8 +18,6 @@ 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;
 import com.google.gwt.query.client.LazyBase;
@@ -37,13 +35,13 @@ public interface LazyEvents<T> extends LazyBase<T>{
    * parameter
    * 
    */
-  LazyEvents<T> bind(int eventbits, Object data, Function...funcs);
+  LazyEvents<T> bind(int eventbits, Object data, Function... funcs);
 
   /**
    * Binds a set of handlers to a particular Event for each matched element.
    * 
-   * The namespace is a way to group events of the same type, making easier unbind
-   * specific handlers.
+   * The namespace is a way to group events of the same type, making easier
+   * unbind specific handlers.
    * 
    * The event handlers are passed as Functions that you can use to prevent
    * default behavior. To stop both default action and event bubbling, the
@@ -51,14 +49,14 @@ public interface LazyEvents<T> extends LazyBase<T>{
    * 
    * You can pass an additional Object data to your Function
    * 
-   */  
-  LazyEvents<T> bind(int eventbits, String namespace, Object data, Function...funcs);
+   */
+  LazyEvents<T> bind(int eventbits, String namespace, Object data, Function... funcs);
 
   /**
    * Binds a set of handlers to a particular Event for each matched element.
    * 
-   * The name could contain a namespace which is a way to group events of the same type, 
-   * making easier unbind specific handlers.
+   * The name could contain a namespace which is a way to group events of the
+   * same type, making easier unbind specific handlers.
    * 
    * The event handlers are passed as Functions that you can use to prevent
    * default behavior. To stop both default action and event bubbling, the
@@ -66,53 +64,51 @@ public interface LazyEvents<T> extends LazyBase<T>{
    * 
    * You can pass an additional Object data to your Function
    * 
-   */  
-  LazyEvents<T> 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);
+  LazyEvents<T> bind(String event, Object data, Function... funcs);
 
   /**
-   * Add events to all elements which match the current selector,
-   * now and in the future.
+   * Remove an event handlers previously attached using live() The selector used
+   * with it must match exactly the selector initially used with live(). if
+   * <code>eventName</code> is null, all event handlers corresponding of the
+   * GQuery selector will be removed
    */
-  GQuery live(int eventBits, Function... funcs);
+  GQuery die(String eventName);
+
+  GQuery live(String eventName, Object data, Function func);
 
   /**
    * Binds a handler to a particular Event (like Event.ONCLICK) for each matched
    * element. The handler is executed only once for each element.
-   *
+   * 
    * The event handler is passed as a Function that you can use to prevent
    * default behavior. To stop both default action and event bubbling, the
    * function event handler has to return false.
-   *
+   * 
    * You can pass an additional Object data to your Function as the second
    * parameter
-   */  
+   */
   LazyEvents<T> one(int eventbits, Object data, Function f);
 
   /**
-   * Execute all handlers and behaviors attached to the matched elements for the given event types.
+   * Execute all handlers and behaviors attached to the matched elements for the
+   * given event types.
    * 
-   * Different event types can be passed joining these using the or bit wise operator.
+   * Different event types can be passed joining these using the or bit wise
+   * operator.
    * 
-   * For keyboard events you can pass a second parameter which represents 
-   * the key-code of the pushed key. 
+   * For keyboard events you can pass a second parameter which represents the
+   * key-code of the pushed key.
    * 
-   * Example: fire(Event.ONCLICK | Event.ONFOCUS)
-   * Example: fire(Event.ONKEYDOWN. 'a');
+   * Example: fire(Event.ONCLICK | Event.ONFOCUS) Example: fire(Event.ONKEYDOWN.
+   * 'a');
    */
   LazyEvents<T> trigger(int eventbits, int... keys);
 
   /**
    * Trigger a html event in all matched elements.
    * 
-   * @param htmlEvent
-   *    An string representing the html event desired 
+   * @param htmlEvent An string representing the html event desired
    */
   LazyEvents<T> triggerHtmlEvent(String htmlEvent);
 
@@ -133,8 +129,8 @@ public interface LazyEvents<T> extends LazyBase<T>{
   LazyEvents<T> unbind(int eventbits, String name);
 
   /**
-   * Removes all handlers, that matches event name passed. This name
-   * could contain a namespace.
+   * Removes all handlers, that matches event name passed. This name could
+   * contain a namespace.
    * 
    * Example: unbind("click.my.namespace")
    */
index 5dee20d5cdcf4275cc5f98337c2970b0f2908773..ccac4b194fab49027f10cc8420ed863fa0c2c92d 100755 (executable)
@@ -62,14 +62,14 @@ public abstract class MousePlugin extends UiPlugin {
           (Object) null, new Function() {\r
             @Override\r
             public boolean f(com.google.gwt.user.client.Event event) {\r
-              return mouseDown(e, Event.create(event));\r
+              return mouseDown(e, (Event)Event.create(event));\r
 \r
             }\r
           }).bind(Event.ONCLICK, getPluginName(), (Object) null,\r
           new Function() {\r
             @Override\r
             public boolean f(com.google.gwt.user.client.Event event) {\r
-              preventClickEvent |= !mouseClick(e, Event.create(event));\r
+              preventClickEvent |= !mouseClick(e, (Event)Event.create(event));\r
 \r
               if (preventClickEvent) {\r
 \r
@@ -212,14 +212,14 @@ public abstract class MousePlugin extends UiPlugin {
         (Object) null, new Function() {\r
           @Override\r
           public boolean f(com.google.gwt.user.client.Event e) {\r
-            mouseMove(element, Event.create(e));\r
+            mouseMove(element, (Event) Event.create(e));\r
             return false;\r
           }\r
         }).bind(Event.ONMOUSEUP, getPluginName(), (Object) null,\r
         new Function() {\r
           @Override\r
           public boolean f(com.google.gwt.user.client.Event e) {\r
-            mouseUp(element, Event.create(e));\r
+            mouseUp(element, (Event) Event.create(e));\r
             return false;\r
           }\r
         });\r
index 78dc3b1093d6b383bece64344b799b5ca6033fb3..92c7539582ede52e20293b8f4d3027149154ac9f 100755 (executable)
@@ -64,72 +64,14 @@ public class UiPlugin extends GQuery {
   }
 
   /**
-   * This object allows you to have a full copy of the original Event and
-   * implements some useful method of the jQuery event model. This is also
-   * useful in Internet Explorer because it use the same javascript object to
-   * fire MouseDownEvent, MouseMoveEvent or MouseStopEvent on the same element.
-   * So, we cannot keep a copy of the MouseDownEvent during a dragginf for
-   * example. Now we can !
    * 
-   * TOBEFIXED : the method preventDefault() must be called directly on the
-   * original event
    *
-   * 
+   * @deprecated use {@link com.google.gwt.query.client.plugins.events.Event} instead 
    */
-  public static class Event extends com.google.gwt.user.client.Event {
-
-    /**
-     * Create a new {@link Event} by copying the <code>originalEvent</code>.
-     */
-    public static Event create(com.google.gwt.user.client.Event originalEvent) {
-      Event gQueryEvent = createObject().cast();
-      copy(originalEvent, gQueryEvent);
-      return gQueryEvent;
-    }
-
-    private static native void copy(
-        com.google.gwt.user.client.Event originalEvent, Event gQueryEvent) /*-{
-      for ( var field in originalEvent  ) {
-        gQueryEvent[field] = originalEvent[field];
-      }   
-      gQueryEvent.originalEvent = originalEvent;
-    }-*/;
-
-    protected Event() {
-    }
-
-    /**
-     * Return the original event (the one created by the browser)
-     */
-    public final native com.google.gwt.user.client.Event getOriginalEvent()/*-{
-      return this.originalEvent;
-    }-*/;
-
-    /**
-     * Tell whether ctrl or cmd key is pressed
-     * 
-     */
-    public final boolean isMetaKeyPressed() {
-      return getMetaKey() || getCtrlKey();
-    }
-
-    /**
-     * The mouse position relative to the left edge of the document
-     * 
-     */
-    public final int pageX() {
-      return getClientX() + document.getScrollLeft();
-    }
-
-    /**
-     * The mouse position relative to the top edge of the document.
-     * 
-     */
-    public final int pageY() {
-      return getClientY() + document.getScrollTop();
-    }
+  public static class Event extends com.google.gwt.query.client.plugins.events.Event {
 
   }
+  
 
   private static class GQueryUiImpl {
 
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/Event.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/Event.java
new file mode 100644 (file)
index 0000000..0b5ae1b
--- /dev/null
@@ -0,0 +1,88 @@
+package com.google.gwt.query.client.plugins.events;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.query.client.GQuery;
+
+/**
+ * This object allows you to have a full copy of the original Event and
+ * implements some useful method of the jQuery event model.
+ * 
+ * This is also useful in Internet Explorer because it use the same javascript
+ * object to fire MouseDownEvent, MouseMoveEvent or MouseStopEvent on the same
+ * element. So, we cannot keep a copy of the MouseDownEvent during a dragging
+ * for example.
+ * 
+ * 
+ * 
+ * TOBEFIXED : the method preventDefault() must be called directly on the
+ * original event
+ * 
+ * 
+ */
+public class Event extends com.google.gwt.user.client.Event {
+
+  
+  /**
+   * Create a new {@link Event} by copying the <code>originalEvent</code>.
+   */
+  public static Event create(com.google.gwt.user.client.Event originalEvent) {
+    Event gQueryEvent = createObject().cast();
+    copy(originalEvent, gQueryEvent);
+    return gQueryEvent;
+  }
+
+  private static native void copy(
+      com.google.gwt.user.client.Event originalEvent, Event gQueryEvent) /*-{
+               for ( var field in originalEvent) {
+                       gQueryEvent[field] = originalEvent[field];
+               }
+               gQueryEvent.originalEvent = originalEvent;
+  }-*/;
+
+  protected Event() {
+  }
+
+  /**
+   * Return the original event (the one created by the browser)
+   */
+  public final native com.google.gwt.user.client.Event getOriginalEvent()/*-{
+               return this.originalEvent;
+  }-*/;
+  
+  public final native void setCurrentElementTarget(Element e)/*-{
+    
+    this.currentTarget = e;
+    
+    //ie don't have a currentEventTarget field on event
+    try{
+      @com.google.gwt.dom.client.DOMImplTrident::currentEventTarget = e;
+    }catch(e){}
+  }-*/;
+
+  /**
+   * Tell whether ctrl or cmd key is pressed
+   * 
+   */
+  public final boolean isMetaKeyPressed() {
+    return getMetaKey() || getCtrlKey();
+  }
+
+  /**
+   * The mouse position relative to the left edge of the document
+   * 
+   */
+  public final int pageX() {
+    return getClientX() + GQuery.document.getScrollLeft();
+  }
+
+  /**
+   * The mouse position relative to the top edge of the document.
+   * 
+   */
+  public final int pageY() {
+    return getClientY() + GQuery.document.getScrollTop();
+  }
+  
+
+}
index dbb7e70a0e944a2b0c098379a8d9f3ca9ac635a9..e77540ffef565bc40cfad6af042c4afb5255a476 100644 (file)
  */
 package com.google.gwt.query.client.plugins.events;
 
+import static com.google.gwt.query.client.GQuery.$;
+
 import com.google.gwt.core.client.Duration;
 import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.EventTarget;
 import com.google.gwt.query.client.Function;
 import com.google.gwt.query.client.js.JsObjectArray;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.EventListener;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * This class implements an event queue instance for one Element. The queue
  * instance is configured as the default event listener in GWT.
@@ -48,7 +55,7 @@ public class EventsListener implements EventListener {
       type = t;
       function = f;
       data = d;
-      if (n!=null) {
+      if (n != null) {
         nameSpace = n;
       }
     }
@@ -69,17 +76,148 @@ public class EventsListener implements EventListener {
     public boolean hasEventType(int etype) {
       return (type & etype) == type;
     }
+
+    @Override
+    public String toString() {
+      return "bind function for event type " + type;
+    }
+  }
+
+  /**
+   * {@link BindFunction} used for live() method.
+   * 
+   */
+  private static class LiveBindFunction extends BindFunction {
+
+    // TODO can be a list of BindFunction
+    Map<String, BindFunction> bindFunctionBySelector;
+
+    LiveBindFunction(int type, String namespace) {
+
+      super(type, namespace, null, null, -1);
+      bindFunctionBySelector = new HashMap<String, BindFunction>();
+    }
+
+    /**
+     * Add a {@link BindFunction} for a specific css selector
+     */
+    public void addBindFunctionForSelector(String cssSelector, BindFunction f) {
+      bindFunctionBySelector.put(cssSelector, f);
+    }
+
+    @Override
+    public boolean fire(Event event) {
+
+      if (isEmpty()) {
+        return true;
+      }
+
+      String[] selectors = bindFunctionBySelector.keySet().toArray(
+          new String[0]);
+
+      // first element where the event was fired
+      Element eventTarget = getEventTarget(event);
+      // last element where the event was dispatched on
+      Element liveContextElement = getCurrentEventTarget(event);
+
+      if (eventTarget == null || liveContextElement == null) {
+        return true;
+      }
+
+      Map<String, List<Element>> realCurrentTargetBySelector = $(eventTarget).closest(
+          selectors, liveContextElement);
+
+      // nothing match the selectors
+      if (realCurrentTargetBySelector.isEmpty()) {
+        return true;
+      }
+
+      boolean result = true;
+
+      com.google.gwt.query.client.plugins.events.Event gqEvent = com.google.gwt.query.client.plugins.events.Event.create(event);
+
+      for (String cssSelector : realCurrentTargetBySelector.keySet()) {
+        BindFunction f = bindFunctionBySelector.get(cssSelector);
+        for (Element element : realCurrentTargetBySelector.get(cssSelector)) {
+          gqEvent.setCurrentElementTarget(element);
+          boolean subResult = f.fire(gqEvent);
+          result &= subResult;
+          if (!subResult) {
+            // Event should not continue to be bubbled, break the second for
+            break;
+          }
+        }
+      }
+
+      // trick to reset the right currentTarget on the original event on ie
+      gqEvent.setCurrentElementTarget(liveContextElement);
+
+      return result;
+
+    }
+
+    /**
+     * Remove the BindFunction associated to this cssSelector
+     */
+    public BindFunction removeBindFunctionForSelector(String cssSelector) {
+      return bindFunctionBySelector.remove(cssSelector);
+    }
+
+    /**
+     * Tell if no {@link BindFunction} are linked to this object
+     * 
+     * @return
+     */
+    public boolean isEmpty() {
+      return bindFunctionBySelector.isEmpty();
+    }
+
+    @Override
+    public String toString() {
+      return "live bind function for selector "
+          + bindFunctionBySelector.keySet();
+    }
+
+    /**
+     * Return the element whose the listener fired last. It represent the
+     * context element where the {@link LiveBindFunction} was binded
+     * 
+     */
+    private Element getCurrentEventTarget(Event e) {
+      EventTarget currentEventTarget = e.getCurrentEventTarget();
+
+      if (!Element.is(currentEventTarget)) {
+        return null;
+      }
+
+      return Element.as(currentEventTarget);
+    }
+
+    /**
+     * Return the element that was the actual target of the element
+     */
+    private Element getEventTarget(Event e) {
+      EventTarget eventTarget = e.getEventTarget();
+
+      if (!Element.is(eventTarget)) {
+        return null;
+      }
+
+      return Element.as(eventTarget);
+    }
+
   }
+
   // Gwt Events class has not this event defined
   public static int ONSUBMIT = 0x08000;
 
   public static void clean(Element e) {
     EventsListener ret = getGQueryEventListener(e);
-    if (ret != null){
+    if (ret != null) {
       ret.clean();
-    } 
+    }
   }
-  
+
   public static EventsListener getInstance(Element e) {
     EventsListener ret = getGQueryEventListener(e);
     return ret != null ? ret : new EventsListener(e);
@@ -87,9 +225,9 @@ public class EventsListener implements EventListener {
 
   public static void rebind(Element e) {
     EventsListener ret = getGQueryEventListener(e);
-    if (ret != null && ret.eventBits != 0){
+    if (ret != null && ret.eventBits != 0) {
       ret.sink();
-    } 
+    }
   }
 
   private static native void cleanGQListeners(Element elem) /*-{
@@ -100,39 +238,41 @@ public class EventsListener implements EventListener {
                elem.__gqueryevent = null
 
   }-*/;
-  
+
   private static native EventsListener getGQueryEventListener(Element elem) /*-{
-    return elem.__gqueryevent;
+               return elem.__gqueryevent;
   }-*/;
-  
+
   private static native EventListener getGwtEventListener(Element elem) /*-{
-    return elem.__gwtlistener;
+               return elem.__gwtlistener;
   }-*/;
 
   private static native void setGQueryEventListener(Element elem,
       EventsListener gqevent) /*-{
-    if (elem.__gqueryevent) {
-      elem.__listener = elem.__gqueryevent;
-    } else {
-      elem.__gwtlistener = elem.__listener;
-      elem.__gqueryevent = gqevent;
-    }  
+               if (elem.__gqueryevent) {
+                       elem.__listener = elem.__gqueryevent;
+               } else {
+                       elem.__gwtlistener = elem.__listener;
+                       elem.__gqueryevent = gqevent;
+               }
   }-*/;
 
   // Gwt does't handle submit events in DOM.sinkEvents
   private static native void sinkSubmitEvent(Element elem) /*-{
-    if (elem.__gquerysubmit) return;
-    elem.__gquerysubmit = true;
-    
-    var handle = function(event) {
-      elem.__gqueryevent.@com.google.gwt.query.client.plugins.events.EventsListener::dispatchEvent(Lcom/google/gwt/user/client/Event;)(event);
-    };
-    
-    if (elem.addEventListener)
-      elem.addEventListener("submit", handle, true);
-    else
-      elem.attachEvent("onsubmit", handle);
+               if (elem.__gquerysubmit)
+                       return;
+               elem.__gquerysubmit = true;
+
+               var handle = function(event) {
+                       elem.__gqueryevent.@com.google.gwt.query.client.plugins.events.EventsListener::dispatchEvent(Lcom/google/gwt/user/client/Event;)(event);
+               };
+
+               if (elem.addEventListener)
+                       elem.addEventListener("submit", handle, true);
+               else
+                       elem.attachEvent("onsubmit", handle);
   }-*/;
+
   int eventBits = 0;
   double lastEvnt = 0;
 
@@ -140,14 +280,14 @@ public class EventsListener implements EventListener {
 
   private Element element;
 
-  private JsObjectArray<BindFunction> elementEvents = JsObjectArray
-      .createArray().cast();
-  
+  private JsObjectArray<BindFunction> elementEvents = JsObjectArray.createArray().cast();
+  private Map<String, LiveBindFunction> liveBindFunctionByEventType = new HashMap<String, LiveBindFunction>();
+
   private EventsListener(Element element) {
     this.element = element;
   }
-  
-  public void bind(int eventbits, final Object data, Function...funcs) {
+
+  public void bind(int eventbits, final Object data, Function... funcs) {
     bind(eventbits, null, data, funcs);
   }
 
@@ -155,25 +295,27 @@ public class EventsListener implements EventListener {
       int times) {
     bind(eventbits, null, data, function, times);
   }
-  
-  public void bind(int eventbits, String name, final Object data, Function...funcs) {
-    for (Function function: funcs) {
+
+  public void bind(int eventbits, String name, final Object data,
+      Function... funcs) {
+    for (Function function : funcs) {
       bind(eventbits, name, data, function, -1);
     }
   }
-  
-  public void bind(int eventbits, String namespace, final Object data, final Function function,
-      int times) {
+
+  public void bind(int eventbits, String namespace, final Object data,
+      final Function function, int times) {
     if (function == null) {
       unbind(eventbits, namespace);
       return;
     }
     eventBits |= eventbits;
     sink();
-    elementEvents.add(new BindFunction(eventbits, namespace, function, data, times));
+    elementEvents.add(new BindFunction(eventbits, namespace, function, data,
+        times));
   }
-  
-  public void bind(String event, final Object data, Function...funcs) {
+
+  public void bind(String event, final Object data, Function... funcs) {
     String nameSpace = event.replaceFirst("^[^\\.]+\\.*(.*)$", "$1");
     String eventName = event.replaceFirst("^([^\\.]+).*$", "$1");
     int b = 0;
@@ -182,12 +324,25 @@ public class EventsListener implements EventListener {
     } else {
       b = Event.getTypeInt(eventName);
     }
-    for (Function function: funcs) {
+    for (Function function : funcs) {
       bind(b, nameSpace, data, function, -1);
     }
   }
-  
+
+  public void die(String eventName, String cssSelector) {
+    if (eventName == null) {
+      for (LiveBindFunction liveBindFunction : liveBindFunctionByEventType.values()) {
+        liveBindFunction.removeBindFunctionForSelector(cssSelector);
+      }
+    } else {
+      LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(eventName);
+      liveBindFunction.removeBindFunctionForSelector(cssSelector);
+    }
+
+  }
+
   public void dispatchEvent(Event event) {
+
     int etype = "submit".equalsIgnoreCase(event.getType()) ? ONSUBMIT
         : DOM.eventGetType(event);
     for (int i = 0; i < elementEvents.length(); i++) {
@@ -200,16 +355,38 @@ public class EventsListener implements EventListener {
       }
     }
   }
-  
+
   /**
-   * Return the original gwt EventListener associated with
-   * this element, before gquery replaced it to introduce its
-   * own event handler.
+   * Return the original gwt EventListener associated with this element, before
+   * gquery replaced it to introduce its own event handler.
    */
   public EventListener getOriginalEventListener() {
     return getGwtEventListener(element);
   }
-  
+
+  public void live(String eventName, String cssSelector, Object data, Function f) {
+    int eventType = 0;
+    if ("submit".equals(eventName)) {
+      eventType = ONSUBMIT;
+    } else {
+      eventType = Event.getTypeInt(eventName);
+    }
+
+    // is a LiveBindFunction already attached for this kind of event
+    LiveBindFunction liveBindFunction = liveBindFunctionByEventType.get(eventName);
+    if (liveBindFunction == null) {
+      liveBindFunction = new LiveBindFunction(eventType, "live");
+      eventBits |= eventType;
+      sink();
+      elementEvents.add(liveBindFunction);
+      liveBindFunctionByEventType.put(eventName, liveBindFunction);
+    }
+
+    liveBindFunction.addBindFunctionForSelector(cssSelector, new BindFunction(
+        eventType, "live", f, data));
+
+  }
+
   public void onBrowserEvent(Event event) {
     // Workaround for Issue_20
     if (lastType == event.getTypeInt()
@@ -224,20 +401,20 @@ public class EventsListener implements EventListener {
     if (getOriginalEventListener() != null) {
       getOriginalEventListener().onBrowserEvent(event);
     }
-    
+
     dispatchEvent(event);
   }
-  
+
   public void unbind(int eventbits) {
     unbind(eventbits, null);
   }
-  
+
   public void unbind(int eventbits, String namespace) {
-    JsObjectArray<BindFunction> newList = JsObjectArray
-        .createArray().cast();
+    JsObjectArray<BindFunction> newList = JsObjectArray.createArray().cast();
     for (int i = 0; i < elementEvents.length(); i++) {
       BindFunction listener = elementEvents.get(i);
-      boolean matchNS = namespace == null || namespace.isEmpty() || listener.nameSpace.equals(namespace);
+      boolean matchNS = namespace == null || namespace.isEmpty()
+          || listener.nameSpace.equals(namespace);
       boolean matchEV = eventbits <= 0 || listener.hasEventType(eventbits);
       if (matchNS && matchEV) {
         continue;
@@ -246,7 +423,7 @@ public class EventsListener implements EventListener {
     }
     elementEvents = newList;
   }
-  
+
   public void unbind(String event) {
     String nameSpace = event.replaceFirst("^[^\\.]+\\.*(.*)$", "$1");
     String eventName = event.replaceFirst("^([^\\.]+).*$", "$1");
@@ -258,24 +435,26 @@ public class EventsListener implements EventListener {
     }
     unbind(b, nameSpace);
   }
-  
-  private void clean(){
+
+  private void clean() {
     cleanGQListeners(element);
-    elementEvents =  JsObjectArray.createArray().cast();
+    elementEvents = JsObjectArray.createArray().cast();
+    liveBindFunctionByEventType = new HashMap<String, LiveBindFunction>();
   }
-  
+
   private void sink() {
     setGQueryEventListener(element, this);
-    DOM.setEventListener((com.google.gwt.user.client.Element)element, this);
+    DOM.setEventListener((com.google.gwt.user.client.Element) element, this);
     if (eventBits == ONSUBMIT) {
       sinkSubmitEvent(element);
     } else {
-      if ((eventBits | Event.FOCUSEVENTS) == Event.FOCUSEVENTS && element.getAttribute("tabIndex").length() == 0) {
+      if ((eventBits | Event.FOCUSEVENTS) == Event.FOCUSEVENTS
+          && element.getAttribute("tabIndex").length() == 0) {
         element.setAttribute("tabIndex", "0");
       }
       DOM.sinkEvents((com.google.gwt.user.client.Element) element, eventBits
           | DOM.getEventsSunk((com.google.gwt.user.client.Element) element));
-      
+
     }
   }
 }
index 5d1d7ddfec43aa8c265595d7a9f2bc35e3654434..ed8d3b80619f0dbee3e503ccc6f7df43f31f14b5 100644 (file)
@@ -1201,18 +1201,28 @@ public class GQueryCoreTest extends GWTTestCase {
   
   public void testClosestMethodWithArrayOfString(){
     
-    String html = "<div id='mainDiv' class='test'><p><div id='firstDiv'><p id='firstP'><span><input id='firstInput' type='text'></input></span></p></div></p></div>";
+    String html = "<div id='mainDiv'><div id='subDiv' class='test'><div id='subSubDiv'><p id='mainP'><span id='testSpan' class='test'><input id='firstInput' type='text'></input></span></p></div></div></div>";
     $(e).html(html);
     
-    Map<String, Element> close = $("input", e).closest(new String[]{"p","div", ".test", "#unknown"});
+    Map<String, List<Element>> close = $("input", e).closest(new String[]{"p","div", ".test", "#unknown"});
     
     assertEquals(3, close.size());
+    
     assertNotNull(close.get("p"));
-    assertEquals("firstP", close.get("p").getId());
+    assertEquals(1,close.get("p").size());
+    assertEquals("mainP", close.get("p").get(0).getId());
+    
     assertNotNull(close.get("div"));
-    assertEquals("firstDiv", close.get("div").getId());
+    assertEquals(3,close.get("div").size());
+    assertEquals("subSubDiv", close.get("div").get(0).getId());
+    assertEquals("subDiv", close.get("div").get(1).getId());
+    assertEquals("mainDiv", close.get("div").get(2).getId());
+    
     assertNotNull(close.get(".test"));
-    assertEquals("mainDiv", close.get(".test").getId());
+    assertEquals(2,close.get(".test").size());
+    assertEquals("testSpan", close.get(".test").get(0).getId());
+    assertEquals("subDiv", close.get(".test").get(1).getId());
+    
     assertNull(close.get("#unknown"));
     
   }
index eb4a9e80757d86b68127ee738259176c9be3b9b5..99515ec3b91bd33a07a946d3ff6796120f3cfa3e 100644 (file)
@@ -58,10 +58,90 @@ public class GQueryEventsTest extends GWTTestCase {
       e = testPanel.getElement();
       e.setId("evnt-tst");
     } else {
+      EventsListener.clean(e);
       e.setInnerHTML("");
     }
   } 
   
+  
+  public void testDie() {
+    $(e).html("<div id='div1'>content</div>");
+    $(".clickMe", e).live("click", new Function(){
+      public void f(Element e) {
+        $(e).css(CSS.COLOR.with(RGBColor.RED));
+      }
+    });
+    
+    $(".clickMe", e).live("dblclick", new Function(){
+      public void f(Element e) {
+        $(e).css(CSS.COLOR.with(RGBColor.BLUE));
+      }
+    });
+    
+    $("#div1",e).addClass("clickMe");
+    
+    $("#div1",e).click();
+    assertEquals(RGBColor.RED.getCssName(), $("#div1", e).css(CSS.COLOR));
+    
+    $("#div1",e).dblclick();
+    assertEquals(RGBColor.BLUE.getCssName(), $("#div1", e).css(CSS.COLOR));
+    
+    //reset
+    $("#div1",e).css(CSS.COLOR.with(RGBColor.BLACK));
+    
+    $(".clickMe", e).die("click");
+    $("#div1",e).click();
+    assertEquals(RGBColor.BLACK.getCssName(), $("#div1", e).css(CSS.COLOR));
+    
+    $("#div1",e).dblclick();
+    assertEquals(RGBColor.BLUE.getCssName(), $("#div1", e).css(CSS.COLOR));
+    
+    //reset
+    $("#div1",e).css(CSS.COLOR.with(RGBColor.BLACK));
+    
+    $(".clickMe", e).die("dblclick");
+    
+    $("#div1",e).dblclick();
+    assertEquals(RGBColor.BLACK.getCssName(), $("#div1", e).css(CSS.COLOR));
+    
+  }
+  
+  public void testDie2() {
+    $(e).html("<div id='div1'>content</div>");
+    $(".clickMe", e).live("click", new Function(){
+      public void f(Element e) {
+        $(e).css(CSS.COLOR.with(RGBColor.RED));
+      }
+    });
+    
+    $(".clickMe", e).live("dblclick", new Function(){
+      public void f(Element e) {
+        $(e).css(CSS.COLOR.with(RGBColor.BLUE));
+      }
+    });
+    
+    $("#div1",e).addClass("clickMe");
+    
+    $("#div1",e).click();
+    assertEquals(RGBColor.RED.getCssName(), $("#div1", e).css(CSS.COLOR));
+    
+    $("#div1",e).dblclick();
+    assertEquals(RGBColor.BLUE.getCssName(), $("#div1", e).css(CSS.COLOR));
+    
+    //reset
+    $("#div1",e).css(CSS.COLOR.with(RGBColor.BLACK));
+    
+    $(".clickMe", e).die();
+
+    $("#div1",e).click();
+    assertEquals(RGBColor.BLACK.getCssName(), $("#div1", e).css(CSS.COLOR));
+
+    $("#div1",e).dblclick();
+    assertEquals(RGBColor.BLACK.getCssName(), $("#div1", e).css(CSS.COLOR));
+    
+  }
+
   /**
    * TODO: DblClick doesn't work with HtmlUnit, investigate and report.
    */
@@ -77,7 +157,7 @@ public class GQueryEventsTest extends GWTTestCase {
     $("p", e).dblclick();
     assertEquals("yellow", $("p", e).css("color"));
   }
-
+  
   public void testEventsPlugin() {
     $(e).html("<p>Content</p>");
 
@@ -184,30 +264,6 @@ public class GQueryEventsTest extends GWTTestCase {
     $("input", e).keyup('c');
     assertEquals("abc", $("input", e).val());
   }
-
-  public void testLive() {
-    $(e).html("<div class='clickMe'>Content 1</div>");
-    $(".clickMe").live(Event.ONCLICK, new Function(){
-      public void f(Element e) {
-        $(e).css("color", "red");
-      }
-    });
-    $(e).append("<p class='clickMe'>Content 2</p>");
-    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("<span class='clickMe'>Content 3</span>");
-    
-    $(".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));
@@ -222,6 +278,92 @@ public class GQueryEventsTest extends GWTTestCase {
     assertEquals("black", $(e).css("color"));
   }
 
+  public void testLive() {
+    $(e).html("<div id='div1' class='clickMe'><div id='div2'>Content 1<span id='span1'> blop</span></div></div>");
+    $(".clickMe", e).live("click", new Function(){
+      public void f(Element el) {
+        $(el).css("color", "red");
+      }
+    });
+    
+    $(e).append("<div id='div3' class='clickMe'>Content 2 <div id='div4'><span id='span2'>blop</span></div></div>");
+
+    $(".clickMe", e).click();
+    assertEquals("red", $("#div1", e).css("color"));
+    assertEquals("red", $("#div3", e).css("color"));
+    
+    //reset
+    $("*", e).css(CSS.COLOR.with(RGBColor.BLACK));
+    assertEquals("black", $("div", e).css("color"));
+    assertEquals("black", $("span", e).css("color"));
+
+    $("#span1", e).click();
+    assertEquals("red", $("#div1", e).css("color"));
+    assertEquals("black", $("#div3", e).css("color"));
+    
+     //reset
+    $("*", e).css(CSS.COLOR.with(RGBColor.BLACK));
+    
+    $("#span2", e).click();
+    assertEquals("black", $("#div1", e).css("color"));
+    assertEquals("red", $("#div3", e).css("color"));
+    
+    //reset
+    $("*", e).css(CSS.COLOR.with(RGBColor.BLACK));
+    
+    $("#div2, #div4", e).addClass("clickMe");
+    
+    $("#span1", e).click();
+    assertEquals("red", $("#div1", e).css("color"));
+    assertEquals("red", $("#div2", e).css("color"));
+    assertEquals("black", $("#div3", e).css("color"));
+    assertEquals("black", $("#div4", e).css("color"));
+
+  //reset
+    $("*", e).css(CSS.COLOR.with(RGBColor.BLACK));
+          
+    $("#span2", e).click();
+    assertEquals("black", $("#div1", e).css("color"));
+    assertEquals("black", $("#div2", e).css("color"));
+    assertEquals("red", $("#div3", e).css("color"));
+    assertEquals("red", $("#div4", e).css("color"));
+
+  }
+
+  public void testLive2() {
+    
+    $(e).html("<div id='div1'><div id='div2'>Content 1<span id='span1'> blop</span></div></div>");
+    
+    $(".clickable", e).live("click", new Function(){
+      public void f(Element e) {
+        $(e).css(CSS.COLOR.with(RGBColor.RED));
+      }
+    });
+    
+    $(".clickable2", e).live("click", new Function(){
+      public void f(Element e) {
+        $(e).css(CSS.COLOR.with(RGBColor.BLUE));
+      }
+    });
+    
+    $(".hover", e).live("mouseover", new Function(){
+      public void f(Element e) {
+        $(e).css(CSS.BACKGROUND_COLOR.with(RGBColor.YELLOW));
+      }
+    });
+    
+    $("#div1", e).addClass("clickable");
+    $("#div2", e).addClass("clickable2", "hover");
+    
+    $("#span1", e).click();
+    
+    assertEquals("red", $("#div1", e).css(CSS.COLOR));
+    assertEquals("blue", $("#div2", e).css(CSS.COLOR));
+    assertNotSame("yellow", $("#div2", e).css(CSS.BACKGROUND_COLOR));
+    
+    
+  }
+
   public void testNamedBinding() {
     $(e).html("<p>Content</p>");
 
@@ -269,6 +411,18 @@ public class GQueryEventsTest extends GWTTestCase {
     assertEquals(12.0d, $("p", e).cur("fontSize", true));
   }
 
+  public void testRebind() {
+    final GQuery b = $("<p>content</p>");
+    b.click(new Function() {
+      public void f(Element e){
+        b.css(CSS.COLOR.with(RGBColor.RED));
+      }
+    });
+    $(e).append(b);
+    b.click();
+    assertEquals("red", $(b).css("color"));
+  }
+  
   public void testSubmitEvent() {
     // Add a form and an iframe to the dom. The form target is the iframe
     $(e).html("<form action='whatever' target='miframe'><input type='text' value='Hello'><input type='submit' value='Go'></form><iframe name='miframe' id='miframe' src=\"javascript:''\">");
@@ -302,39 +456,23 @@ public class GQueryEventsTest extends GWTTestCase {
       }
     }.schedule(500);
   }
-
-  public void testWidgetEvents() {
-    final Button b = new Button("click-me");
-    b.addClickHandler(new ClickHandler() {
-      public void onClick(ClickEvent event) {
-        b.getElement().getStyle().setBackgroundColor("black");
-      }
-    });
-    RootPanel.get().add(b);
-    $(b).click(lazy().css(CSS.COLOR.with(RGBColor.RED)).done());
-
-    $(b).click();
-    assertEquals("red", $("button").css("color"));
-    assertEquals("black", $("button").css("background-color"));
-    RootPanel.get().remove(b);
+  
+  /**
+   * Test for issue 62
+   * http://code.google.com/p/gwtquery/issues/detail?id=62
+   */
+  public void testTabInbexInFocusEventBinding(){
+    String content="<div id='test'>test content</div>";
+    $(e).html(content);
+    $("#test").focus(new Function(){});
     
-    $(e).append($(b));
-    $(b).css(CSS.COLOR.with(RGBColor.YELLOW), CSS.BACKGROUND_COLOR.with(RGBColor.BLUE));
-    $(b).click();
-    assertEquals("red", $("button").css("color"));
-    assertEquals("black", $("button").css("background-color"));
-  }
-
-  public void testRebind() {
-    final GQuery b = $("<p>content</p>");
-    b.click(new Function() {
-      public void f(Element e){
-        b.css(CSS.COLOR.with(RGBColor.RED));
-      }
-    });
-    $(e).append(b);
-    b.click();
-    assertEquals("red", $(b).css("color"));
+    assertEquals($("#test").attr("tabIndex"), "0");
+    
+    content="<div id='test' tabIndex='2'>test content</div>";
+    $(e).html(content);
+    $("#test").focus(new Function(){});
+    
+    assertEquals($("#test").attr("tabIndex"), "2");
   }
   
   public void testUnbindMultipleEvents() {
@@ -361,23 +499,27 @@ public class GQueryEventsTest extends GWTTestCase {
     $(document).trigger(Event.ONMOUSEUP);
     assertEquals("black", $("p").css("color"));
   }
-  
-  /**
-   * Test for issue 62
-   * http://code.google.com/p/gwtquery/issues/detail?id=62
-   */
-  public void testTabInbexInFocusEventBinding(){
-    String content="<div id='test'>test content</div>";
-    $(e).html(content);
-    $("#test").focus(new Function(){});
-    
-    assertEquals($("#test").attr("tabIndex"), "0");
-    
-    content="<div id='test' tabIndex='2'>test content</div>";
-    $(e).html(content);
-    $("#test").focus(new Function(){});
+
+  public void testWidgetEvents() {
+    final Button b = new Button("click-me");
+    b.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        b.getElement().getStyle().setBackgroundColor("black");
+      }
+    });
+    RootPanel.get().add(b);
+    $(b).click(lazy().css(CSS.COLOR.with(RGBColor.RED)).done());
+
+    $(b).click();
+    assertEquals("red", $("button").css("color"));
+    assertEquals("black", $("button").css("background-color"));
+    RootPanel.get().remove(b);
     
-    assertEquals($("#test").attr("tabIndex"), "2");
+    $(e).append($(b));
+    $(b).css(CSS.COLOR.with(RGBColor.YELLOW), CSS.BACKGROUND_COLOR.with(RGBColor.BLUE));
+    $(b).click();
+    assertEquals("red", $("button").css("color"));
+    assertEquals("black", $("button").css("background-color"));
   }
 
 }