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
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
* 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
: 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
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
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
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
}-*/;\r
\r
protected String currentSelector;\r
+ protected Node currentContext;\r
\r
private NodeList<Element> elements = JavaScriptObject.createArray().cast();\r
\r
\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
} 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
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
}\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
GQuery g = new GQuery(elts);\r
g.setPreviousObject(this);\r
g.setSelector(selector);\r
+ g.currentContext = currentContext;\r
return g;\r
}\r
\r
import junit.framework.Assert;
import java.util.List;
+import java.util.Map;
/**
* Test class for testing gwtquery-core api.
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");
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"));
+
+ }
}