private void getEnabledPseudo(JsNodeArray previousMatch, JsNodeArray matchingElms) {
   Node previous;
   for (int q = 0, qlen = previousMatch.size(); q < qlen; q++) {
     previous = previousMatch.getNode(q);
     if (enabled(previous)) {
       matchingElms.addNode(previous);
     }
   }
 }
 private static void getGeneralSiblingNodes(
     JsNodeArray matchingElms, JsObjectArray<String> nextTag, JsRegexp nextRegExp, Node prevRef) {
   while (JsUtils.truth((prevRef = SelectorEngine.getNextSibling(prevRef))) && !isAdded(prevRef)) {
     if (!JsUtils.truth(nextTag) || nextRegExp.test(prevRef.getNodeName())) {
       setAdded(prevRef, true);
       matchingElms.addNode(prevRef);
     }
   }
 }
 private void getEmptyPseudo(JsNodeArray previousMatch, JsNodeArray matchingElms) {
   Node previous;
   for (int q = 0, qlen = previousMatch.size(); q < qlen; q++) {
     previous = previousMatch.getNode(q);
     if (!previous.hasChildNodes()) {
       matchingElms.addNode(previous);
     }
   }
 }
 private void getDefaultPseudo(
     JsNodeArray previousMatch, String pseudoClass, String pseudoValue, JsNodeArray matchingElms) {
   Node previous;
   for (int w = 0, wlen = previousMatch.size(); w < wlen; w++) {
     previous = previousMatch.getElement(w);
     if (JsUtils.eq(((Element) previous).getAttribute(pseudoClass), pseudoValue)) {
       matchingElms.addNode(previous);
     }
   }
 }
 private static void getSiblingNodes(
     JsNodeArray matchingElms, JsObjectArray<String> nextTag, JsRegexp nextRegExp, Node prevRef) {
   while (JsUtils.truth(prevRef = SelectorEngine.getNextSibling(prevRef))
       && prevRef.getNodeType() != Node.ELEMENT_NODE) {}
   if (JsUtils.truth(prevRef)) {
     if (!JsUtils.truth(nextTag) || nextRegExp.test(prevRef.getNodeName())) {
       matchingElms.addNode(prevRef);
     }
   }
 }
 private static void getDescendantNodes(
     JsNodeArray matchingElms, String nextTagStr, Node prevRef) {
   NodeList<Element> children = getElementsByTagName(nextTagStr, prevRef);
   for (int k = 0, klen = children.getLength(); k < klen; k++) {
     Node child = children.getItem(k);
     if (child.getParentNode() == prevRef) {
       matchingElms.addNode(child);
     }
   }
 }
 private void getContainsPseudo(
     JsNodeArray previousMatch, String pseudoValue, JsNodeArray matchingElms) {
   Node previous;
   for (int q = 0, qlen = previousMatch.size(); q < qlen; q++) {
     previous = previousMatch.getNode(q);
     if (!isAdded(previous)) {
       if (((Element) previous).getInnerText().indexOf(pseudoValue) != -1) {
         setAdded(previous, true);
         matchingElms.addNode(previous);
       }
     }
   }
 }
 private JsNodeArray getNthChildPseudo(
     JsNodeArray previousMatch,
     String pseudoValue,
     JsNodeArray prevParents,
     JsNodeArray matchingElms) {
   Node previous;
   if (JsUtils.eq(pseudoValue, "n")) {
     matchingElms = previousMatch;
   } else {
     Sequence sequence = getSequence(pseudoValue);
     if (sequence != null) {
       for (int l = 0, llen = previousMatch.size(); l < llen; l++) {
         previous = previousMatch.getNode(l);
         Node prevParent = previous.getParentNode();
         if (!hasChildElms(prevParent)) {
           int iteratorNext = sequence.start;
           int childCount = 0;
           Node childElm = prevParent.getFirstChild();
           while (childElm != null && (sequence.max < 0 || iteratorNext <= sequence.max)) {
             if (childElm.getNodeType() == Node.ELEMENT_NODE) {
               if (++childCount == iteratorNext) {
                 if (JsUtils.eq(childElm.getNodeName(), previous.getNodeName())) {
                   matchingElms.addNode(childElm);
                 }
                 iteratorNext += sequence.add;
               }
             }
             childElm = SelectorEngine.getNextSibling(childElm);
           }
           setHasChildElms(prevParent, true);
           prevParents.addNode(prevParent);
         }
       }
       clearChildElms(prevParents);
     }
   }
   return matchingElms;
 }
 private JsNodeArray getNotPseudo(
     JsNodeArray previousMatch, String pseudoValue, JsNodeArray matchingElms) {
   if (new JsRegexp("(:\\w+[\\w\\-]*)$").test(pseudoValue)) {
     matchingElms =
         subtractArray(
             previousMatch, getElementsByPseudo(previousMatch, pseudoValue.substring(1), ""));
   } else {
     pseudoValue = pseudoValue.replace("^\\[#([\\w\\u00C0-\\uFFFF\\-\\_]+)\\]$", "[id=$1]");
     JsObjectArray<String> notTag = new JsRegexp("^(\\w+)").exec(pseudoValue);
     JsObjectArray<String> notClass =
         new JsRegexp("^\\.([\\w\u00C0-\uFFFF\\-_]+)").exec(pseudoValue);
     JsObjectArray<String> notAttr =
         new JsRegexp("\\[(\\w+)(\\^|\\$|\\*|\\||~)?=?([\\w\\u00C0-\\uFFFF\\s\\-_\\.]+)?\\]")
             .exec(pseudoValue);
     JsRegexp notRegExp =
         new JsRegexp(
             "(^|\\s)"
                 + (JsUtils.truth(notTag)
                     ? notTag.get(1)
                     : JsUtils.truth(notClass) ? notClass.get(1) : "")
                 + "(\\s|$)",
             "i");
     if (JsUtils.truth(notAttr)) {
       String notAttribute =
           JsUtils.truth(notAttr.get(3)) ? notAttr.get(3).replace("\\.", "\\.") : null;
       String notMatchingAttrVal = attrToRegExp(notAttribute, notAttr.get(2));
       notRegExp = new JsRegexp(notMatchingAttrVal, "i");
     }
     for (int v = 0, vlen = previousMatch.size(); v < vlen; v++) {
       Element notElm = previousMatch.getElement(v);
       Element addElm = null;
       if (JsUtils.truth(notTag) && !notRegExp.test(notElm.getNodeName())) {
         addElm = notElm;
       } else if (JsUtils.truth(notClass) && !notRegExp.test(notElm.getClassName())) {
         addElm = notElm;
       } else if (JsUtils.truth(notAttr)) {
         String att = getAttr(notElm, notAttr.get(1));
         if (!JsUtils.truth(att) || !notRegExp.test(att)) {
           addElm = notElm;
         }
       }
       if (JsUtils.truth(addElm) && !isAdded(addElm)) {
         setAdded(addElm, true);
         matchingElms.addNode(addElm);
       }
     }
   }
   return matchingElms;
 }
 private void getFirstChildPseudo(
     JsNodeArray previousMatch, boolean previousDir, JsNodeArray matchingElms) {
   Node next;
   Node previous;
   for (int j = 0, jlen = previousMatch.size(); j < jlen; j++) {
     previous = next = previousMatch.getElement(j);
     if (previousDir) {
       while (JsUtils.truth((next = SelectorEngine.getPreviousSibling(next)))
           && next.getNodeType() != Node.ELEMENT_NODE) {}
     } else {
       while (JsUtils.truth((next = SelectorEngine.getNextSibling(next)))
           && next.getNodeType() != Node.ELEMENT_NODE) {}
     }
     if (!JsUtils.truth(next)) {
       matchingElms.addNode(previous);
     }
   }
 }
 private void getOnlyOfTypePseudo(JsNodeArray previousMatch, JsNodeArray matchingElms) {
   Node previous;
   Node next;
   Node prev;
   Node oParent = null;
   for (int o = 0, olen = previousMatch.size(); o < olen; o++) {
     prev = next = previous = previousMatch.getNode(o);
     Node prevParent = previous.getParentNode();
     if (prevParent != oParent) {
       while (JsUtils.truth(prev = SelectorEngine.getPreviousSibling(prev))
           && !JsUtils.eq(prev.getNodeName(), previous.getNodeName())) {}
       while (JsUtils.truth(next = SelectorEngine.getNextSibling(next))
           && !JsUtils.eq(next.getNodeName(), previous.getNodeName())) {}
       if (!JsUtils.truth(prev) && !JsUtils.truth(next)) {
         matchingElms.addNode(previous);
       }
       oParent = prevParent;
     }
   }
 }
 private void getOnlyChildPseudo(JsNodeArray previousMatch, JsNodeArray matchingElms) {
   Node previous;
   Node next;
   Node prev;
   Node kParent = null;
   for (int k = 0, klen = previousMatch.size(); k < klen; k++) {
     prev = next = previous = previousMatch.getNode(k);
     Node prevParent = previous.getParentNode();
     if (prevParent != kParent) {
       while (JsUtils.truth(prev = SelectorEngine.getPreviousSibling(prev))
           && prev.getNodeType() != Node.ELEMENT_NODE) {}
       while (JsUtils.truth(next = SelectorEngine.getNextSibling(next))
           && next.getNodeType() != Node.ELEMENT_NODE) {}
       if (!JsUtils.truth(prev) && !JsUtils.truth(next)) {
         matchingElms.addNode(previous);
       }
       kParent = prevParent;
     }
   }
 }
  private void getFirstOfTypePseudo(
      JsNodeArray previousMatch, boolean previousDir, JsNodeArray matchingElms) {
    Node previous;
    Node next;
    for (int n = 0, nlen = previousMatch.size(); n < nlen; n++) {
      next = previous = previousMatch.getNode(n);

      if (previousDir) {
        while (JsUtils.truth(next = SelectorEngine.getPreviousSibling(next))
            && !JsUtils.eq(next.getNodeName(), previous.getNodeName())) {}
      } else {
        while (JsUtils.truth(next = SelectorEngine.getNextSibling(next))
            && !JsUtils.eq(next.getNodeName(), previous.getNodeName())) {}
      }

      if (!JsUtils.truth(next)) {
        matchingElms.addNode(previous);
      }
    }
  }
  public NodeList<Element> select(String sel, Node ctx) {
    String selectors[] = sel.replace("\\s*(,)\\s*", "$1").split(",");
    boolean identical = false;
    JsNodeArray elm = JsNodeArray.create();
    for (int a = 0, len = selectors.length; a < len; a++) {
      if (a > 0) {
        identical = false;
        for (int b = 0, bl = a; b < bl; b++) {
          if (JsUtils.eq(selectors[a], selectors[b])) {
            identical = true;
            break;
          }
        }
        if (identical) {
          continue;
        }
      }
      String currentRule = selectors[a];
      JsObjectArray<String> cssSelectors = selectorSplitRegExp.match(currentRule);
      JsNodeArray prevElem = JsNodeArray.create(ctx);
      for (int i = 0, slen = cssSelectors.length(); i < slen; i++) {
        JsNodeArray matchingElms = JsNodeArray.create();
        String rule = cssSelectors.get(i);
        if (i > 0 && childOrSiblingRefRegExp.test(rule)) {
          JsObjectArray<String> childOrSiblingRef = childOrSiblingRefRegExp.exec(rule);
          if (JsUtils.truth(childOrSiblingRef)) {
            JsObjectArray<String> nextTag = new JsRegexp("^\\w+").exec(cssSelectors.get(i + 1));
            JsRegexp nextRegExp = null;
            String nextTagStr = null;
            if (JsUtils.truth(nextTag)) {
              nextTagStr = nextTag.get(0);
              nextRegExp = new JsRegexp("(^|\\s)" + nextTagStr + "(\\s|$)", "i");
            }
            for (int j = 0, jlen = prevElem.size(); j < jlen; j++) {
              Node prevRef = prevElem.getNode(j);
              String ref = childOrSiblingRef.get(0);
              if (JsUtils.eq(">", ref)) {
                getDescendantNodes(matchingElms, nextTagStr, prevRef);
              } else if (JsUtils.eq("+", ref)) {
                getSiblingNodes(matchingElms, nextTag, nextRegExp, prevRef);
              } else if (JsUtils.eq("~", ref)) {
                getGeneralSiblingNodes(matchingElms, nextTag, nextRegExp, prevRef);
              }
            }
            prevElem = matchingElms;
            clearAdded(prevElem);
            rule = cssSelectors.get(++i);
            if (new JsRegexp("^\\w+$").test(rule)) {
              continue;
            }
            setSkipTag(prevElem, true);
          }
        }
        JsObjectArray<String> cssSelector = cssSelectorRegExp.exec(rule);
        SplitRule splitRule =
            new SplitRule(
                !JsUtils.truth(cssSelector.get(1)) || JsUtils.eq(cssSelector.get(3), "*")
                    ? "*"
                    : cssSelector.get(1),
                !JsUtils.eq(cssSelector.get(3), "*") ? cssSelector.get(2) : null,
                cssSelector.get(4),
                cssSelector.get(6),
                cssSelector.get(10));
        if (JsUtils.truth(splitRule.id)) {
          Element domelem = Document.get().getElementById(splitRule.id.substring(1));
          if (JsUtils.truth(domelem)) {
            matchingElms = JsNodeArray.create(domelem);
          }
          prevElem = matchingElms;
        } else if (JsUtils.truth(splitRule.tag) && !isSkipped(prevElem)) {
          if (i == 0 && matchingElms.size() == 0 && prevElem.size() == 1) {
            prevElem =
                matchingElms =
                    JsNodeArray.create(getElementsByTagName(splitRule.tag, prevElem.getNode(0)));
          } else {
            NodeList<Element> tagCollectionMatches;
            for (int l = 0, ll = prevElem.size(); l < ll; l++) {
              tagCollectionMatches = getElementsByTagName(splitRule.tag, prevElem.getNode(l));
              for (int m = 0, mlen = tagCollectionMatches.getLength(); m < mlen; m++) {
                Node tagMatch = tagCollectionMatches.getItem(m);

                if (!isAdded(tagMatch)) {
                  setAdded(tagMatch, true);
                  matchingElms.addNode(tagMatch);
                }
              }
            }
            prevElem = matchingElms;
            clearAdded(prevElem);
          }
          if (matchingElms.size() == 0) {
            break;
          }
          setSkipTag(prevElem, false);
          if (JsUtils.truth(splitRule.allClasses)) {
            String[] allClasses = splitRule.allClasses.replaceFirst("^\\.", "").split("\\.");
            JsRegexp[] regExpClassNames = new JsRegexp[allClasses.length];
            for (int n = 0, nl = allClasses.length; n < nl; n++) {
              regExpClassNames[n] = new JsRegexp("(^|\\s)" + allClasses[n] + "(\\s|$)");
            }
            JsNodeArray matchingClassElms = JsNodeArray.create();
            for (int o = 0, olen = prevElem.size(); o < olen; o++) {
              Element current = prevElem.getElement(o);
              String elmClass = current.getClassName();
              boolean addElm = false;
              if (JsUtils.truth(elmClass) && !isAdded(current)) {
                for (int p = 0, pl = regExpClassNames.length; p < pl; p++) {
                  addElm = regExpClassNames[p].test(elmClass);
                  if (!addElm) {
                    break;
                  }
                }
                if (addElm) {
                  setAdded(current, true);
                  matchingClassElms.addNode(current);
                }
              }
            }
            clearAdded(prevElem);
            prevElem = matchingElms = matchingClassElms;
          }
          if (JsUtils.truth(splitRule.allAttr)) {
            JsObjectArray<String> allAttr = JsRegexp.match("\\[[^\\]]+\\]", "g", splitRule.allAttr);
            JsRegexp[] regExpAttributes = new JsRegexp[allAttr.length()];
            String[] regExpAttributesStr = new String[allAttr.length()];
            JsRegexp attributeMatchRegExp =
                new JsRegexp("(\\w+)(\\^|\\$|\\*|\\||~)?=?[\"']?([\\w\u00C0-\uFFFF\\s\\-_\\.]+)?");
            for (int q = 0, ql = allAttr.length(); q < ql; q++) {
              JsObjectArray<String> attributeMatch = attributeMatchRegExp.exec(allAttr.get(q));
              String attributeValue =
                  JsUtils.truth(attributeMatch.get(3))
                      ? attributeMatch.get(3).replaceAll("\\.", "\\.")
                      : null;
              String attrVal =
                  attrToRegExp(attributeValue, (JsUtils.or(attributeMatch.get(2), null)));
              regExpAttributes[q] = (JsUtils.truth(attrVal) ? new JsRegexp(attrVal) : null);
              regExpAttributesStr[q] = attributeMatch.get(1);
            }
            JsNodeArray matchingAttributeElms = JsNodeArray.create();

            for (int r = 0, rlen = matchingElms.size(); r < rlen; r++) {
              Element current = matchingElms.getElement(r);
              boolean addElm = false;
              for (int s = 0, sl = regExpAttributes.length; s < sl; s++) {
                addElm = false;
                JsRegexp attributeRegexp = regExpAttributes[s];
                String currentAttr = getAttr(current, regExpAttributesStr[s]);
                if (JsUtils.truth(currentAttr) && currentAttr.length() != 0) {
                  if (attributeRegexp == null || attributeRegexp.test(currentAttr)) {
                    addElm = true;
                  }
                }
                if (!addElm) {
                  break;
                }
              }
              if (addElm) {
                matchingAttributeElms.addNode(current);
              }
            }
            prevElem = matchingElms = matchingAttributeElms;
          }
          if (JsUtils.truth(splitRule.allPseudos)) {
            JsRegexp pseudoSplitRegExp = new JsRegexp(":(\\w[\\w\\-]*)(\\(([^\\)]+)\\))?");

            JsObjectArray<String> allPseudos =
                JsRegexp.match("(:\\w+[\\w\\-]*)(\\([^\\)]+\\))?", "g", splitRule.allPseudos);
            for (int t = 0, tl = allPseudos.length(); t < tl; t++) {
              JsObjectArray<String> pseudo = pseudoSplitRegExp.match(allPseudos.get(t));
              String pseudoClass =
                  JsUtils.truth(pseudo.get(1)) ? pseudo.get(1).toLowerCase() : null;
              String pseudoValue = JsUtils.truth(pseudo.get(3)) ? pseudo.get(3) : null;
              matchingElms = getElementsByPseudo(matchingElms, pseudoClass, pseudoValue);
              clearAdded(matchingElms);
            }
            prevElem = matchingElms;
          }
        }
      }
      elm.pushAll(prevElem);
    }

    return JsUtils.unique(elm.<JsArray<Element>>cast()).cast();
  }