From 05167d6898ba2135d1fab5f9f7f0ae4bcd07ffa0 Mon Sep 17 00:00:00 2001 From: Julien Dramaix Date: Mon, 28 Mar 2011 21:13:39 +0000 Subject: [PATCH] add detach method. refactoring of the remove() method to be compliant with jQuery documentation add test for remove and detach methods. Don't try to clean GQuery events if no one EventListener exists + correct bug in event cleaning --- .../com/google/gwt/query/client/GQuery.java | 101 +++++++++++----- .../google/gwt/query/client/LazyGQuery.java | 24 ++++ .../client/plugins/events/EventsListener.java | 78 +++++++------ .../gwt/query/client/GQueryCoreTest.java | 110 ++++++++++++++++++ 4 files changed, 250 insertions(+), 63 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 e66cc950..9eee6b24 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 @@ -1064,9 +1064,9 @@ public class GQuery implements Lazy { } else { Node c = e.getFirstChild(); while (c != null) { - removeData(c.cast(), null); + removeData(c. cast(), null); GqUi.detachWidget(getAssociatedWidget(e)); - EventsListener.getInstance(c.cast()).clean(); + EventsListener.clean(c. cast()); e.removeChild(c); c = e.getFirstChild(); } @@ -1165,26 +1165,26 @@ public class GQuery implements Lazy { * filters at once. */ public GQuery filter(String... filters) { - + JsNodeArray array = JsNodeArray.create(); - + for (String f : filters) { for (Element e : elements()) { boolean ghostParent = false; - - if (e.getParentNode() == null){ + + if (e.getParentNode() == null) { DOM.createDiv().appendChild(e); ghostParent = true; } - + for (Element c : $(f, e.getParentNode()).elements()) { if (c == e) { array.addNode(c); break; } } - - if(ghostParent){ + + if (ghostParent) { e.removeFromParent(); } } @@ -1240,6 +1240,7 @@ public class GQuery implements Lazy { * negative index is counted from the end of the matched set. * * Example: + * *
    *  $("div").get(0) will return the first matched div
    *  $("div").get(1) will return the second matched div
@@ -1562,7 +1563,6 @@ 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);
@@ -1986,26 +1986,62 @@ public class GQuery implements Lazy {
    * Removes all matched elements from the DOM.
    */
   public GQuery remove() {
-    for (Element e : elements()) {
-      Widget w = getAssociatedWidget(e);
-      if (w != null) {
-        w.removeFromParent();
-      } else {
-        e.removeFromParent();
-      }
-    }
-    return this;
+    return remove(null, true);
+  }
+
+  /**
+   * Removes from the DOM all matched elements filtered by the
+   * filter.
+   */
+  public GQuery remove(String filter) {
+    return remove(filter, true);
   }
 
   /**
-   * Removes all matched elements from the DOM and cleans their data and bound events.
+   * Detach all matched elements from the DOM. This method is the same than
+   * {@link #remove()} method except all data and event handlers are not remove
+   * from the element. This method is useful when removed elements are to be
+   * reinserted into the DOM at a later time.
    */
-  public GQuery remove(boolean clean) {
+  public GQuery detach() {
+    return remove(null, false);
+  }
+
+  /**
+   * Detach from the DOM all matched elements filtered by the
+   * filter.. This method is the same than {@link #remove(String)}
+   * method except all data and event handlers are not remove from the element.
+   * This method is useful when removed elements are to be reinserted into the
+   * DOM at a later time.
+   */
+  public GQuery detach(String filter) {
+    return remove(filter, false);
+  }
+
+  /**
+   * Removes all matched elements from the DOM and cleans their data and bound
+   * events if the value of clean parameter is set to true. The
+   *  filter parameter allows to filter the matched set to remove.
+   */
+  protected GQuery remove(String filter, boolean clean) {
+     
     for (Element e : elements()) {
-      EventsListener.getInstance(e).clean();
-      removeData(e, null);
+      if (filter == null || $(e).filter(filter).length() == 1) {
+        if (clean) {
+          //clean data linked to the children
+          cleanGQData($(e.getElementsByTagName("*")).elements());
+          //clean data linked to the element itself
+          cleanGQData(e);
+        }
+        Widget w = getAssociatedWidget(e);
+        if (w != null) {
+          w.removeFromParent();
+        } else {
+          e.removeFromParent();
+        }
+      }
     }
-    remove();
+
     return this;
   }
 
@@ -2877,10 +2913,10 @@ public class GQuery implements Lazy {
       // e.getOwnerDocument();
       if (e.getNodeType() == Node.DOCUMENT_NODE) {
         e = e. cast().getBody();
-      }  
+      }
       for (int j = 0; j < g.size(); j++) {
-//        Widget w = getAssociatedWidget(g.get(j));
-//        GqUi.detachWidget(w);
+        // Widget w = getAssociatedWidget(g.get(j));
+        // GqUi.detachWidget(w);
         Node n = g.get(j);
         if (g.size() > 1) {
           n = n.cloneNode(true);
@@ -2900,8 +2936,8 @@ public class GQuery implements Lazy {
             newNodes.addNode(e.getParentNode().insertBefore(n, e));
             break;
         }
-        EventsListener.getInstance(n.cast()).rebind();
-//        GqUi.attachWidget(w);
+        EventsListener.getInstance(n. cast()).rebind();
+        // GqUi.attachWidget(w);
       }
     }
     if (newNodes.size() > g.size()) {
@@ -2959,4 +2995,11 @@ public class GQuery implements Lazy {
       dataCache.delete(id);
     }
   }
+  
+  private void cleanGQData(Element... elements){
+    for (Element el : elements){
+      EventsListener.clean(el);
+      removeData(el, null);
+    }
+  }
 }
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 98d3d035..ef43e69d 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
@@ -523,6 +523,7 @@ public interface LazyGQuery extends LazyBase{
    * negative index is counted from the end of the matched set.
    * 
    * Example:
+   * 
    * 
    *  $("div").get(0) will return the first matched div
    *  $("div").get(1) will return the second matched div
@@ -965,6 +966,29 @@ public interface LazyGQuery extends LazyBase{
    */
   LazyGQuery remove();
 
+  /**
+   * Removes from the DOM all matched elements filtered by the
+   * filter.
+   */
+  LazyGQuery remove(String filter);
+
+  /**
+   * Detach all matched elements from the DOM. This method is the same than
+   * {@link #remove()} method except all data and event handlers are not remove
+   * from the element. This method is useful when removed elements are to be
+   * reinserted into the DOM at a later time.
+   */
+  LazyGQuery detach();
+
+  /**
+   * Detach from the DOM all matched elements filtered by the
+   * filter.. This method is the same than {@link #remove(String)}
+   * method except all data and event handlers are not remove from the element.
+   * This method is useful when removed elements are to be reinserted into the
+   * DOM at a later time.
+   */
+  LazyGQuery detach(String filter);
+
   /**
    * Remove the named attribute from every element in the matched set.
    */
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java
index 9bbae7ce..3903794d 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java
@@ -73,25 +73,35 @@ public class EventsListener implements EventListener {
   // 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){
+      ret.clean();
+    } 
+  }
+
   public static EventsListener getInstance(Element e) {
     EventsListener ret = getGQueryEventListener(e);
     return ret != null ? ret : new EventsListener(e);
   }
 
+  private static native void cleanGQListeners(Element elem) /*-{
+		if (elem.__gwtlistener) {
+			elem.__listener = elem.__gwtlistener;
+		}
+		elem.__gquerysubmit = null;
+		elem.__gqueryevent = null
+
+  }-*/;
+  
   private static native EventsListener getGQueryEventListener(Element elem) /*-{
     return elem.__gqueryevent;
   }-*/;
-
+  
   private static native EventListener getGwtEventListener(Element elem) /*-{
     return elem.__gwtlistener;
   }-*/;
-  
-  private static native void cleanGQListeners() /*-{
-    elem.__listener = elem.__gwtlistener;
-    elem.__gquerysubmit = null;
-    elem.__gqueryevent = null;
-  }-*/;
-  
+
   private static native void setGQueryEventListener(Element elem,
       EventsListener gqevent) /*-{
     if (elem.__gqueryevent) {
@@ -116,16 +126,16 @@ public class EventsListener implements EventListener {
     else
       elem.attachEvent("onsubmit", handle);
   }-*/;
-
+  int eventBits = 0;
   double lastEvnt = 0;
+
   int lastType = 0;
-  int eventBits = 0;
 
   private Element element;
 
   private JsObjectArray elementEvents = JsObjectArray
       .createArray().cast();
-
+  
   private EventsListener(Element element) {
     this.element = element;
   }
@@ -133,12 +143,12 @@ public class EventsListener implements EventListener {
   public void bind(int eventbits, final Object data, Function...funcs) {
     bind(eventbits, null, data, funcs);
   }
-  
+
   public void bind(int eventbits, final Object data, final Function function,
       int times) {
     bind(eventbits, null, data, function, times);
   }
-
+  
   public void bind(int eventbits, String name, final Object data, Function...funcs) {
     for (Function function: funcs) {
       bind(eventbits, name, data, function, -1);
@@ -170,21 +180,6 @@ public class EventsListener implements EventListener {
     }
   }
   
-  private void sink() {
-    setGQueryEventListener(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) {
-        element.setAttribute("tabIndex", "0");
-      }
-      DOM.sinkEvents((com.google.gwt.user.client.Element) element, eventBits
-          | DOM.getEventsSunk((com.google.gwt.user.client.Element) element));
-      
-    }
-  }
-  
   public void dispatchEvent(Event event) {
     int etype = "submit".equalsIgnoreCase(event.getType()) ? ONSUBMIT
         : DOM.eventGetType(event);
@@ -226,6 +221,10 @@ public class EventsListener implements EventListener {
     dispatchEvent(event);
   }
   
+  public void rebind() {
+    sink();
+  }
+  
   public void unbind(int eventbits) {
     unbind(eventbits, null);
   }
@@ -257,12 +256,23 @@ public class EventsListener implements EventListener {
     unbind(b, nameSpace);
   }
   
-  public void rebind() {
-    sink();
-  }
-
-  public void clean() {
-    cleanGQListeners();
+  private void clean(){
+    cleanGQListeners(element);
     elementEvents =  JsObjectArray.createArray().cast();
   }
+  
+  private void sink() {
+    setGQueryEventListener(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) {
+        element.setAttribute("tabIndex", "0");
+      }
+      DOM.sinkEvents((com.google.gwt.user.client.Element) element, eventBits
+          | DOM.getEventsSunk((com.google.gwt.user.client.Element) element));
+      
+    }
+  }
 }
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 21365a01..1a53a88b 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
@@ -978,5 +978,115 @@ public class GQueryCoreTest extends GWTTestCase {
     Assert.assertNull($((String) null).get(-1));
     Assert.assertEquals(0, $((String) null).eq(0).size());
   }
+  
+  public void testRemoveMethod(){
+    String html = "
parent
child
"; + $(e).html(html); + + Function failCallback = new Function(){ + @Override + public void f() { + fail("Event binding not removed"); + } + }; + + Element parent = $("#parent", e).get(0); + Element child = $("#child", e).get(0); + + $("#child", e).data("key", "child"); + $("#child", e).click(failCallback); + $("#parent", e).data("key", "parent"); + $("#parent", e).click(failCallback); + + $("#parent", e).remove(); + + assertNull($(child).data("key")); + assertNull($(parent).data("key")); + //if failCallback is always binded, test fails... + $(child).click(); + $(parent).click(); + + + + } + + public void testRemoveMethodWithFilter(){ + String html = "
parent
child
"; + $(e).html(html); + + Function failCallback = new Function(){ + @Override + public void f() { + fail("Event binding not removed"); + } + }; + + Function noFailCallback = new Function(){ + @Override + public void f(Element e) { + $(e).css(CSS.BACKGROUND_COLOR.with(RGBColor.RED)); + } + }; + + Element parent = $("#parent", e).get(0); + Element child = $("#child", e).get(0); + + $("#child", e).data("key", "child"); + $("#child", e).click(failCallback); + $("#parent", e).data("key", "parent"); + $("#parent", e).click(noFailCallback); + + $("div", e).remove("#child"); + + assertNull($(child).data("key")); + assertEquals("parent",$(parent).data("key")); + + //if failCallback is always binded, test fails... + $(child).click(); + + + $(parent).click(); + assertEquals("red", $(parent).css(CSS.BACKGROUND_COLOR)); + + + } + + public void testDetachMethod(){ + String html = "
parent
child
"; + $(e).html(html); + + Function noFailCallback = new Function(){ + @Override + public void f(Element e) { + $(e).css(CSS.BACKGROUND_COLOR.with(RGBColor.RED)); + } + }; + + Element parent = $("#parent", e).get(0); + Element child = $("#child", e).get(0); + + $("#child", e).data("key", "child"); + $("#child", e).click(noFailCallback); + $("#parent", e).data("key", "parent"); + $("#parent", e).click(noFailCallback); + + GQuery $parent = $("#parent", e).detach(); + + assertEquals("child",$(child).data("key")); + assertEquals("parent",$(parent).data("key")); + + $(e).append($parent); + + assertEquals("child",$("#child", e).data("key")); + assertEquals("parent",$("#parent", e).data("key")); + + $("#child", e).click(); + assertEquals("red", $(child).css(CSS.BACKGROUND_COLOR)); + $("#parent", e).click(); + assertEquals("red", $(parent).css(CSS.BACKGROUND_COLOR)); + + + + } } -- 2.39.5