}\r
};\r
\r
+ private static ReplaceCallback rc_Attr = new ReplaceCallback() {\r
+ public String foundMatch(ArrayList<String> s) {\r
+ if (s.get(1) == null || s.get(1).length() == 0) {\r
+ s.set(1, "*");\r
+ }\r
+ if (s.get(3) == null || s.get(3).length() == 0) {\r
+ s.set(3, "");\r
+ }\r
+ return s.get(1) + "[@" + s.get(2) + s.get(3) + "]";\r
+ }\r
+ };\r
+ \r
private static ReplaceCallback rc_nth_child = new ReplaceCallback() {\r
public String foundMatch(ArrayList<String> s) {\r
- if (s.get(1).length() == 0) {\r
+ if (s.get(1) == null || s.get(1).length() == 0) {\r
s.set(1, "0");\r
}\r
if ("n".equals(s.get(2))) {\r
};\r
\r
private static Object[] regs = new Object[]{\r
- "\\[([^\\]~\\$\\*\\^\\|\\!]+)(=[^\\]]+)?\\]", "[@$1$2]",\r
+ // tag[attrib=value]\r
+ "([a-zA-Z0-9_\\-\\*\\[\\]])?\\[([^\\]@~\\$\\*\\^\\|\\!]+)(=[^\\]]+)?\\]", rc_Attr,\r
// multiple queries\r
"\\s*,\\s*", "|",\r
// , + ~ >\r
"([\\>\\+\\|\\~\\,\\s])([a-zA-Z\\*]+)", "$1//$2",\r
"\\s+//", "//",\r
// :first-child\r
- "([a-zA-Z0-9_\\-\\*]+):first-child", "*[1]/self::$1",\r
- // :first\r
- "([a-zA-Z0-9_\\-\\*]+):first", "*[1]/self::$1",\r
+ "([a-zA-Z0-9_\\-\\*]+):first-child", "$1[not(preceding-sibling::*)]",\r
// :last-child\r
"([a-zA-Z0-9_\\-\\*]+):last-child", "$1[not(following-sibling::*)]",\r
// :only-child\r
"#([a-zA-Z0-9_\\-]+)", "[@id='$1']",\r
"\\.([a-zA-Z0-9_\\-]+)", "[contains(concat(' ',normalize-space(@class),' '),' $1 ')]",\r
// normalize multiple filters\r
- "\\]\\[([^\\]]+)", " and ($1)"};\r
+ "\\]\\[([^\\]]+)", " and ($1)",\r
+ // tag:attrib\r
+ "([a-zA-Z0-9_\\-\\*]+):([a-zA-Z0-9_\\-]+)", "$1[@$2='$2']"\r
+ };\r
\r
public static SelectorEngineCssToXPath getInstance() {\r
if (instance == null) {\r
for (int i = 0; i < regs.length;) {\r
ret = replacer.replaceAll(ret, regs[i++].toString(), regs[i++]);\r
}\r
- return "//" + ret;\r
+ return ".//" + ret;\r
}\r
\r
public NodeList<Element> select(String sel, Node ctx) {\r
JSArray elm = JSArray.create();\r
- if (!sel.startsWith("//") && !sel.startsWith("./") && !sel.startsWith("/")) {\r
+ if (!sel.startsWith("./") && !sel.startsWith("/")) {\r
sel = css2Xpath(sel);\r
}\r
SelectorEngine.xpathEvaluate(sel, ctx, elm);\r
*/\r
package com.google.gwt.query.client.impl;\r
\r
+import com.google.gwt.core.client.JsArray;\r
import com.google.gwt.dom.client.Document;\r
import com.google.gwt.dom.client.Element;\r
import com.google.gwt.dom.client.Node;\r
elm.pushAll(prevElem);\r
}\r
\r
- return elm;\r
+ return unique(elm.<JsArray<Element>>cast()).cast();\r
}\r
\r
protected String getAttr(Element current, String name) {\r
import static com.google.gwt.query.client.SelectorEngine.eq;\r
import static com.google.gwt.query.client.SelectorEngine.truth;\r
\r
+import com.google.gwt.core.client.JsArray;\r
import com.google.gwt.dom.client.Element;\r
import com.google.gwt.dom.client.Node;\r
import com.google.gwt.dom.client.NodeList;\r
\r
private Regexp combinator;\r
\r
- private SelectorEngineImpl jsEngine = null;\r
-\r
public SelectorEngineXPath() {\r
}\r
\r
}\r
}\r
}\r
- try {\r
- SelectorEngine.xpathEvaluate(xPathExpression, ctx, elm).cast(); \r
- } catch (Exception e) {\r
- if (jsEngine == null) {\r
- jsEngine = new SelectorEngineSizzle();\r
- }\r
- return jsEngine.select(sel, ctx).cast();\r
- }\r
+ SelectorEngine.xpathEvaluate(xPathExpression, ctx, elm);\r
}\r
- return elm;\r
+ return unique(elm.<JsArray<Element>>cast()).cast();\r
}\r
\r
private void init() {\r
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.query.client.impl.SelectorEngineCssToXPath;
import com.google.gwt.query.client.impl.SelectorEngineImpl;
import com.google.gwt.query.client.impl.SelectorEngineJS;
import com.google.gwt.query.client.impl.SelectorEngineNative;
public void testIssue12() {
$(e).html("<table><tr><td><p myCustomAttr='whatever'><input type='radio' name='wantedName' value='v1'>1</input></p><input type='radio' name='n' value='v2' checked='checked'>2</input></td><td><button myCustomAttr='val'>Click</button></tr><td></table>");
- executeSelectInAllImplementations("[myCustomAttr]", 2);
- executeSelectInAllImplementations("*[myCustomAttr]", 2);
- executeSelectInAllImplementations("input[name=wantedName]", 1);
- executeSelectInAllImplementations("input[name='wantedName']", 1);
- executeSelectInAllImplementations("input[name=\"wantedName\"]", 1);
+ executeSelectInAllImplementations("[myCustomAttr]", e, 2);
+ executeSelectInAllImplementations("*[myCustomAttr]", e, 2);
+ executeSelectInAllImplementations("input[name=wantedName]", e, 1);
+ executeSelectInAllImplementations("input[name='wantedName']", e, 1);
+ executeSelectInAllImplementations("input[name=\"wantedName\"]", e, 1);
+ }
+
+ public void testSelectElementsInsideContext() {
+ $(e).html("<spam><p>s</p></spam>");
+ GQuery q = $("spam", e);
+ // TODO: in XPath engine it returns 2 when it should return 1
+ executeSelectInAllImplementations("*", q.get(0), 1, 2);
}
public void testSelectorEngineDomAssistant() {
executeSelectorEngineTests(selEng);
}
+ public void testSelectorEngineCssToXpath() {
+ SelectorEngineImpl selEng = new SelectorEngineCssToXPath();
+ executeSelectorEngineTests(selEng);
+ }
+
public void testSelectorsGeneratorNative() {
$(e).html(
"<input type='radio' name='n' value='v1'>1</input>"
assertTrue(message, done);
}
- private void executeSelectInAllImplementations(String selector, int result) {
+ private void executeSelectInAllImplementations(String selector, Element elem, Object... array) {
SelectorEngineImpl selSizz = new SelectorEngineSizzle();
SelectorEngineImpl selJS = new SelectorEngineJS();
SelectorEngineImpl selXpath = new SelectorEngineXPath();
+ SelectorEngineImpl selC2X = new SelectorEngineCssToXPath();
SelectorEngineImpl selNative = new SelectorEngineNative();
- assertEquals(result, selSizz.select(selector, e).getLength());
- assertEquals(result, selJS.select(selector, e).getLength());
- assertEquals(result, selXpath.select(selector, e).getLength());
+ assertArrayContains(selector, selSizz.select(selector, elem).getLength(), array);
+ assertArrayContains(selector, selJS.select(selector, elem).getLength(), array);
+ assertArrayContains(selector, selXpath.select(selector, elem).getLength(), array);
+ assertArrayContains(selector, selC2X.select(selector, elem).getLength(), array);
if (hasNativeSelector()) {
- assertEquals(result, selNative.select(selector, e).getLength());
+ assertArrayContains(selector, selNative.select(selector, elem), array);
}
}
assertArrayContains(selEng.select("body div", Document.get()).getLength(), 53, 55);
assertArrayContains(selEng.select("h1[id]:contains(Selectors)", e).getLength(), 1);
+ // :first is not a valid selector, it only works in sizzle
assertArrayContains(selEng.select("*:first", e).getLength(), 1, 0);
assertArrayContains(selEng.select("div[class!=madeup]", e).getLength(), 52, 53);
assertArrayContains(selEng.select("div, p a", e).getLength(), 136, 137, 138);
assertArrayContains(selEng.select("p:nth-child(odd)", e).getLength(), 165);
assertArrayContains(selEng.select("p:only-child", e).getLength(), 3);
assertArrayContains(selEng.select("#title", e).getLength(), 1);
- assertArrayContains(selEng.select("#title, h1#title", e).getLength(), 2);
+ assertArrayContains(selEng.select("#title, h1#title", e).getLength(), 1);
assertArrayContains(selEng.select("ul.toc li.tocline2", e).getLength(), 12);
assertArrayContains(selEng.select("h1[id]:contains(Selectors)", e).getLength(), 1);
}
public void testCssToXpath() {
SelectorEngineCssToXPath sel = new SelectorEngineCssToXPath();
- assertEquals("//div[starts-with(@class,'exa') and (substring(@class,string-length(@class)-3)='mple')]",
+ assertEquals(".//div[starts-with(@class,'exa') and (substring(@class,string-length(@class)-3)='mple')]",
sel.css2Xpath("div[class^=exa][class$=mple]"));
- assertEquals("//div[not(contains(concat(' ',normalize-space(@class),' '),' example '))]",
+ assertEquals(".//div[not(contains(concat(' ',normalize-space(@class),' '),' example '))]",
sel.css2Xpath("div:not(.example)"));
- assertEquals("//p",
+ assertEquals(".//p",
sel.css2Xpath("p:nth-child(n)"));
- assertEquals("//p[(count(preceding-sibling::*) + 1) mod 2=1]",
+ assertEquals(".//p[(count(preceding-sibling::*) + 1) mod 2=1]",
sel.css2Xpath("p:nth-child(odd)"));
- assertEquals("//*[(position()-0) mod 2=0 and position()>=0]/self::p",
+ assertEquals(".//*[(position()-0) mod 2=0 and position()>=0]/self::p",
sel.css2Xpath("p:nth-child(2n)"));
- assertEquals("//div[substring(@class,string-length(@class)-3)='mple']",
+ assertEquals(".//div[substring(@class,string-length(@class)-3)='mple']",
sel.css2Xpath("div[class$=mple]"));
- assertEquals("//div[substring(@class,string-length(@class)-5)='xample']",
+ assertEquals(".//div[substring(@class,string-length(@class)-5)='xample']",
sel.css2Xpath("div[class$=xample]"));
- assertEquals("//div[not(contains(concat(' ',normalize-space(@class),' '),' example '))]",
+ assertEquals(".//div[not(contains(concat(' ',normalize-space(@class),' '),' example '))]",
sel.css2Xpath("div:not(.example)"));
+ assertEquals(".//*[@myAttr]",
+ sel.css2Xpath("[myAttr]"));
+
+ assertEquals(".//tag[@myAttr='abcd']",
+ sel.css2Xpath("tag[myAttr=abcd]"));
+
+ assertEquals(".//a[@href and (@lang) and (@class)]",
+ sel.css2Xpath("a[href][lang][class]"));
+
}
public void testCss2Xpath() {
SelectorGeneratorCssToXPath sel = new SelectorGeneratorCssToXPath();
- assertEquals("//div[starts-with(@class,'exa') and (substring(@class,string-length(@class)-3)='mple')]",
+ assertEquals(".//div[starts-with(@class,'exa') and (substring(@class,string-length(@class)-3)='mple')]",
sel.css2Xpath("div[class^=exa][class$=mple]"));
- assertEquals("//div[not(contains(concat(' ',normalize-space(@class),' '),' example '))]",
+ assertEquals(".//div[not(contains(concat(' ',normalize-space(@class),' '),' example '))]",
sel.css2Xpath("div:not(.example)"));
- assertEquals("//p",
+ assertEquals(".//p",
sel.css2Xpath("p:nth-child(n)"));
- assertEquals("//p[(count(preceding-sibling::*) + 1) mod 2=1]",
+ assertEquals(".//p[(count(preceding-sibling::*) + 1) mod 2=1]",
sel.css2Xpath("p:nth-child(odd)"));
- assertEquals("//*[(position()-0) mod 2=0 and position()>=0]/self::p",
+ assertEquals(".//*[(position()-0) mod 2=0 and position()>=0]/self::p",
sel.css2Xpath("p:nth-child(2n)"));
- assertEquals("//div[substring(@class,string-length(@class)-3)='mple']",
+ assertEquals(".//div[substring(@class,string-length(@class)-3)='mple']",
sel.css2Xpath("div[class$=mple]"));
- assertEquals("//div[substring(@class,string-length(@class)-5)='xample']",
+ assertEquals(".//div[substring(@class,string-length(@class)-5)='xample']",
sel.css2Xpath("div[class$=xample]"));
- assertEquals("//div[not(contains(concat(' ',normalize-space(@class),' '),' example '))]",
+ assertEquals(".//div[not(contains(concat(' ',normalize-space(@class),' '),' example '))]",
sel.css2Xpath("div:not(.example)"));
+
+ assertEquals(".//*",
+ sel.css2Xpath("*"));
+
+ assertEquals(".//input[@checked='checked']",
+ sel.css2Xpath("input:checked"));
+
+ assertEquals(".//*[@myAttr]",
+ sel.css2Xpath("[myAttr]"));
+
+ assertEquals(".//tag[@myAttr='abcd']",
+ sel.css2Xpath("tag[myAttr=abcd]"));
+
+ assertEquals(".//a[@href and (@lang) and (@class)]",
+ sel.css2Xpath("a[href][lang][class]"));
+
}
public void testReplaceAll() {