function next(node) {
  if (node.firstChild)
    return node.firstChild;
  while (node && !node.nextSibling)
    node = node.parentNode;
  if (node && node.nextSibling)
    return node.nextSibling;
  return node;
}
function depth(node) {
  var d = 0;
  while (node) {
    ++d;
    node = node.parentNode;
  }
  return d;
}
function stringify(node) {
  if (node.nodeType == 1 /* ELEMENT_NODE */) {
    return depth(node) + ': <' + node.nodeName + '>';
  } else if (node.nodeType == 3 /* TEXT_NODE */) {
    return depth(node) + ': #text';
  } else if (node.nodeType == 8 /* COMMENT_NODE */) {
    return depth(node) + ': #comment';
  } else if (node.nodeType == 9 /* DOCUMENT_NODE */) {
    return depth(node) + ': #document';
  } else if (node.nodeType == 10 /* DOCUMENT_TYPE_NODE */) {
    return depth(node) + ': #doctype';
  } else {
    return depth(node) + ': ' + node.nodeName + ' (' + node.nodeType + ';' + node.nodeValue + ')';
  }
}
function fail(s) {
  say(s, 'fail');
}
function pass(s) {
  say(s, 'pass');
}
function say(s, c) {
  var span = document.createElement('SPAN');
  span.className = c;
  span.appendChild(document.createTextNode(s));
  document.getElementById('results').appendChild(span);
}

var node = document;

function passNode() {
  pass(stringify(node));
}
function failNode(s) {
  fail(stringify(node) + ' [expected: ' + s + ']');
}

function element(tagName, d) {
  if (node.nodeType == 1 /* ELEMENT_NODE */ &&
      node.nodeName == tagName &&
      depth(node) == d) {
    passNode();
  } else {
    failNode(d +': <' + tagName + '>');
  }
  node = next(node);
}
function text(d) {
  if (node.nodeType == 3 /* TEXT_NODE */ &&
      depth(node) == d) {
    passNode();
  } else {
    failNode(d + ': #text');
  }
  node = next(node);
}
function whitespace(d) {
  if (node.nodeType == 3 /* TEXT_NODE */ &&
      // XXX check is whitespace &&
      depth(node) == d) {
    passNode();
    node = next(node);
  } else {
    say('(whitespace expected but not found)', 'note');
  }
}
function comment(d) {
  if (node.nodeType == 8 /* COMMENT_NODE */ &&
      depth(node) == d) {
    passNode();
  } else {
    failNode(d + ': #comment');
  }
  node = next(node);
}
function doc(d) {
  if (node.nodeType == 9 /* DOCUMENT_NODE */ &&
      depth(node) == d) {
    passNode();
  } else {
    failNode(d + ': #document');
  }
  node = next(node);
}
function doctype(d) {
  if (node.nodeType == 10 /* DOCUMENT_TYPE_NODE */ &&
      depth(node) == d) {
    passNode();
    node = next(node);
  } else {
    fail('(doctype expected but not found)');
  }
}

