/**
   * Overwrites the default rich text area behavior when the Tab key is being pressed.
   *
   * @param event the native event that was fired
   */
  protected void onTab(Event event) {
    Selection selection = getTextArea().getDocument().getSelection();
    if (selection.getRangeCount() == 0) {
      return;
    }

    // Prevent the default browser behavior.
    event.xPreventDefault();

    // See in which context the tab key has been pressed.
    Range range = selection.getRangeAt(0);
    List<String> specialTags = Arrays.asList(new String[] {LI, TD, TH});
    Node ancestor = range.getStartContainer();
    int index = specialTags.indexOf(ancestor.getNodeName().toLowerCase());
    while (ancestor != null && index < 0) {
      ancestor = ancestor.getParentNode();
      if (ancestor != null) {
        index = specialTags.indexOf(ancestor.getNodeName().toLowerCase());
      }
    }

    // Handle the tab key depending on the context.
    switch (index) {
      case 0:
        onTabInListItem(event, ancestor);
        break;
      case 1:
      case 2:
        onTabInTableCell(event, (TableCellElement) ancestor);
        break;
      default:
        onTabDefault(event);
        break;
    }
  }
 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 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 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 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);
      }
    }
  }
 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;
 }