<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>
      Element and attribute namespaces (HTML)
    </title>
    <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
    <script language="javascript" type="text/javascript">

    var elem;
    var attr;

    var resultText = "";
    var passes = 0;
    var failures = 0;
    var errors = 0;
    var currentTest = '';

    function shouldBe(_a,_b) {
      var _av = eval(_a);
      var _bv = eval(_b);

      if (_av == _bv) {
        resultText += "PASS: " + currentTest + ": " + _a + " is " + _b + "\n";
        passes++;
      }
      else {
        resultText += "FAIL: " + currentTest + ": " + _a + " should be " + _b + ", was " + _av + "\n";
        failures++;
      }
    }

    function output(msg) {
      resultText += msg + "\n";
    }

    // Element/Attribute creation

    function testCreateHTMLElementLower() {
      // Create a normal HTML element in a HTML document
      // We pass in a lowercase name here, but it should be mapped to uppercase
      // since this is a HTML document
      elem = document.createElement("input");
      shouldBe("elem.nodeName","'INPUT'");
      shouldBe("elem.namespaceURI","null");
      shouldBe("elem.prefix","null");
      shouldBe("elem.localName","null");
      shouldBe("elem.tagName","'INPUT'");

      // Verify that this is actually a HTMLInputElement
      shouldBe("(elem.blur != undefined)","true");
    }

    function testCreateHTMLElementUpper() {
      // Create a normal HTML element in a HTML document
      elem = document.createElement("INPUT");
      shouldBe("elem.nodeName","'INPUT'");
      shouldBe("elem.namespaceURI","null");
      shouldBe("elem.prefix","null");
      shouldBe("elem.localName","null");
      shouldBe("elem.tagName","'INPUT'");

      // Verify that this is actually a HTMLInputElement
      shouldBe("(elem.blur != undefined)","true");
    }

    function testCreateXHTMLElement() {
      // Create a HTML element in the XHTML namespace.  This should have the namespaceURI
      // and localName set, and the tag name should remain in lowercase
      elem = document.createElementNS("http://www.w3.org/1999/xhtml","input");
      shouldBe("elem.nodeName","'input'");
      shouldBe("elem.namespaceURI","'http://www.w3.org/1999/xhtml'");
      shouldBe("elem.prefix","null");
      shouldBe("elem.localName","'input'");
      shouldBe("elem.tagName","'input'");

      // Verify that this is actually a HTMLInputElement
      shouldBe("(elem.blur != undefined)","true");
    }

    function testCreateNonHTMLElementInXHTMLNamespace() {
      // Create an element in the XHTML namespace. This is not a HTML element however,
      // since we're passing the name in as (partial) uppercase, and XHTML elements are lowercase only
      elem = document.createElementNS("http://www.w3.org/1999/xhtml","InPuT");
      shouldBe("elem.nodeName","'InPuT'");
      shouldBe("elem.namespaceURI","'http://www.w3.org/1999/xhtml'");
      shouldBe("elem.prefix","null");
      shouldBe("elem.localName","'InPuT'");
      shouldBe("elem.tagName","'InPuT'");

      // Verify that this is _not_ a HTMLInputElement
      shouldBe("elem.blur","undefined");
    }

    function testCreateNonHTMLElementInXHTMLNamespace2() {
      // Create an element in the XHTML namespace but using a non-HTML name
      elem = document.createElementNS("http://www.w3.org/1999/xhtml","other");
      shouldBe("elem.nodeName","'other'");
      shouldBe("elem.namespaceURI","'http://www.w3.org/1999/xhtml'");
      shouldBe("elem.prefix","null");
      shouldBe("elem.localName","'other'");
      shouldBe("elem.tagName","'other'");
    }

    function testCreateXHTMLElementWithPrefix() {
      // Create a HTML element in the XHTML namespace with a prefix.  This should have the namespaceURI,
      // prefix and localName set, and the tag name should remain in lowercase
      elem = document.createElementNS("http://www.w3.org/1999/xhtml","testprefix:input");
      shouldBe("elem.nodeName","'testprefix:input'");
      shouldBe("elem.namespaceURI","'http://www.w3.org/1999/xhtml'");
      shouldBe("elem.prefix","'testprefix'");
      shouldBe("elem.localName","'input'");
      shouldBe("elem.tagName","'testprefix:input'");

      // Verify that this is actually a HTMLInputElement
      shouldBe("(elem.blur != undefined)","true");
    }

    function testCreateNonHTMLElementInXHTMLNamespaceWithPrefix() {
      // Create an element in the XHTML namespace with a prefix. This is not a HTML element however,
      // since we're passing the name in as (partial) uppercase, and XHTML elements are lowercase only
      elem = document.createElementNS("http://www.w3.org/1999/xhtml","testprefix:InPuT");
      shouldBe("elem.nodeName","'testprefix:InPuT'");
      shouldBe("elem.namespaceURI","'http://www.w3.org/1999/xhtml'");
      shouldBe("elem.prefix","'testprefix'");
      shouldBe("elem.localName","'InPuT'");
      shouldBe("elem.tagName","'testprefix:InPuT'");

      // Verify that this is _not_ a HTMLInputElement
      shouldBe("elem.blur","undefined");
    }

    function testCreateNonHTMLElementInXHTMLNamespaceWithPrefix2() {
      // Create an element in the XHTML namespace with a prefix but using a non-HTML name
      elem = document.createElementNS("http://www.w3.org/1999/xhtml","testprefix:other");
      shouldBe("elem.nodeName","'testprefix:other'");
      shouldBe("elem.namespaceURI","'http://www.w3.org/1999/xhtml'");
      shouldBe("elem.prefix","'testprefix'");
      shouldBe("elem.localName","'other'");
      shouldBe("elem.tagName","'testprefix:other'");
    }

    function testCreateNonHTMLElement() {
      // Create an non-html element with no namespace. Since this is a HTML document, the
      // name should be mapped to uppercase, even though it's not actually a HTML element
      elem = document.createElement("TestElement");
      shouldBe("elem.nodeName","'TESTELEMENT'");
      shouldBe("elem.namespaceURI","null");
      shouldBe("elem.prefix","null");
      shouldBe("elem.localName","null");
      shouldBe("elem.tagName","'TESTELEMENT'");
    }

    function testCreateElementNS() {
      // Create an element in a different namespace. Even though this is a HTML document,
      // the case should be preserved in this case since the spec only mentions uppercasing
      // element names for createElement(), not createElementNS().
      // The returned element should have both namespaceURI and localName set.
      elem = document.createElementNS("http://www.test.com","TestElement");
      shouldBe("elem.nodeName","'TestElement'");
      shouldBe("elem.namespaceURI","'http://www.test.com'");
      shouldBe("elem.prefix","null");
      shouldBe("elem.localName","'TestElement'");
      shouldBe("elem.tagName","'TestElement'");
    }

    function testCreateElementNSWithPrefix() {
      // Create an element in a different namespace with a prefix. Even though this is a
      // HTML document, the case should be preserved in this case since the spec only
      // mentions uppercasing element names for createElement(), not createElementNS().
      // The returned element should have namespaceURI, prefix and localName set.
      elem = document.createElementNS("http://www.test.com","TestPrefix:TestElement");
      shouldBe("elem.nodeName","'TestPrefix:TestElement'");
      shouldBe("elem.namespaceURI","'http://www.test.com'");
      shouldBe("elem.prefix","'TestPrefix'");
      shouldBe("elem.localName","'TestElement'");
      shouldBe("elem.tagName","'TestPrefix:TestElement'");
    }

    function testParsingHTML() {
      // A bit different, but still related to element tagName: test how we store
      // the name of elements during parsing. This is an HTML-compat document,
      // so they should be uppercase.
      elem = document.getElementById('output');
      shouldBe("elem.nodeName","'PRE'");
      shouldBe("elem.namespaceURI",null);
      shouldBe("elem.prefix",null);
      shouldBe("elem.localName",null);
      shouldBe("elem.tagName","'PRE'");
    }


    function testCreateAttribute() {
      // Create a normal HTML attribute. The name should be transformed to lowercase.
      // Note that in XML documents we preserve the case.
      attr = document.createAttribute("src");
      shouldBe("attr.nodeName","'src'");
      shouldBe("attr.namespaceURI","null");
      shouldBe("attr.prefix","null");
      shouldBe("attr.localName","null");
      shouldBe("attr.name","'src'");
      shouldBe("attr.value","''");
    }

    function testCreateAttributeUppercase() {
      // Create a normal HTML attribute. The name should be transformed to lowercase.
      attr = document.createAttribute("SRC");
      shouldBe("attr.nodeName","'src'");
      shouldBe("attr.namespaceURI","null");
      shouldBe("attr.prefix","null");
      shouldBe("attr.localName","null");
      shouldBe("attr.name","'src'");
      shouldBe("attr.value","''");
    }

    // Referencing nodes using both namespace aware and namspace unaware methods

    function testSetAttribute() {
      // After using setAttribute() to create an attribute node and assign it to an
      // element, the attribute node can be retrieved using getAttributeNode(), and
      // has all of the expected values

      // The attribute name should be lowercased. 
      // Although section 1.3 of
      // DOM Level 2 HTML says: "element and attribute names are exposed as all uppercase
      // (for consistency) when used on an HTML document", DOM Core is much less affirmative.
      // W3C members have expressed remorse on this statement, and
      // concerns about undermining the ongoing standardization on lowercase attributes
      // amongst major web browsers.

      elem = document.createElement("p");
      elem.setAttribute("class","test");
      attr = elem.getAttributeNode("class");
      shouldBe("attr.nodeName","'class'");
      shouldBe("attr.namespaceURI","null");
      shouldBe("attr.prefix","null");
      shouldBe("attr.localName","null");
      shouldBe("attr.name","'class'");
      shouldBe("attr.value","'test'");
      shouldBe("attr.nodeValue","'test'");
      shouldBe("elem.getAttribute('class')","'test'");
      shouldBe("elem.getAttribute('ClAsS')","'test'");
      shouldBe("elem.getAttribute('CLASS')","'test'");
      shouldBe("elem.getAttributeNode('class')","attr");
      shouldBe("elem.getAttributeNode('ClAsS')","attr");
      shouldBe("elem.getAttributeNode('CLASS')","attr");
      shouldBe("attr.ownerElement","elem");
      shouldBe("attr.specified","true");

      shouldBe("elem.attributes.getNamedItem('class')","attr");
      shouldBe("elem.attributes.getNamedItem('ClAsS')","attr");
      shouldBe("elem.attributes.getNamedItem('CLASS')","attr");

      // getAttributeNodeNS() and getAttributeNS() should fail here, as they match based on the
      // namespaceURI and localName. But since the attribute was created in a non-namespace aware
      // context, it's localName is null.
      shouldBe("elem.getAttributeNodeNS(null,'CLASS')","null");
      shouldBe("elem.getAttributeNS(null,'CLASS')","''");
      shouldBe("elem.attributes.getNamedItemNS(null,'CLASS')","null");

      // If an attribute with that name is already present in the element, its value is
      // changed to be that of the value parameter
      elem.setAttribute("class","newvalue");
      shouldBe("elem.getAttributeNode('class')","attr");
      shouldBe("elem.attributes.getNamedItem('class')","attr");
      shouldBe("elem.getAttribute('class')","'newvalue'");
      shouldBe("attr.value","'newvalue'");
      shouldBe("attr.nodeValue","'newvalue'");
    }

    function testSetAttributeNS() {
      // After using setAttributeNS() to create an attribute node and assign it to an
      // element, the attribute node can be retrieved using getAttributeNodeNS(), and
      // has all of the expected values

      // the behaviour with regard to compatibility mode is the same than createElementNS

      elem = document.createElement("p");
      elem.setAttributeNS("http://www.test.com","myattr","test");
      attr = elem.getAttributeNodeNS("http://www.test.com","myattr");
      shouldBe("attr.nodeName","'myattr'");
      shouldBe("attr.namespaceURI","'http://www.test.com'");
      shouldBe("attr.prefix","null");
      shouldBe("attr.localName","'myattr'");
      shouldBe("attr.name","'myattr'");
      shouldBe("attr.value","'test'");
      shouldBe("attr.nodeValue","'test'");

      shouldBe("elem.getAttribute('myattr')","'test'");
      shouldBe("elem.getAttribute('MyAtTr')","'test'");
      shouldBe("elem.getAttribute('MYATTR')","'test'");
      shouldBe("elem.getAttributeNS('http://www.test.com','myattr')","'test'");
      shouldBe("elem.getAttributeNode('myattr')","attr");
      shouldBe("elem.getAttributeNode('MyAtTr')","attr");
      shouldBe("elem.getAttributeNode('MYATTR')","attr");
      shouldBe("attr.ownerElement","elem");
      shouldBe("attr.specified","true");

      shouldBe("elem.attributes.getNamedItem('myattr')","attr");
      shouldBe("elem.attributes.getNamedItem('MyAtTr')","attr");
      shouldBe("elem.attributes.getNamedItem('MYATTR')","attr");

      shouldBe("elem.getAttributeNodeNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.getAttributeNS('http://www.test.com','myattr')","'test'");
      shouldBe("elem.attributes.getNamedItemNS('http://www.test.com','myattr')","attr");

      // If an attribute with that name is already present in the element, its value is
      // changed to be that of the value parameter
      elem.setAttribute("myattr","newvalue");
      shouldBe("elem.getAttributeNode('myattr')","attr");
      shouldBe("elem.getAttributeNodeNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.attributes.getNamedItem('myattr')","attr");
      shouldBe("elem.attributes.getNamedItemNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.getAttribute('myattr')","'newvalue'");
      shouldBe("elem.getAttributeNS('http://www.test.com','myattr')","'newvalue'");
      shouldBe("attr.value","'newvalue'");
      shouldBe("attr.nodeValue","'newvalue'");

      elem.setAttributeNS("http://www.test.com","myattr","othervalue");
      shouldBe("elem.getAttributeNode('myattr')","attr");
      shouldBe("elem.getAttributeNodeNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.attributes.getNamedItem('myattr')","attr");
      shouldBe("elem.attributes.getNamedItemNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.getAttribute('myattr')","'othervalue'");
      shouldBe("elem.getAttributeNS('http://www.test.com','myattr')","'othervalue'");
      shouldBe("attr.value","'othervalue'");
      shouldBe("attr.nodeValue","'othervalue'");
    }

    function testSetAttributeNSInXHTMLNamespace() {
      // After using setAttributeNS() to create an attribute node and assign it to an
      // element, the attribute node can be retrieved using getAttributeNodeNS(), and
      // has all of the expected values
    
      // the behaviour with regard to compatibility mode is the same than createElementNS

      elem = document.createElement("p");
      elem.setAttributeNS("http://www.w3.org/1999/xhtml", "OtherAttr", "aValue");
      attr = elem.getAttributeNode("OtherAttr");
      shouldBe("elem.getAttributeNodeNS('http://www.w3.org/1999/xhtml','OtherAttr')","attr");

      shouldBe("attr.nodeName","'OtherAttr'");
      shouldBe("attr.namespaceURI","'http://www.w3.org/1999/xhtml'");
      shouldBe("attr.prefix","null");
      shouldBe("attr.localName","'OtherAttr'");
      shouldBe("attr.name","'OtherAttr'");
      shouldBe("attr.value","'aValue'");
      shouldBe("attr.nodeValue","'aValue'");
    }

    function testSetAttributeNSWithPrefix() {
      // After using setAttributeNS() to create an attribute node and assign it to an
      // element, the attribute node can be retrieved using getAttributeNodeNS(), and
      // has all of the expected values

      // the behaviour with regard to compatibility mode is the same than createElementNS

      elem = document.createElement("p");
      elem.setAttributeNS("http://www.test.com","myprefix:myattr","test");
      attr = elem.getAttributeNodeNS("http://www.test.com","myattr");
      shouldBe("attr.nodeName","'myprefix:myattr'");
      shouldBe("attr.namespaceURI","'http://www.test.com'");
      shouldBe("attr.prefix","'myprefix'");
      shouldBe("attr.localName","'myattr'");
      shouldBe("attr.name","'myprefix:myattr'");
      shouldBe("attr.value","'test'");
      shouldBe("attr.nodeValue","'test'");



      shouldBe("elem.getAttribute('myprefix:myattr')","'test'");
      shouldBe("elem.getAttribute('myprefix:MyAtTr')","'test'");
      shouldBe("elem.getAttribute('myprefix:MYATTR')","'test'");
      shouldBe("elem.getAttribute('MyPrefix:myattr')","'test'");
      shouldBe("elem.getAttribute('MYPREFIX:myattr')","'test'");
      shouldBe("elem.getAttribute('myattr')","''");
      shouldBe("elem.getAttributeNS('http://www.test.com','myattr')","'test'");
      shouldBe("elem.getAttributeNS('http://www.test.com','myprefix:myattr')","''");
      shouldBe("elem.getAttributeNode('myprefix:myattr')","attr");
      shouldBe("elem.getAttributeNode('myprefix:MyAtTr')","attr");
      shouldBe("elem.getAttributeNode('myprefix:MYATTR')","attr");
      shouldBe("elem.getAttributeNode('MyPrefix:myattr')","attr");
      shouldBe("elem.getAttributeNode('MYPREFIX:myattr')","attr");
      shouldBe("elem.getAttributeNode('myattr')","null");
      shouldBe("attr.ownerElement","elem");
      shouldBe("attr.specified","true");

      shouldBe("elem.attributes.getNamedItem('myattr')","null");
      shouldBe("elem.attributes.getNamedItem('myprefix:myattr')","attr");
      shouldBe("elem.attributes.getNamedItem('myprefix:MyAtTr')","attr");
      shouldBe("elem.attributes.getNamedItem('myprefix:MYATTR')","attr");
      shouldBe("elem.attributes.getNamedItem('MyPrefix:myattr')","attr");
      shouldBe("elem.attributes.getNamedItem('MYPREFIX:myattr')","attr");

      shouldBe("elem.getAttributeNodeNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.getAttributeNS('http://www.test.com','myattr')","'test'");
      shouldBe("elem.attributes.getNamedItemNS('http://www.test.com','myattr')","attr");

      // If an attribute with that name is already present in the element, its value is
      // changed to be that of the value parameter
      elem.setAttribute("myprefix:myattr","newvalue");
      shouldBe("elem.getAttributeNode('myprefix:myattr')","attr");
      shouldBe("elem.getAttributeNodeNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.attributes.getNamedItem('myprefix:myattr')","attr");
      shouldBe("elem.attributes.getNamedItemNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.getAttribute('myprefix:myattr')","'newvalue'");
      shouldBe("elem.getAttributeNS('http://www.test.com','myattr')","'newvalue'");
      shouldBe("attr.value","'newvalue'");
      shouldBe("attr.nodeValue","'newvalue'");

      elem.setAttributeNS("http://www.test.com","myattr","othervalue");
      shouldBe("elem.getAttributeNode('myprefix:myattr')","attr");
      shouldBe("elem.getAttributeNodeNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.attributes.getNamedItem('myprefix:myattr')","attr");
      shouldBe("elem.attributes.getNamedItemNS('http://www.test.com','myattr')","attr");
      shouldBe("elem.getAttribute('myprefix:myattr')","'othervalue'");
      shouldBe("elem.getAttributeNS('http://www.test.com','myattr')","'othervalue'");
      shouldBe("attr.value","'othervalue'");
      shouldBe("attr.nodeValue","'othervalue'");
    }

    function runTest(testName,func) {
      currentTest = testName;
      try {
        func();
      }
      catch (e) {
        output("ERROR: " + testName + ": " + e);
        errors++;
      }
      currentTest = '';
    }

    function runTests() {

      // Element/Attribute creation
      runTest("createElement() with a lowercase html element name",
              testCreateHTMLElementLower);
      runTest("createElement() with a upper html element name",
              testCreateHTMLElementUpper);
      runTest("createElementNS() in the xhtml namespace",
              testCreateXHTMLElement);
      runTest("createElementNS() with a html element name but different case in the xhtml namespace",
              testCreateNonHTMLElementInXHTMLNamespace);
      runTest("createElementNS() with a non-HTML element in the xhtml namespace",
              testCreateNonHTMLElementInXHTMLNamespace2);
      runTest("createElementNS() in the xhtml namespace with a prefix",
              testCreateXHTMLElementWithPrefix);
      runTest("createElementNS() with a html element name & prefix but different case in the xhtml namespace",
              testCreateNonHTMLElementInXHTMLNamespaceWithPrefix);
      runTest("createElementNS() with a non-HTML element & prefix in the xhtml namespace",
              testCreateNonHTMLElementInXHTMLNamespaceWithPrefix2);
      runTest("createElement() with a non-html element name",
              testCreateNonHTMLElement);
      runTest("createElementNS()",
              testCreateElementNS);
      runTest("createElementNS() with prefix",
              testCreateElementNSWithPrefix);
      runTest("parsing html compat elements",
              testParsingHTML);

      runTest("createAttribute()",testCreateAttribute);
      runTest("createAttribute() with an uppercase name",testCreateAttributeUppercase);

      // Referencing nodes using both namespace aware and namespace unaware methods
      runTest("setAttribute()",testSetAttribute);
      runTest("setAttributeNS()",testSetAttributeNS);
      runTest("setAttributeNS() in XHTML namespace", testSetAttributeNSInXHTMLNamespace);
      runTest("setAttributeNS() with prefix",testSetAttributeNSWithPrefix);

      showResults();
    }

    function showResults() {
      var str = resultText +
                "Passes: " + passes +
                "\nFailures: " + failures +
                "\nErrors: " + errors;
      document.getElementById("output").appendChild(document.createTextNode(str));
    }
    </script>
  </head>
  <body>
    <h1>
      Element and attribute namespaces (HTML)
    </h1>
    <p>
      Results should be displayed below.
    </p>
    <pre id="output"></pre>
    <script language="javascript" type="text/javascript">
    runTests();
    </script>
  </body>
</html>