// hit when the node is first seen
  public void head(Node node, int depth) {
    String name = node.nodeName();
    if (name.equals("li")) append('\n');
    else if (node.toString().startsWith("<select")) {
      append1("[SELECT]");
      append(tab);
    } else if (node.outerHtml().startsWith("<option")) {
      // append1(node.attr("value")+":");append1(" ");
      TextNodeVisitor textVisitor = new TextNodeVisitor();
      node.traverse(textVisitor);
      append1("{" + textVisitor.toString() + "}");

    } else if (node.outerHtml().startsWith("<input")) {
      if (node.attr("type").equals("input")) append1("[INPUT]" + node.attr("maxLength"));
    } else if (node.outerHtml().startsWith("<span")) {
      TextNodeVisitor textVisitor = new TextNodeVisitor();
      node.traverse(textVisitor);
      append1(":" + textVisitor.toString() + " ");
    }
  }
  public static boolean overlaps(List<Node> nodes, List<Node> targets) {
    final Collection<Node> all = new HashSet<Node>();
    for (Node target : targets) {
      target.traverse(
          new NodeVisitor() {
            @Override
            public void tail(Node n, int d) {}

            @Override
            public void head(Node n, int d) {
              all.add(n);
            }
          });
    }
    final boolean overlaps[] = new boolean[1];
    for (Node node : nodes) {
      node.traverse(
          new NodeVisitor() {
            @Override
            public void tail(Node n, int d) {}

            @Override
            public void head(Node n, int d) {
              if (!overlaps[0]) {
                if (all.contains(n)) {
                  overlaps[0] = true;
                }
              }
            }
          });
      if (overlaps[0]) {
        return true;
      }
    }
    return false;
  }
  private static void clean(Node node) {
    node.traverse(
        new NodeVisitor() {
          @Override
          public void tail(Node node, int depth) {}

          @Override
          public void head(Node node, int depth) {
            String classAttr = node.attr("class");
            classAttr = NodeUtil.cleanClass(classAttr);
            if (CommonUtil.isEmpty(classAttr)) {
              node.removeAttr("class");
            } else {
              node.attr("class", classAttr);
            }
          }
        });
  }
  static void markVisible(Node node) {
    if (node != null) {
      if (node.nodeName().equals("select")) {
        node.traverse(
            new NodeVisitor() {
              @Override
              public void tail(Node n, int d) {}

              @Override
              public void head(Node n, int d) {
                n.attr("class", hiddenMarker.matcher(n.attr("class")).replaceAll(""));
              }
            });
      }
      node.attr("class", hiddenMarker.matcher(node.attr("class")).replaceAll(""));
      markVisible(node.parent());
    }
  }
  static void markFiltered(Node node, final boolean lenient) {
    if (lenient) {
      if (!isFilteredLenient(node)) {
        node.attr("class", node.attr("class") + " " + FILTERED_LENIENT_MARKER + " ");
      }
    } else {
      node.traverse(
          new NodeVisitor() {
            @Override
            public void tail(Node n, int d) {}

            @Override
            public void head(Node n, int d) {
              if (!isFiltered(n)) {
                n.attr("class", n.attr("class") + " " + FILTERED_MARKER + " ");
              }
            }
          });
    }
  }