From 9ff9faa965f122c4674e861d52aab58102be8043 Mon Sep 17 00:00:00 2001 From: Julien Dramaix Date: Wed, 6 Apr 2011 21:50:43 +0000 Subject: [PATCH] add closest methods ! --- .../com/google/gwt/query/client/GQuery.java | 145 +++++++++++++++++- .../gwt/query/client/GQueryCoreTest.java | 55 ++++++- 2 files changed, 191 insertions(+), 9 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 7bbf280f..1491ea11 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 @@ -60,6 +60,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Map; /** * GwtQuery is a GWT clone of the popular jQuery library. @@ -121,6 +122,9 @@ public class GQuery implements Lazy { private static DocumentStyleImpl styleImpl = GWT.create(DocumentStyleImpl.class); private static Element windowData = null; + + //Sizzle POS regex : usefull in some methods + private static final String POS_REGEX = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^\\-]|$)"; /** * Create an empty GQuery object. @@ -197,7 +201,7 @@ public class GQuery implements Lazy { * reference to a plugin to be used. */ public static T $(String selector, Class plugin) { - return $(selector, (Node) null, plugin); + return $(selector, document, plugin); } /** @@ -216,7 +220,7 @@ public class GQuery implements Lazy { : ctx.getOwnerDocument(); return $(cleanHtmlString(selectorOrHtml, doc)); } - return new GQuery(select(selectorOrHtml, ctx)).setSelector(selectorOrHtml); + return new GQuery().select(selectorOrHtml, ctx); } /** @@ -231,7 +235,7 @@ public class GQuery implements Lazy { try { if (plugins != null) { T gquery = (T) plugins.get(plugin).init( - new GQuery(select(selector, context))).setSelector(selector); + new GQuery().select(selector, context)); return gquery; } throw new RuntimeException("No plugin for class " + plugin); @@ -348,7 +352,7 @@ public class GQuery implements Lazy { n = n.getLastChild(); } // TODO: add fixes for IE TBODY issue - return $((NodeList) n.getChildNodes().cast()).as(Events).addLiveEvents(); + return $((NodeList) n.getChildNodes().cast()); } protected static Object data(Element item, String name, S value) { @@ -450,13 +454,18 @@ public class GQuery implements Lazy { n.scrollIntoView() }-*/; - private static NodeList select(String selector, Node context) { + private GQuery select(String selector, Node context) { if (engine == null) { engine = new SelectorEngine(); } NodeList n = engine.select(selector, context); JsNodeArray res = copyNodeList(n); - return res; + + currentSelector = selector; + currentContext = context != null ? context : document; + + return setArray(res); + } private static native Element window() /*-{ @@ -464,6 +473,7 @@ public class GQuery implements Lazy { }-*/; protected String currentSelector; + protected Node currentContext; private NodeList elements = JavaScriptObject.createArray().cast(); @@ -471,6 +481,8 @@ public class GQuery implements Lazy { protected GQuery(GQuery gq) { this(gq == null ? null : gq.get()); + currentSelector = gq.getSelector(); + currentContext = gq.getContext(); } private GQuery() { @@ -772,7 +784,7 @@ public class GQuery implements Lazy { } else if (plugins != null) { Plugin p = plugins.get(plugin); if (p != null) { - return (T) p.init(this).setSelector(currentSelector); + return (T) p.init(this); } } throw new RuntimeException("No plugin registered for class " @@ -957,8 +969,120 @@ public class GQuery implements Lazy { for (Element e : elements()) { result.addNode(e.cloneNode(true)); } - return new GQuery(result).as(Events).addLiveEvents(); + GQuery ret = new GQuery(result); + ret.currentContext = currentContext; + ret.currentSelector = currentSelector; + return ret; + } + + /** + * 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 + */ + public GQuery closest(String selector){ + return closest(selector, null); + } + + /** + * Returns a {@link Map} object as key a selector and as value the first ancestor elements matching this selectors, beginning at the + * first matched element and progressing up through the DOM. This method allows retrieving the list of closest ancestors matching + * many selectors and by reducing the number of DOM traversing. + * + * @param selector + * @return + */ + public Map closest(String[] selectors){ + return closest(selectors, null); } + + /** + * Returns a GQuery object containing the first ancestor elements matching each selectors, beginning at the + * first matched element and progressing up through the DOM tree until reach the context node.. + * This method allows retrieving the list of closest ancestors matching many selectors and by reducing the number of DOM traversing. + * + * @param selector + * @return + */ + public Map closest(String[] selectors, Node context){ + Map results = new HashMap(); + + if (context == null){ + context = currentContext; + } + + Element first = get(0); + + if (first != null && selectors != null && selectors.length > 0){ + + Map matches = new HashMap(); + + for (String selector : selectors){ + if (!matches.containsKey(selector)){ + matches.put(selector, selector.matches(POS_REGEX) ? $(selector, context) : null); + } + } + + Element current = first; + + while (current != null && current.getOwnerDocument() != null && current != context){ + //for each selector, check if the current element match it. + for (String selector : matches.keySet()){ + if (results.containsKey(selector)){ + //first ancestors already found for this selector + continue; + } + GQuery pos = matches.get(selector); + boolean match = pos != null ? pos.index(current) > -1 : $(current).is(selector); + if (match){ + results.put(selector, current); + } + } + + current = current.getParentElement(); + } + + + } + + return results; + } + + /** + * 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 context node. + * + * If no context is passed in then the context of the gQuery object will be used instead. + * + */ + public GQuery closest(String selector, Node context){ + assert selector != null; + + if (context == null){ + context = currentContext; + } + + GQuery pos = selector.matches(POS_REGEX) ? $(selector, context) : null; + JsNodeArray result = JsNodeArray.create(); + + for (Element e : elements()){ + Element current = e; + while (current != null && current.getOwnerDocument() != null && current != context){ + boolean match = pos != null ? pos.index(current) > -1 : $(current).is(selector); + if (match){ + result.addNode(current); + break; + }else{ + current = current.getParentElement(); + } + } + } + + return $(unique(result)); + + } /** * Filter the set of elements to those that contain the specified text. @@ -1524,6 +1648,10 @@ public class GQuery implements Lazy { } return null; } + + public Node getContext() { + return currentContext; + } /** * Return the previous set of matched elements prior to the last destructive @@ -3277,6 +3405,7 @@ public class GQuery implements Lazy { GQuery g = new GQuery(elts); g.setPreviousObject(this); g.setSelector(selector); + g.currentContext = currentContext; return g; } 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 b3f04364..278ab826 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 @@ -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 = $("").appendTo(document).as(Widgets).button().widget(); + b2 = $("").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 = "

"; + $(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 = "

"; + $(e).html(html); + + Map 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")); + + } } -- 2.39.5