You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SelectorEngineJS.java 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. /*
  2. * Copyright 2009 Google Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.google.gwt.query.client.impl.research;
  17. import static com.google.gwt.query.client.GQUtils.eq;
  18. import static com.google.gwt.query.client.GQUtils.truth;
  19. import com.google.gwt.core.client.JavaScriptObject;
  20. import com.google.gwt.core.client.JsArray;
  21. import com.google.gwt.dom.client.Document;
  22. import com.google.gwt.dom.client.Element;
  23. import com.google.gwt.dom.client.Node;
  24. import com.google.gwt.dom.client.NodeList;
  25. import com.google.gwt.query.client.GQUtils;
  26. import com.google.gwt.query.client.JSArray;
  27. import com.google.gwt.query.client.Regexp;
  28. import com.google.gwt.query.client.SelectorEngine;
  29. import com.google.gwt.query.client.impl.SelectorEngineImpl;
  30. /**
  31. * Runtime selector engine implementation with no-XPath/native support based on
  32. * DOMAssistant.
  33. */
  34. public class SelectorEngineJS extends SelectorEngineImpl {
  35. /**
  36. * Internal class.
  37. */
  38. protected static class Sequence {
  39. public int start;
  40. public int max;
  41. public int add;
  42. public int modVal;
  43. }
  44. /**
  45. * Internal class.
  46. */
  47. protected static class SplitRule {
  48. public String tag;
  49. public String id;
  50. public String allClasses;
  51. public String allAttr;
  52. public String allPseudos;
  53. public String tagRelation;
  54. public SplitRule(String tag, String id, String allClasses, String allAttr,
  55. String allPseudos) {
  56. this.tag = tag;
  57. this.id = id;
  58. this.allClasses = allClasses;
  59. this.allAttr = allAttr;
  60. this.allPseudos = allPseudos;
  61. }
  62. public SplitRule(String tag, String id, String allClasses, String allAttr,
  63. String allPseudos, String tagRelation) {
  64. this.tag = tag;
  65. this.id = id;
  66. this.allClasses = allClasses;
  67. this.allAttr = allAttr;
  68. this.allPseudos = allPseudos;
  69. this.tagRelation = tagRelation;
  70. }
  71. }
  72. protected static Sequence getSequence(String expression) {
  73. int start = 0, add = 2, max = -1, modVal = -1;
  74. Regexp expressionRegExp = new Regexp(
  75. "^((odd|even)|([1-9]\\d*)|((([1-9]\\d*)?)n((\\+|\\-)(\\d+))?)|(\\-(([1-9]\\d*)?)n\\+(\\d+)))$");
  76. JSArray pseudoValue = expressionRegExp.exec(expression);
  77. if (!truth(pseudoValue)) {
  78. return null;
  79. } else {
  80. if (truth(pseudoValue.getStr(2))) { // odd or even
  81. start = (eq(pseudoValue.getStr(2), "odd")) ? 1 : 2;
  82. modVal = (start == 1) ? 1 : 0;
  83. } else if (GQUtils
  84. .truth(pseudoValue.getStr(3))) { // single digit
  85. start = Integer.parseInt(pseudoValue.getStr(3), 10);
  86. add = 0;
  87. max = start;
  88. } else if (truth(pseudoValue.getStr(4))) { // an+b
  89. add = truth(pseudoValue.getStr(6)) ? Integer
  90. .parseInt(pseudoValue.getStr(6), 10) : 1;
  91. start = truth(pseudoValue.getStr(7)) ? Integer.parseInt(
  92. (pseudoValue.getStr(8).charAt(0) == '+' ? ""
  93. : pseudoValue.getStr(8)) + pseudoValue.getStr(9), 10) : 0;
  94. while (start < 1) {
  95. start += add;
  96. }
  97. modVal = (start > add) ? (start - add) % add
  98. : ((start == add) ? 0 : start);
  99. } else if (truth(pseudoValue.getStr(10))) { // -an+b
  100. add = truth(pseudoValue.getStr(12)) ? Integer
  101. .parseInt(pseudoValue.getStr(12), 10) : 1;
  102. start = max = Integer.parseInt(pseudoValue.getStr(13), 10);
  103. while (start > add) {
  104. start -= add;
  105. }
  106. modVal = (max > add) ? (max - add) % add : ((max == add) ? 0 : max);
  107. }
  108. }
  109. Sequence s = new Sequence();
  110. s.start = start;
  111. s.add = add;
  112. s.max = max;
  113. s.modVal = modVal;
  114. return s;
  115. }
  116. public static void clearAdded(JSArray a) {
  117. for (int i = 0, len = a.size(); i < len; i++) {
  118. clearAdded(a.getNode(i));
  119. }
  120. }
  121. public static native void clearAdded(Node node) /*-{
  122. node.added = null;
  123. }-*/;
  124. public static native NodeList<Element> getElementsByClassName(String clazz,
  125. Node ctx) /*-{
  126. return ctx.getElementsByClassName(clazz);
  127. }-*/;
  128. public static native boolean isAdded(Node prevRef) /*-{
  129. return prevRef.added || false;
  130. }-*/;
  131. public static native void setAdded(Node prevRef, boolean added) /*-{
  132. prevRef.added = added;
  133. }-*/;
  134. public static native void setSkipTag(JSArray prevElem, boolean skip) /*-{
  135. prevElem.skipTag = skip;
  136. }-*/;
  137. private static String attrToRegExp(String attrVal, String op) {
  138. if (GQUtils.eq("^", op)) {
  139. return "^" + attrVal;
  140. }
  141. if (GQUtils.eq("$", op)) {
  142. return attrVal + "$";
  143. }
  144. if (GQUtils.eq("*", op)) {
  145. return attrVal;
  146. }
  147. if (GQUtils.eq("|", op)) {
  148. return "(^" + attrVal + "(\\-\\w+)*$)";
  149. }
  150. if (GQUtils.eq("~", op)) {
  151. return "\\b" + attrVal + "\\b";
  152. }
  153. return GQUtils.truth(attrVal) ? "^" + attrVal + "$" : null;
  154. }
  155. private static native boolean checked(Node previous) /*-{
  156. return previous.checked || false;
  157. }-*/;
  158. private static void clearChildElms(JSArray prevParents) {
  159. for (int n = 0, nl = prevParents.size(); n < nl; n++) {
  160. setHasChildElms(prevParents.getNode(n), false);
  161. }
  162. }
  163. private static native boolean enabled(Node node) /*-{
  164. return !node.disabled;
  165. }-*/;
  166. private static void getDescendantNodes(JSArray matchingElms,
  167. String nextTagStr, Node prevRef) {
  168. NodeList<Element> children = getElementsByTagName(nextTagStr, prevRef);
  169. for (int k = 0, klen = children.getLength(); k < klen; k++) {
  170. Node child = children.getItem(k);
  171. if (child.getParentNode() == prevRef) {
  172. matchingElms.addNode(child);
  173. }
  174. }
  175. }
  176. private static NodeList<Element> getElementsByTagName(String tag, Node ctx) {
  177. if (ctx == null) {
  178. return JavaScriptObject.createArray().cast();
  179. }
  180. return ((Element) ctx).getElementsByTagName(tag);
  181. }
  182. private static void getGeneralSiblingNodes(JSArray matchingElms,
  183. JSArray nextTag, Regexp nextRegExp, Node prevRef) {
  184. while (
  185. GQUtils.truth((prevRef = SelectorEngine.getNextSibling(prevRef)))
  186. && !isAdded(prevRef)) {
  187. if (!GQUtils.truth(nextTag) || nextRegExp
  188. .test(prevRef.getNodeName())) {
  189. setAdded(prevRef, true);
  190. matchingElms.addNode(prevRef);
  191. }
  192. }
  193. }
  194. private static void getSiblingNodes(JSArray matchingElms, JSArray nextTag,
  195. Regexp nextRegExp, Node prevRef) {
  196. while (
  197. GQUtils.truth(prevRef = SelectorEngine.getNextSibling(prevRef))
  198. && prevRef.getNodeType() != Node.ELEMENT_NODE) {
  199. }
  200. if (GQUtils.truth(prevRef)) {
  201. if (!GQUtils.truth(nextTag) || nextRegExp
  202. .test(prevRef.getNodeName())) {
  203. matchingElms.addNode(prevRef);
  204. }
  205. }
  206. }
  207. private static native boolean hasChildElms(Node prevParent) /*-{
  208. return prevParent.childElms || false;
  209. }-*/;
  210. private static native boolean isSkipped(JSArray prevElem) /*-{
  211. return prevElem.skipTag || false;
  212. }-*/;
  213. private static native void setHasChildElms(Node prevParent, boolean bool) /*-{
  214. prevParent.childElms = bool ? bool : null;
  215. }-*/;
  216. private static native JSArray subtractArray(JSArray previousMatch,
  217. JSArray elementsByPseudo) /*-{
  218. for (var i=0, src1; (src1=arr1[i]); i++) {
  219. var found = false;
  220. for (var j=0, src2; (src2=arr2[j]); j++) {
  221. if (src2 === src1) {
  222. found = true;
  223. break;
  224. }
  225. }
  226. if (found) {
  227. arr1.splice(i--, 1);
  228. }
  229. }
  230. return arr;
  231. }-*/;
  232. private Regexp cssSelectorRegExp;
  233. private Regexp selectorSplitRegExp;
  234. private Regexp childOrSiblingRefRegExp;
  235. public SelectorEngineJS() {
  236. selectorSplitRegExp = new Regexp("[^\\s]+", "g");
  237. childOrSiblingRefRegExp = new Regexp("^(>|\\+|~)$");
  238. cssSelectorRegExp = new Regexp(
  239. "^(\\w+)?(#[\\w\\u00C0-\\uFFFF\\-\\_]+|(\\*))?((\\.[\\w\\u00C0-\\uFFFF\\-_]+)*)?((\\[\\w+(\\^|\\$|\\*|\\||~)?(=[\"']*[\\w\\u00C0-\\uFFFF\\s\\-\\_\\.]+[\"']*)?\\]+)*)?(((:\\w+[\\w\\-]*)(\\((odd|even|\\-?\\d*n?((\\+|\\-)\\d+)?|[\\w\\u00C0-\\uFFFF\\-_]+|((\\w*\\.[\\w\\u00C0-\\uFFFF\\-_]+)*)?|(\\[#?\\w+(\\^|\\$|\\*|\\||~)?=?[\\w\\u00C0-\\uFFFF\\s\\-\\_\\.]+\\]+)|(:\\w+[\\w\\-]*))\\))?)*)?");
  240. }
  241. public NodeList<Element> select(String sel, Node ctx) {
  242. String selectors[] = sel.replace("\\s*(,)\\s*", "$1").split(",");
  243. boolean identical = false;
  244. JSArray elm = JSArray.create();
  245. for (int a = 0, len = selectors.length; a < len; a++) {
  246. if (a > 0) {
  247. identical = false;
  248. for (int b = 0, bl = a; b < bl; b++) {
  249. if (GQUtils.eq(selectors[a], selectors[b])) {
  250. identical = true;
  251. break;
  252. }
  253. }
  254. if (identical) {
  255. continue;
  256. }
  257. }
  258. String currentRule = selectors[a];
  259. JSArray cssSelectors = selectorSplitRegExp.match(currentRule);
  260. JSArray prevElem = JSArray.create(ctx);
  261. for (int i = 0, slen = cssSelectors.size(); i < slen; i++) {
  262. JSArray matchingElms = JSArray.create();
  263. String rule = cssSelectors.getStr(i);
  264. if (i > 0 && childOrSiblingRefRegExp.test(rule)) {
  265. JSArray childOrSiblingRef = childOrSiblingRefRegExp.exec(rule);
  266. if (GQUtils.truth(childOrSiblingRef)) {
  267. JSArray nextTag = new Regexp("^\\w+")
  268. .exec(cssSelectors.getStr(i + 1));
  269. Regexp nextRegExp = null;
  270. String nextTagStr = null;
  271. if (GQUtils.truth(nextTag)) {
  272. nextTagStr = nextTag.getStr(0);
  273. nextRegExp = new Regexp("(^|\\s)" + nextTagStr + "(\\s|$)", "i");
  274. }
  275. for (int j = 0, jlen = prevElem.size(); j < jlen; j++) {
  276. Node prevRef = prevElem.getNode(j);
  277. String ref = childOrSiblingRef.getStr(0);
  278. if (GQUtils.eq(">", ref)) {
  279. getDescendantNodes(matchingElms, nextTagStr, prevRef);
  280. } else if (GQUtils.eq("+", ref)) {
  281. getSiblingNodes(matchingElms, nextTag, nextRegExp, prevRef);
  282. } else if (GQUtils.eq("~", ref)) {
  283. getGeneralSiblingNodes(matchingElms, nextTag, nextRegExp,
  284. prevRef);
  285. }
  286. }
  287. prevElem = matchingElms;
  288. clearAdded(prevElem);
  289. rule = cssSelectors.getStr(++i);
  290. if (new Regexp("^\\w+$").test(rule)) {
  291. continue;
  292. }
  293. setSkipTag(prevElem, true);
  294. }
  295. }
  296. JSArray cssSelector = cssSelectorRegExp.exec(rule);
  297. SplitRule splitRule = new SplitRule(
  298. !GQUtils.truth(cssSelector.getStr(1)) || GQUtils
  299. .eq(cssSelector.getStr(3), "*") ? "*" : cssSelector.getStr(1),
  300. !GQUtils.eq(cssSelector.getStr(3), "*") ? cssSelector
  301. .getStr(2) : null, cssSelector.getStr(4), cssSelector.getStr(6),
  302. cssSelector.getStr(10));
  303. if (GQUtils.truth(splitRule.id)) {
  304. Element domelem = Document.get()
  305. .getElementById(splitRule.id.substring(1));
  306. if (GQUtils.truth(domelem)) {
  307. matchingElms = JSArray.create(domelem);
  308. }
  309. prevElem = matchingElms;
  310. } else if (GQUtils.truth(splitRule.tag) && !isSkipped(
  311. prevElem)) {
  312. if (i == 0 && matchingElms.size() == 0 && prevElem.size() == 1) {
  313. prevElem = matchingElms = JSArray.create(
  314. getElementsByTagName(splitRule.tag, prevElem.getNode(0)));
  315. } else {
  316. NodeList<Element> tagCollectionMatches;
  317. for (int l = 0, ll = prevElem.size(); l < ll; l++) {
  318. tagCollectionMatches = getElementsByTagName(splitRule.tag,
  319. prevElem.getNode(l));
  320. for (int m = 0, mlen = tagCollectionMatches.getLength(); m < mlen;
  321. m++) {
  322. Node tagMatch = tagCollectionMatches.getItem(m);
  323. if (!isAdded(tagMatch)) {
  324. setAdded(tagMatch, true);
  325. matchingElms.addNode(tagMatch);
  326. }
  327. }
  328. }
  329. prevElem = matchingElms;
  330. clearAdded(prevElem);
  331. }
  332. if (matchingElms.size() == 0) {
  333. break;
  334. }
  335. setSkipTag(prevElem, false);
  336. if (GQUtils.truth(splitRule.allClasses)) {
  337. String[] allClasses = splitRule.allClasses.replaceFirst("^\\.", "")
  338. .split("\\.");
  339. Regexp[] regExpClassNames = new Regexp[allClasses.length];
  340. for (int n = 0, nl = allClasses.length; n < nl; n++) {
  341. regExpClassNames[n] = new Regexp(
  342. "(^|\\s)" + allClasses[n] + "(\\s|$)");
  343. }
  344. JSArray matchingClassElms = JSArray.create();
  345. for (int o = 0, olen = prevElem.size(); o < olen; o++) {
  346. Element current = prevElem.getElement(o);
  347. String elmClass = current.getClassName();
  348. boolean addElm = false;
  349. if (GQUtils.truth(elmClass) && !isAdded(current)) {
  350. for (int p = 0, pl = regExpClassNames.length; p < pl; p++) {
  351. addElm = regExpClassNames[p].test(elmClass);
  352. if (!addElm) {
  353. break;
  354. }
  355. }
  356. if (addElm) {
  357. setAdded(current, true);
  358. matchingClassElms.addNode(current);
  359. }
  360. }
  361. }
  362. clearAdded(prevElem);
  363. prevElem = matchingElms = matchingClassElms;
  364. }
  365. if (GQUtils.truth(splitRule.allAttr)) {
  366. JSArray allAttr = Regexp
  367. .match("\\[[^\\]]+\\]", "g", splitRule.allAttr);
  368. Regexp[] regExpAttributes = new Regexp[allAttr.size()];
  369. String[] regExpAttributesStr = new String[allAttr.size()];
  370. Regexp attributeMatchRegExp = new Regexp(
  371. "(\\w+)(\\^|\\$|\\*|\\||~)?=?[\"']?([\\w\u00C0-\uFFFF\\s\\-_\\.]+)?");
  372. for (int q = 0, ql = allAttr.size(); q < ql; q++) {
  373. JSArray attributeMatch = attributeMatchRegExp
  374. .exec(allAttr.getStr(q));
  375. String attributeValue =
  376. GQUtils.truth(attributeMatch.getStr(3))
  377. ? attributeMatch.getStr(3).replaceAll("\\.", "\\.")
  378. : null;
  379. String attrVal = attrToRegExp(attributeValue,
  380. (GQUtils.or(attributeMatch.getStr(2), null)));
  381. regExpAttributes[q] = (GQUtils.truth(attrVal) ? new Regexp(
  382. attrVal) : null);
  383. regExpAttributesStr[q] = attributeMatch.getStr(1);
  384. }
  385. JSArray matchingAttributeElms = JSArray.create();
  386. for (int r = 0, rlen = matchingElms.size(); r < rlen; r++) {
  387. Element current = matchingElms.getElement(r);
  388. boolean addElm = false;
  389. for (int s = 0, sl = regExpAttributes.length;
  390. s < sl; s++) {
  391. addElm = false;
  392. Regexp attributeRegexp = regExpAttributes[s];
  393. String currentAttr = getAttr(current, regExpAttributesStr[s]);
  394. if (GQUtils.truth(currentAttr)
  395. && currentAttr.length() != 0) {
  396. if (attributeRegexp == null || attributeRegexp
  397. .test(currentAttr)) {
  398. addElm = true;
  399. }
  400. }
  401. if (!addElm) {
  402. break;
  403. }
  404. }
  405. if (addElm) {
  406. matchingAttributeElms.addNode(current);
  407. }
  408. }
  409. prevElem = matchingElms = matchingAttributeElms;
  410. }
  411. if (GQUtils.truth(splitRule.allPseudos)) {
  412. Regexp pseudoSplitRegExp = new Regexp(
  413. ":(\\w[\\w\\-]*)(\\(([^\\)]+)\\))?");
  414. JSArray allPseudos = Regexp
  415. .match("(:\\w+[\\w\\-]*)(\\([^\\)]+\\))?", "g",
  416. splitRule.allPseudos);
  417. for (int t = 0, tl = allPseudos.size(); t < tl; t++) {
  418. JSArray pseudo = pseudoSplitRegExp.match(allPseudos.getStr(t));
  419. String pseudoClass = GQUtils.truth(pseudo.getStr(1))
  420. ? pseudo.getStr(1).toLowerCase() : null;
  421. String pseudoValue = GQUtils.truth(pseudo.getStr(3))
  422. ? pseudo.getStr(3) : null;
  423. matchingElms = getElementsByPseudo(matchingElms, pseudoClass,
  424. pseudoValue);
  425. clearAdded(matchingElms);
  426. }
  427. prevElem = matchingElms;
  428. }
  429. }
  430. }
  431. elm.pushAll(prevElem);
  432. }
  433. return GQUtils.unique(elm.<JsArray<Element>>cast()).cast();
  434. }
  435. protected String getAttr(Element current, String name) {
  436. return current.getAttribute(name);
  437. }
  438. private void getCheckedPseudo(JSArray previousMatch, JSArray matchingElms) {
  439. Node previous;
  440. for (int q = 0, qlen = previousMatch.size(); q < qlen; q++) {
  441. previous = previousMatch.getNode(q);
  442. if (checked(previous)) {
  443. matchingElms.addNode(previous);
  444. }
  445. }
  446. }
  447. private void getContainsPseudo(JSArray previousMatch, String pseudoValue,
  448. JSArray matchingElms) {
  449. Node previous;
  450. for (int q = 0, qlen = previousMatch.size(); q < qlen; q++) {
  451. previous = previousMatch.getNode(q);
  452. if (!isAdded(previous)) {
  453. if (((Element) previous).getInnerText().indexOf(pseudoValue) != -1) {
  454. setAdded(previous, true);
  455. matchingElms.addNode(previous);
  456. }
  457. }
  458. }
  459. }
  460. private void getDefaultPseudo(JSArray previousMatch, String pseudoClass,
  461. String pseudoValue, JSArray matchingElms) {
  462. Node previous;
  463. for (int w = 0, wlen = previousMatch.size(); w < wlen; w++) {
  464. previous = previousMatch.getElement(w);
  465. if (GQUtils
  466. .eq(((Element) previous).getAttribute(pseudoClass), pseudoValue)) {
  467. matchingElms.addNode(previous);
  468. }
  469. }
  470. }
  471. private void getDisabledPseudo(JSArray previousMatch, JSArray matchingElms) {
  472. Node previous;
  473. for (int q = 0, qlen = previousMatch.size(); q < qlen; q++) {
  474. previous = previousMatch.getNode(q);
  475. if (!enabled(previous)) {
  476. matchingElms.addNode(previous);
  477. }
  478. }
  479. }
  480. private JSArray getElementsByPseudo(JSArray previousMatch, String pseudoClass,
  481. String pseudoValue) {
  482. JSArray prevParents = JSArray.create();
  483. boolean previousDir = pseudoClass.startsWith("first") ? true : false;
  484. JSArray matchingElms = JSArray.create();
  485. if (GQUtils.eq("first-child", pseudoClass) || GQUtils
  486. .eq("last-child", pseudoClass)) {
  487. getFirstChildPseudo(previousMatch, previousDir, matchingElms);
  488. } else if (GQUtils.eq("only-child", pseudoClass)) {
  489. getOnlyChildPseudo(previousMatch, matchingElms);
  490. } else if (GQUtils.eq("nth-child", pseudoClass)) {
  491. matchingElms = getNthChildPseudo(previousMatch, pseudoValue, prevParents,
  492. matchingElms);
  493. } else if (GQUtils.eq("first-of-type", pseudoClass) || GQUtils
  494. .eq("last-of-type", pseudoClass)) {
  495. getFirstOfTypePseudo(previousMatch, previousDir, matchingElms);
  496. } else if (GQUtils.eq("only-of-type", pseudoClass)) {
  497. getOnlyOfTypePseudo(previousMatch, matchingElms);
  498. } else if (GQUtils.eq("nth-of-type", pseudoClass)) {
  499. matchingElms = getNthOfTypePseudo(previousMatch, pseudoValue, prevParents,
  500. matchingElms);
  501. } else if (GQUtils.eq("empty", pseudoClass)) {
  502. getEmptyPseudo(previousMatch, matchingElms);
  503. } else if (GQUtils.eq("enabled", pseudoClass)) {
  504. getEnabledPseudo(previousMatch, matchingElms);
  505. } else if (GQUtils.eq("disabled", pseudoClass)) {
  506. getDisabledPseudo(previousMatch, matchingElms);
  507. } else if (GQUtils.eq("checked", pseudoClass)) {
  508. getCheckedPseudo(previousMatch, matchingElms);
  509. } else if (GQUtils.eq("contains", pseudoClass)) {
  510. getContainsPseudo(previousMatch, pseudoValue, matchingElms);
  511. } else if (GQUtils.eq("not", pseudoClass)) {
  512. matchingElms = getNotPseudo(previousMatch, pseudoValue, matchingElms);
  513. } else {
  514. getDefaultPseudo(previousMatch, pseudoClass, pseudoValue, matchingElms);
  515. }
  516. return matchingElms;
  517. }
  518. private void getEmptyPseudo(JSArray previousMatch, JSArray matchingElms) {
  519. Node previous;
  520. for (int q = 0, qlen = previousMatch.size(); q < qlen; q++) {
  521. previous = previousMatch.getNode(q);
  522. if (!previous.hasChildNodes()) {
  523. matchingElms.addNode(previous);
  524. }
  525. }
  526. }
  527. private void getEnabledPseudo(JSArray previousMatch, JSArray matchingElms) {
  528. Node previous;
  529. for (int q = 0, qlen = previousMatch.size(); q < qlen; q++) {
  530. previous = previousMatch.getNode(q);
  531. if (enabled(previous)) {
  532. matchingElms.addNode(previous);
  533. }
  534. }
  535. }
  536. private void getFirstChildPseudo(JSArray previousMatch, boolean previousDir,
  537. JSArray matchingElms) {
  538. Node next;
  539. Node previous;
  540. for (int j = 0, jlen = previousMatch.size(); j < jlen; j++) {
  541. previous = next = previousMatch.getElement(j);
  542. if (previousDir) {
  543. while (GQUtils
  544. .truth((next = SelectorEngine.getPreviousSibling(next)))
  545. && next.getNodeType() != Node.ELEMENT_NODE) {
  546. }
  547. } else {
  548. while (
  549. GQUtils.truth((next = SelectorEngine.getNextSibling(next)))
  550. && next.getNodeType() != Node.ELEMENT_NODE) {
  551. }
  552. }
  553. if (!GQUtils.truth(next)) {
  554. matchingElms.addNode(previous);
  555. }
  556. }
  557. }
  558. private void getFirstOfTypePseudo(JSArray previousMatch, boolean previousDir,
  559. JSArray matchingElms) {
  560. Node previous;
  561. Node next;
  562. for (int n = 0, nlen = previousMatch.size(); n < nlen; n++) {
  563. next = previous = previousMatch.getNode(n);
  564. if (previousDir) {
  565. while (
  566. GQUtils.truth(next = SelectorEngine.getPreviousSibling(next))
  567. && !GQUtils
  568. .eq(next.getNodeName(), previous.getNodeName())) {
  569. }
  570. } else {
  571. while (GQUtils.truth(next = SelectorEngine.getNextSibling(next))
  572. && !GQUtils.eq(next.getNodeName(), previous.getNodeName())) {
  573. }
  574. }
  575. if (!GQUtils.truth(next)) {
  576. matchingElms.addNode(previous);
  577. }
  578. }
  579. }
  580. private JSArray getNotPseudo(JSArray previousMatch, String pseudoValue,
  581. JSArray matchingElms) {
  582. if (new Regexp("(:\\w+[\\w\\-]*)$").test(pseudoValue)) {
  583. matchingElms = subtractArray(previousMatch,
  584. getElementsByPseudo(previousMatch, pseudoValue.substring(1), ""));
  585. } else {
  586. pseudoValue = pseudoValue
  587. .replace("^\\[#([\\w\\u00C0-\\uFFFF\\-\\_]+)\\]$", "[id=$1]");
  588. JSArray notTag = new Regexp("^(\\w+)").exec(pseudoValue);
  589. JSArray notClass = new Regexp("^\\.([\\w\u00C0-\uFFFF\\-_]+)")
  590. .exec(pseudoValue);
  591. JSArray notAttr = new Regexp(
  592. "\\[(\\w+)(\\^|\\$|\\*|\\||~)?=?([\\w\\u00C0-\\uFFFF\\s\\-_\\.]+)?\\]")
  593. .exec(pseudoValue);
  594. Regexp notRegExp = new Regexp("(^|\\s)"
  595. + (GQUtils.truth(notTag) ? notTag.getStr(1)
  596. : GQUtils.truth(notClass) ? notClass.getStr(1) : "")
  597. + "(\\s|$)", "i");
  598. if (GQUtils.truth(notAttr)) {
  599. String notAttribute = GQUtils.truth(notAttr.getStr(3)) ? notAttr
  600. .getStr(3).replace("\\.", "\\.") : null;
  601. String notMatchingAttrVal = attrToRegExp(notAttribute,
  602. notAttr.getStr(2));
  603. notRegExp = new Regexp(notMatchingAttrVal, "i");
  604. }
  605. for (int v = 0, vlen = previousMatch.size(); v < vlen; v++) {
  606. Element notElm = previousMatch.getElement(v);
  607. Element addElm = null;
  608. if (GQUtils.truth(notTag) && !notRegExp
  609. .test(notElm.getNodeName())) {
  610. addElm = notElm;
  611. } else if (GQUtils.truth(notClass) && !notRegExp
  612. .test(notElm.getClassName())) {
  613. addElm = notElm;
  614. } else if (GQUtils.truth(notAttr)) {
  615. String att = getAttr(notElm, notAttr.getStr(1));
  616. if (!GQUtils.truth(att) || !notRegExp.test(att)) {
  617. addElm = notElm;
  618. }
  619. }
  620. if (GQUtils.truth(addElm) && !isAdded(addElm)) {
  621. setAdded(addElm, true);
  622. matchingElms.addNode(addElm);
  623. }
  624. }
  625. }
  626. return matchingElms;
  627. }
  628. private JSArray getNthChildPseudo(JSArray previousMatch, String pseudoValue,
  629. JSArray prevParents, JSArray matchingElms) {
  630. Node previous;
  631. if (GQUtils.eq(pseudoValue, "n")) {
  632. matchingElms = previousMatch;
  633. } else {
  634. Sequence sequence = getSequence(pseudoValue);
  635. if (sequence != null) {
  636. for (int l = 0, llen = previousMatch.size(); l < llen; l++) {
  637. previous = previousMatch.getNode(l);
  638. Node prevParent = previous.getParentNode();
  639. if (!hasChildElms(prevParent)) {
  640. int iteratorNext = sequence.start;
  641. int childCount = 0;
  642. Node childElm = prevParent.getFirstChild();
  643. while (childElm != null && (sequence.max < 0
  644. || iteratorNext <= sequence.max)) {
  645. if (childElm.getNodeType() == Node.ELEMENT_NODE) {
  646. if (++childCount == iteratorNext) {
  647. if (GQUtils
  648. .eq(childElm.getNodeName(), previous.getNodeName())) {
  649. matchingElms.addNode(childElm);
  650. }
  651. iteratorNext += sequence.add;
  652. }
  653. }
  654. childElm = SelectorEngine.getNextSibling(childElm);
  655. }
  656. setHasChildElms(prevParent, true);
  657. prevParents.addNode(prevParent);
  658. }
  659. }
  660. clearChildElms(prevParents);
  661. }
  662. }
  663. return matchingElms;
  664. }
  665. private JSArray getNthOfTypePseudo(JSArray previousMatch, String pseudoValue,
  666. JSArray prevParents, JSArray matchingElms) {
  667. Node previous;
  668. if (pseudoValue.startsWith("n")) {
  669. matchingElms = previousMatch;
  670. } else {
  671. Sequence sequence = getSequence(pseudoValue);
  672. if (sequence != null) {
  673. for (int p = 0, plen = previousMatch.size(); p < plen; p++) {
  674. previous = previousMatch.getNode(p);
  675. Node prevParent = previous.getParentNode();
  676. if (!hasChildElms(prevParent)) {
  677. int iteratorNext = sequence.start;
  678. int childCount = 0;
  679. Node childElm = prevParent.getFirstChild();
  680. while (GQUtils.truth(childElm) && (sequence.max < 0
  681. || iteratorNext <= sequence.max)) {
  682. if (GQUtils
  683. .eq(childElm.getNodeName(), previous.getNodeName())) {
  684. if (++childCount == iteratorNext) {
  685. matchingElms.addNode(childElm);
  686. iteratorNext += sequence.add;
  687. }
  688. }
  689. childElm = SelectorEngine.getNextSibling(childElm);
  690. }
  691. setHasChildElms(prevParent, true);
  692. prevParents.addNode(prevParent);
  693. }
  694. }
  695. clearChildElms(prevParents);
  696. }
  697. }
  698. return matchingElms;
  699. }
  700. private void getOnlyChildPseudo(JSArray previousMatch, JSArray matchingElms) {
  701. Node previous;
  702. Node next;
  703. Node prev;
  704. Node kParent = null;
  705. for (int k = 0, klen = previousMatch.size(); k < klen; k++) {
  706. prev = next = previous = previousMatch.getNode(k);
  707. Node prevParent = previous.getParentNode();
  708. if (prevParent != kParent) {
  709. while (
  710. GQUtils.truth(prev = SelectorEngine.getPreviousSibling(prev))
  711. && prev.getNodeType() != Node.ELEMENT_NODE) {
  712. }
  713. while (GQUtils.truth(next = SelectorEngine.getNextSibling(next))
  714. && next.getNodeType() != Node.ELEMENT_NODE) {
  715. }
  716. if (!GQUtils.truth(prev) && !GQUtils.truth(next)) {
  717. matchingElms.addNode(previous);
  718. }
  719. kParent = prevParent;
  720. }
  721. }
  722. }
  723. private void getOnlyOfTypePseudo(JSArray previousMatch,
  724. JSArray matchingElms) {
  725. Node previous;
  726. Node next;
  727. Node prev;
  728. Node oParent = null;
  729. for (int o = 0, olen = previousMatch.size(); o < olen; o++) {
  730. prev = next = previous = previousMatch.getNode(o);
  731. Node prevParent = previous.getParentNode();
  732. if (prevParent != oParent) {
  733. while (
  734. GQUtils.truth(prev = SelectorEngine.getPreviousSibling(prev))
  735. && !GQUtils
  736. .eq(prev.getNodeName(), previous.getNodeName())) {
  737. }
  738. while (GQUtils.truth(next = SelectorEngine.getNextSibling(next))
  739. && !GQUtils.eq(next.getNodeName(), previous.getNodeName())) {
  740. }
  741. if (!GQUtils.truth(prev) && !GQUtils.truth(next)) {
  742. matchingElms.addNode(previous);
  743. }
  744. oParent = prevParent;
  745. }
  746. }
  747. }
  748. }