<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>goog.dom.TagIterator Tests</title>
<script src="../base.js"></script>
<script>
  goog.require('goog.testing.dom');
  goog.require('goog.testing.jsunit');
  goog.require('goog.dom.TagIterator');
</script>
</head>
<body>

<div id="test"><a href="#">T<b>e</b>xt</a><span></span><p>Text</p></div>
<ul id="test2"><li>Not<li>Closed</ul>
<div id="test3">text</div>
<div id="testSplice"></div>

<script>
  var it;
  var pos;

  function assertStartTag(type) {
    assertEquals('Position ' + pos + ' should be start tag',
        goog.dom.TagWalkType.START_TAG, it.tagType);
    assertTrue('isStartTag should return true', it.isStartTag());
    assertFalse('isEndTag should return false', it.isEndTag());
    assertFalse('isNonElement should return false', it.isNonElement());
    assertEquals('Position ' + pos + ' should be ' + type, type,
        it.node.tagName);
  }

  function assertEndTag(type) {
    assertEquals('Position ' + pos + ' should be end tag',
        goog.dom.TagWalkType.END_TAG, it.tagType);
    assertFalse('isStartTag should return false', it.isStartTag());
    assertTrue('isEndTag should return true', it.isEndTag());
    assertFalse('isNonElement should return false', it.isNonElement());
    assertEquals('Position ' + pos + ' should be ' + type, type,
        it.node.tagName);
  }

  function assertTextNode(value) {
    assertEquals('Position ' + pos + ' should be text node',
        goog.dom.TagWalkType.OTHER, it.tagType);
    assertFalse('isStartTag should return false', it.isStartTag());
    assertFalse('isEndTag should return false', it.isEndTag());
    assertTrue('isNonElement should return true', it.isNonElement());
    assertEquals('Position ' + pos + ' should be "' + value + '"', value,
        it.node.nodeValue);
  }

  function testBasicHTML() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test'));
    pos = 0;

    goog.iter.forEach(it, function() {
      pos++;
      switch (pos) {
        case 1:
          assertStartTag('DIV');
          break;
        case 2:
          assertStartTag('A');
          break;
        case 3:
          assertTextNode('T');
          break;
        case 4:
          assertStartTag('B');
          assertEquals('Depth at <B> should be 3', 3, it.depth);
          break;
        case 5:
          assertTextNode('e');
          break;
        case 6:
          assertEndTag('B');
          break;
        case 7:
          assertTextNode('xt');
          break;
        case 8:
          assertEndTag('A');
          break;
        case 9:
          assertStartTag('SPAN');
          break;
        case 10:
          assertEndTag('SPAN');
          break;
        case 11:
          assertStartTag('P');
          break;
        case 12:
          assertTextNode('Text');
          break;
        case 13:
          assertEndTag('P');
          break;
        case 14:
          assertEndTag('DIV');
          assertEquals('Depth at end should be 0', 0, it.depth);
          break;
        default:
          throw goog.iter.StopIteration;
      }
    });
  }

  function testSkipTag() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test'));
    pos = 0;

    goog.iter.forEach(it, function() {
      pos++;
      switch (pos) {
        case 1:
          assertStartTag('DIV');
          break;
        case 2:
          assertStartTag('A');
          it.skipTag();
          break;
        case 3:
          assertStartTag('SPAN');
          break;
        case 4:
          assertEndTag('SPAN');
          break;
        case 5:
          assertStartTag('P');
          break;
        case 6:
          assertTextNode('Text');
          break;
        case 7:
          assertEndTag('P');
          break;
        case 8:
          assertEndTag('DIV');
          assertEquals('Depth at end should be 0', 0, it.depth);
          break;
        default:
          throw goog.iter.StopIteration;
      }
    });
  }

  function testRestartTag() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test'));
    pos = 0;
    var done = false;

    goog.iter.forEach(it, function() {
      pos++;
      switch (pos) {
        case 1:
          assertStartTag('DIV');
          break;
        case 2:
          assertStartTag('A');
          it.skipTag();
          break;
        case 3:
          assertStartTag('SPAN');
          break;
        case 4:
          assertEndTag('SPAN');
          break;
        case 5:
          assertStartTag('P');
          break;
        case 6:
          assertTextNode('Text');
          break;
        case 7:
          assertEndTag('P');
          break;
        case 8:
          assertEndTag('DIV');
          assertEquals('Depth at end should be 0', 0, it.depth);

          // Do them all again, starting after this element.
          if (!done) {
            pos = 1;
            it.restartTag();
            done = true;
          }
          break;
        default:
          throw goog.iter.StopIteration;
      }
    });
  }


  function testSkipTagReverse() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test'), true);
    pos = 9;

    goog.iter.forEach(it, function() {
      pos--;
      switch (pos) {
        case 1:
          assertStartTag('DIV');
          assertEquals('Depth at end should be 0', 0, it.depth);
          break;
        case 2:
          assertEndTag('A');
          it.skipTag();
          break;
        case 3:
          assertStartTag('SPAN');
          break;
        case 4:
          assertEndTag('SPAN');
          break;
        case 5:
          assertStartTag('P');
          break;
        case 6:
          assertTextNode('Text');
          break;
        case 7:
          assertEndTag('P');
          break;
        case 8:
          assertEndTag('DIV');
          break;
        default:
          throw goog.iter.StopIteration;
      }
    });
  }


  function testUnclosedLI() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test2'));
    pos = 0;

    goog.iter.forEach(it, function() {
      pos++;
      switch (pos) {
        case 1:
          assertStartTag('UL');
          break;
        case 2:
          assertStartTag('LI');
          assertEquals('Depth at <LI> should be 2', 2, it.depth);
          break;
        case 3:
          assertTextNode('Not');
          break;
        case 4:
          assertEndTag('LI');
          break;
        case 5:
          assertStartTag('LI');
          assertEquals('Depth at second <LI> should be 2', 2, it.depth);
          break;
        case 6:
          assertTextNode('Closed');
          break;
        case 7:
          assertEndTag('LI');
          break;
        case 8:
          assertEndTag('UL');
          assertEquals('Depth at end should be 0', 0, it.depth);
          break;
        default:
          throw goog.iter.StopIteration;
      }
    });
  }

  function testReversedUnclosedLI() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test2'), true);
    pos = 9;

    goog.iter.forEach(it, function() {
      pos--;
      switch (pos) {
        case 1:
          assertStartTag('UL');
          assertEquals('Depth at start should be 0', 0, it.depth);
          break;
        case 2:
          assertStartTag('LI');
          break;
        case 3:
          assertTextNode('Not');
          break;
        case 4:
          assertEndTag('LI');
          assertEquals('Depth at <LI> should be 2', 2, it.depth);
          break;
        case 5:
          assertStartTag('LI');
          break;
        case 6:
          assertTextNode('Closed');
          break;
        case 7:
          assertEndTag('LI');
          assertEquals('Depth at second <LI> should be 2', 2, it.depth);            
          break;
        case 8:
          assertEndTag('UL');
          break;
        default:
          throw goog.iter.StopIteration;
      }
    });
  }

  function testConstrained() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test3'), false, false);
    pos = 0;

    goog.iter.forEach(it, function() {
      pos++;
      switch (pos) {
        case 1:
          assertStartTag('DIV');
          break;
        case 2:
          assertTextNode('text');
          break;
        case 3:
          assertEndTag('DIV');
          break;
      }
    });

    assertEquals('Constrained iterator should stop at position 3.', 3, pos);
  }

  function testUnconstrained() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test3'), false, true);
    pos = 0;

    goog.iter.forEach(it, function() {
      pos++;
      switch (pos) {
        case 1:
          assertStartTag('DIV');
          break;
        case 2:
          assertTextNode('text');
          break;
        case 3:
          assertEndTag('DIV');
          break;
      }
    });

    assertNotEquals('Unonstrained iterator should not stop at position 3.', 3,
        pos);
  }

  function testConstrainedText() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test3').firstChild,
        false, false);
    pos = 0;

    goog.iter.forEach(it, function() {
      pos++;
      switch (pos) {
        case 1:
          assertTextNode('text');
          break;
      }
    });

    assertEquals('Constrained text iterator should stop at position 1.', 1,
        pos);
  }

  function testReverseConstrained() {
    it = new goog.dom.TagIterator(goog.dom.getElement('test3'), true, false);
    pos = 4;

    goog.iter.forEach(it, function() {
      pos--;
      switch (pos) {
        case 1:
          assertStartTag('DIV');
          break;
        case 2:
          assertTextNode('text');
          break;
        case 3:
          assertEndTag('DIV');
          break;
      }
    });

    assertEquals('Constrained reversed iterator should stop at position 1.', 1,
        pos);
  }

  function testSpliceRemoveSingleNode() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = '<br/>';
    it = new goog.dom.TagIterator(testDiv.firstChild);

    goog.iter.forEach(it, function(node, dummy, i) {
      i.splice();
    });

    assertEquals('Node not removed', 0, testDiv.childNodes.length);
  }

  function testSpliceRemoveFirstTextNode() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = 'hello<b>world</b><em>goodbye</em>';
    it = new goog.dom.TagIterator(testDiv.firstChild, false, true);

    goog.iter.forEach(it, function(node, dummy, i) {
      if (node.nodeType == 3 && node.data == 'hello') {
        i.splice();
      }
      if (node.nodeName == 'EM') {
        i.splice(goog.dom.createDom('I', null, node.childNodes));
      }
    });

    goog.testing.dom.assertHtmlMatches('<b>world</b><i>goodbye</i>',
        testDiv.innerHTML);
  }

  function testSpliceReplaceFirstTextNode() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = 'hello<b>world</b>';
    it = new goog.dom.TagIterator(testDiv.firstChild, false, true);

    goog.iter.forEach(it, function(node, dummy, i) {
      if (node.nodeType == 3 && node.data == 'hello') {
        i.splice(goog.dom.createDom('EM', null, 'HELLO'));
      } else if (node.nodeName == 'EM') {
        i.splice(goog.dom.createDom('I', null, node.childNodes));
      }
    });

    goog.testing.dom.assertHtmlMatches('<i>HELLO</i><b>world</b>',
        testDiv.innerHTML);
  }

  function testSpliceReplaceSingleNode() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = '<br/>';
    it = new goog.dom.TagIterator(testDiv.firstChild);

    goog.iter.forEach(it, function(node, dummy, i) {
      i.splice(goog.dom.createDom('link'), goog.dom.createDom('img'));
    });

    goog.testing.dom.assertHtmlMatches('<link><img>', testDiv.innerHTML);
  }

  function testSpliceFlattenSingleNode() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = '<div><b>one</b>two<i>three</i></div>';
    it = new goog.dom.TagIterator(testDiv.firstChild);

    goog.iter.forEach(it, function(node, dummy, i) {
      i.splice(node.childNodes);
    });

    goog.testing.dom.assertHtmlMatches('<b>one</b>two<i>three</i>',
        testDiv.innerHTML);
  }

  function testSpliceMiddleNode() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = 'a<b>hello<span>world</span></b>c';
    it = new goog.dom.TagIterator(testDiv);

    goog.iter.forEach(it, function(node, dummy, i) {
      if (node.nodeName == 'B') {
        i.splice(goog.dom.createDom('IMG'));
      }
    });

    goog.testing.dom.assertHtmlMatches('a<img>c', testDiv.innerHTML);
  }

  function testSpliceMiddleNodeReversed() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = 'a<b>hello<span>world</span></b>c';
    it = new goog.dom.TagIterator(testDiv, true);

    goog.iter.forEach(it, function(node, dummy, i) {
      if (node.nodeName == 'B') {
        i.splice(goog.dom.createDom('IMG'));
      }
    });

    goog.testing.dom.assertHtmlMatches('a<img>c', testDiv.innerHTML);
  }

  function testSpliceMiddleNodeAtEndTag() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = 'a<b>hello<span>world</span></b>c';
    it = new goog.dom.TagIterator(testDiv);

    goog.iter.forEach(it, function(node, dummy, i) {
      if (node.tagName == 'B' && i.isEndTag()) {
        i.splice(goog.dom.createDom('IMG'));
      }
    });

    goog.testing.dom.assertHtmlMatches('a<img>c', testDiv.innerHTML);
  }

  function testSpliceMultipleNodes() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = '<STRONG>this</STRONG> is <EM>from IE</EM>';
    it = new goog.dom.TagIterator(testDiv);

    goog.iter.forEach(it, function(node, dummy, i) {
      var replace = null;
      if (node.nodeName == 'STRONG') {
        replace = goog.dom.createDom('B', null, node.childNodes);
      } else if (node.nodeName == 'EM') {
        replace = goog.dom.createDom('I', null, node.childNodes);
      }
      if (replace) {
        i.splice(replace);
      }
    });

    goog.testing.dom.assertHtmlMatches('<b>this</b> is <i>from IE</i>',
        testDiv.innerHTML);
  }

  function testSpliceMultipleNodesAtEnd() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = '<STRONG>this</STRONG> is <EM>from IE</EM>';
    it = new goog.dom.TagIterator(testDiv);

    goog.iter.forEach(it, function(node, dummy, i) {
      var replace = null;
      if (node.nodeName == 'STRONG' && i.isEndTag()) {
        replace = goog.dom.createDom('B', null, node.childNodes);
      } else if (node.nodeName == 'EM' && i.isEndTag()) {
        replace = goog.dom.createDom('I', null, node.childNodes);
      }
      if (replace) {
        i.splice(replace);
      }
    });

    goog.testing.dom.assertHtmlMatches('<b>this</b> is <i>from IE</i>',
        testDiv.innerHTML);
  }

  function testSpliceMultipleNodesReversed() {
    var testDiv = goog.dom.getElement('testSplice');
    testDiv.innerHTML = '<STRONG>this</STRONG> is <EM>from IE</EM>';
    it = new goog.dom.TagIterator(testDiv, true);

    goog.iter.forEach(it, function(node, dummy, i) {
      var replace = null;
      if (node.nodeName == 'STRONG') {
        replace = goog.dom.createDom('B', null, node.childNodes);
      } else if (node.nodeName == 'EM') {
        replace = goog.dom.createDom('I', null, node.childNodes);
      }
      if (replace) {
        i.splice(replace);
      }
    });

    goog.testing.dom.assertHtmlMatches('<b>this</b> is <i>from IE</i>',
        testDiv.innerHTML);
  }

</script>
</body>
</html>
