]> source.dussan.org Git - gwtquery.git/commitdiff
add closest methods !
authorJulien Dramaix <julien.dramaix@gmail.com>
Wed, 6 Apr 2011 21:50:43 +0000 (21:50 +0000)
committerJulien Dramaix <julien.dramaix@gmail.com>
Wed, 6 Apr 2011 21:50:43 +0000 (21:50 +0000)
gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java
gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTest.java

index 7bbf280f0a0710666dd73cc60fd5aa2c0dc9fdfa..1491ea11f2191c1e9863c0ffeddfdfed15893bb3 100644 (file)
@@ -60,6 +60,7 @@ import java.util.ArrayList;
 import java.util.Arrays;\r
 import java.util.HashMap;\r
 import java.util.List;\r
+import java.util.Map;\r
 \r
 /**\r
  * GwtQuery is a GWT clone of the popular jQuery library.\r
@@ -121,6 +122,9 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
   private static DocumentStyleImpl styleImpl = GWT.create(DocumentStyleImpl.class);\r
 \r
   private static Element windowData = null;\r
+  \r
+  //Sizzle POS regex : usefull in some methods\r
+  private static final String POS_REGEX = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^\\-]|$)";\r
 \r
   /**\r
    * Create an empty GQuery object.\r
@@ -197,7 +201,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
    * reference to a plugin to be used.\r
    */\r
   public static <T extends GQuery> T $(String selector, Class<T> plugin) {\r
-    return $(selector, (Node) null, plugin);\r
+    return $(selector, document, plugin);\r
   }\r
 \r
   /**\r
@@ -216,7 +220,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
           : ctx.getOwnerDocument();\r
       return $(cleanHtmlString(selectorOrHtml, doc));\r
     }\r
-    return new GQuery(select(selectorOrHtml, ctx)).setSelector(selectorOrHtml);\r
+    return new GQuery().select(selectorOrHtml, ctx);\r
   }\r
 \r
   /**\r
@@ -231,7 +235,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
     try {\r
       if (plugins != null) {\r
         T gquery = (T) plugins.get(plugin).init(\r
-            new GQuery(select(selector, context))).setSelector(selector);\r
+            new GQuery().select(selector, context));\r
         return gquery;\r
       }\r
       throw new RuntimeException("No plugin for class " + plugin);\r
@@ -348,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
@@ -450,13 +454,18 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
                        n.scrollIntoView()\r
   }-*/;\r
 \r
-  private static NodeList<Element> select(String selector, Node context) {\r
+  private GQuery select(String selector, Node context) {\r
     if (engine == null) {\r
       engine = new SelectorEngine();\r
     }\r
     NodeList<Element> n = engine.select(selector, context);\r
     JsNodeArray res = copyNodeList(n);\r
-    return res;\r
+    \r
+    currentSelector = selector;\r
+    currentContext = context != null ? context  : document;\r
+    \r
+    return setArray(res);\r
+\r
   }\r
 \r
   private static native Element window() /*-{\r
@@ -464,6 +473,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
   }-*/;\r
 \r
   protected String currentSelector;\r
+  protected Node currentContext;\r
 \r
   private NodeList<Element> elements = JavaScriptObject.createArray().cast();\r
 \r
@@ -471,6 +481,8 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
 \r
   protected GQuery(GQuery gq) {\r
     this(gq == null ? null : gq.get());\r
+    currentSelector = gq.getSelector();\r
+    currentContext = gq.getContext();\r
   }\r
 \r
   private GQuery() {\r
@@ -772,7 +784,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
     } else if (plugins != null) {\r
       Plugin<?> p = plugins.get(plugin);\r
       if (p != null) {\r
-        return (T) p.init(this).setSelector(currentSelector);\r
+        return (T) p.init(this);\r
       }\r
     }\r
     throw new RuntimeException("No plugin registered for class "\r
@@ -957,8 +969,120 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
     for (Element e : elements()) {\r
       result.addNode(e.cloneNode(true));\r
     }\r
-    return new GQuery(result).as(Events).addLiveEvents();\r
+    GQuery ret = new GQuery(result);\r
+    ret.currentContext = currentContext;\r
+    ret.currentSelector = currentSelector;\r
+    return ret;\r
+  }\r
+  \r
+  /**\r
+   * Get the first ancestor element that matches the selector (for each matched element), beginning at the\r
+   * current element and progressing up through the DOM tree.\r
+   * \r
+   * @param selector\r
+   * @return\r
+   */\r
+  public GQuery closest(String selector){\r
+    return closest(selector, null);\r
+  }\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
+   * \r
+   * @param selector\r
+   * @return\r
+   */\r
+  public Map<String, Element> closest(String[] selectors){\r
+    return closest(selectors, null);\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
+   * \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
+      context = currentContext;\r
+    }\r
+    \r
+    Element first = get(0);\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
+      }\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
+          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
+        }\r
+        \r
+        current = current.getParentElement();  \r
+      }\r
+      \r
+      \r
+    }\r
+    \r
+      return results;\r
+  }\r
+  \r
+  /**\r
+   * Get the first ancestor element that matches the selector (for each matched element), beginning at the\r
+   * current element and progressing up through the DOM tree until reach the <code>context</code> node.\r
+   * \r
+   * If no context is passed in then the context of the gQuery object will be used instead.\r
+   *\r
+   */\r
+  public GQuery closest(String selector, Node context){\r
+    assert selector != null;\r
+    \r
+    if (context == null){\r
+      context = currentContext;\r
+    }\r
+    \r
+    GQuery pos = selector.matches(POS_REGEX) ? $(selector, context) : null;\r
+    JsNodeArray result = JsNodeArray.create();\r
+    \r
+    for (Element e : elements()){\r
+      Element current = e;\r
+      while (current != null && current.getOwnerDocument() != null && current != context){\r
+        boolean match = pos != null ? pos.index(current) > -1 : $(current).is(selector);\r
+        if (match){\r
+          result.addNode(current);\r
+          break;\r
+        }else{\r
+          current = current.getParentElement();\r
+        }       \r
+      }\r
+    }\r
+    \r
+    return $(unique(result));\r
+    \r
+  } \r
 \r
   /**\r
    * Filter the set of elements to those that contain the specified text.\r
@@ -1524,6 +1648,10 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
     }\r
     return null;\r
   }\r
+  \r
+  public Node getContext() {\r
+    return currentContext;\r
+  }\r
 \r
   /**\r
    * Return the previous set of matched elements prior to the last destructive\r
@@ -3277,6 +3405,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> {
     GQuery g = new GQuery(elts);\r
     g.setPreviousObject(this);\r
     g.setSelector(selector);\r
+    g.currentContext = currentContext;\r
     return g;\r
   }\r
 \r
index b3f04364f9e9f4a6a5822148d212b4a66d79bf50..278ab826d69cbd605e5d2d030edd027b8a655ea0 100644 (file)
@@ -43,6 +43,7 @@ import com.google.gwt.user.client.ui.TextArea;
 import junit.framework.Assert;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Test class for testing gwtquery-core api.
@@ -899,7 +900,7 @@ public class GQueryCoreTest extends GWTTestCase {
     Button b2 = g.widget();
     assertEquals(b1, b2);
 
-    b2 = $("<button>Click-me</button>").appendTo(document).as(Widgets).button().widget();
+    b2 = $("<button>Click-me</button>").appendTo(document).as(Widgets).widget();
     b2.addClickHandler(new ClickHandler() {
       public void onClick(ClickEvent event) {
         $(b1).css("color", "red");
@@ -1163,5 +1164,57 @@ public class GQueryCoreTest extends GWTTestCase {
     assertEquals(expectedHtml, $(e).html());
     
   }
+  
+  public void testClosestMethod(){
+    String html = "<div><p><div id='firstDiv'><p id='firstP'><span><input id='firstInput' type='text'></input></span></p></div></p></div>";
+    $(e).html(html);
+    
+    GQuery closeP = $("input", e).closest("p,div");
+    
+    assertEquals(1, closeP.length());
+    assertEquals("firstP", closeP.get(0).getId());
+    
+    GQuery closeDiv = $("input", e).closest("div");
+    
+    assertEquals(1, closeDiv.length());
+    assertEquals("firstDiv", closeDiv.get(0).getId());
+    
+    GQuery closeInput = $("input", e).closest("input");
+    
+    assertEquals(1, closeInput.length());
+    assertEquals("firstInput", closeInput.get(0).getId());
+    
+    GQuery closeUnknown = $("input", e).closest("h1");
+    
+    assertEquals(0, closeUnknown.length());
+    
+    GQuery closePWithContext = $("input", e).closest("p,div",$("#firstDiv").get(0));
+    
+    assertEquals(1, closePWithContext.length());
+    assertEquals("firstP", closePWithContext.get(0).getId());
+    
+    GQuery closeDivWithContext = $("input", e).closest("div",$("#firstP").get(0));
+    
+    assertEquals(0, closeDivWithContext.length()); 
+    
+  }
+  
+  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>";
+    $(e).html(html);
+    
+    Map<String, 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());
+    assertNotNull(close.get("div"));
+    assertEquals("firstDiv", close.get("div").getId());
+    assertNotNull(close.get(".test"));
+    assertEquals("mainDiv", close.get(".test").getId());
+    assertNull(close.get("#unknown"));
+    
+  }
  
 }