<!doctype html>
<title>Node assorted property tests</title>
<link rel=author title="Aryeh Gregor" href=ayg@aryeh.name>
<meta charset=utf-8>
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=../common.js></script>
<script>
"use strict";
/**
 * First we define a data structure to tell us what tests to run.  The keys
 * will be eval()ed, and are mostly global variables defined in common.js.  The
 * values are objects, which maps properties to expected values.  So
 *
 *     foo: {
 *         bar: "baz",
 *         quz: 7,
 *     },
 *
 * will test that eval("foo.bar") === "baz" and eval("foo.quz") === 7.  "foo"
 * and "bar" could thus be expressions, like "document.documentElement" and
 * "childNodes[4]" respectively.
 *
 * To avoid repetition, some values are automatically added based on others.
 * For instance, if we specify nodeType: Node.TEXT_NODE, we'll automatically
 * also test nodeName: "#text".  This is handled by code after this variable is
 * defined.
 */
var expected = {
    testDiv: {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: document.body,
        parentElement: document.body,
        "childNodes.length": 7,
        "childNodes[0]": paras[0],
        "childNodes[1]": paras[1],
        "childNodes[2]": paras[2],
        "childNodes[3]": paras[3],
        "childNodes[4]": paras[4],
        "childNodes[5]": paras[5],
        "childNodes[6]": comment,
        previousSibling: null,
        nextSibling: document.getElementById("log"),
        textContent: "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\nIjklmnop\nQrstuvwxYzabcdefGhijklmn123456789012",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "div",
        tagName: "DIV",
        id: "test",
        "children[0]": paras[0],
        "children[1]": paras[1],
        "children[2]": paras[2],
        "children[3]": paras[3],
        "children[4]": paras[4],
        "children[5]": paras[5],
        previousElementSibling: null,
        // nextSibling isn't explicitly set
        //nextElementSibling: ,
        childElementCount: 6,
    },
    detachedDiv: {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: null,
        parentElement: null,
        "childNodes.length": 2,
        "childNodes[0]": detachedPara1,
        "childNodes[1]": detachedPara2,
        previousSibling: null,
        nextSibling: null,
        textContent: "OpqrstuvWxyzabcd",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "div",
        tagName: "DIV",
        "children[0]": detachedPara1,
        "children[1]": detachedPara2,
        previousElementSibling: null,
        nextElementSibling: null,
        childElementCount: 2,
    },
    detachedPara1: {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: detachedDiv,
        parentElement: detachedDiv,
        "childNodes.length": 1,
        previousSibling: null,
        nextSibling: detachedPara2,
        textContent: "Opqrstuv",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        previousElementSibling: null,
        nextElementSibling: detachedPara2,
        childElementCount: 0,
    },
    detachedPara2: {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: detachedDiv,
        parentElement: detachedDiv,
        "childNodes.length": 1,
        previousSibling: detachedPara1,
        nextSibling: null,
        textContent: "Wxyzabcd",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        previousElementSibling: detachedPara1,
        nextElementSibling: null,
        childElementCount: 0,
    },
    document: {
        // Node
        nodeType: Node.DOCUMENT_NODE,
        "childNodes.length": 2,
        "childNodes[0]": document.doctype,
        "childNodes[1]": document.documentElement,

        // Document
        URL: String(location),
        compatMode: "CSS1Compat",
        characterSet: "UTF-8",
        contentType: "text/html",
        doctype: doctype,
        //documentElement: ,
    },
    foreignDoc: {
        // Node
        nodeType: Node.DOCUMENT_NODE,
        "childNodes.length": 3,
        "childNodes[0]": foreignDoc.doctype,
        "childNodes[1]": foreignDoc.documentElement,
        "childNodes[2]": foreignComment,

        // Document
        URL: "about:blank",
        compatMode: "CSS1Compat",
        characterSet: "UTF-8",
        contentType: "text/html",
        //doctype: ,
        //documentElement: ,
    },
    foreignPara1: {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: foreignDoc,
        parentNode: foreignDoc.body,
        parentElement: foreignDoc.body,
        "childNodes.length": 1,
        previousSibling: null,
        nextSibling: foreignPara2,
        textContent: "Efghijkl",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        previousElementSibling: null,
        nextElementSibling: foreignPara2,
        childElementCount: 0,
    },
    foreignPara2: {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: foreignDoc,
        parentNode: foreignDoc.body,
        parentElement: foreignDoc.body,
        "childNodes.length": 1,
        previousSibling: foreignPara1,
        nextSibling: foreignTextNode,
        textContent: "Mnopqrst",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        previousElementSibling: foreignPara1,
        nextElementSibling: null,
        childElementCount: 0,
    },
    xmlDoc: {
        // Node
        nodeType: Node.DOCUMENT_NODE,
        "childNodes.length": 4,
        "childNodes[0]": xmlDoctype,
        "childNodes[1]": xmlElement,
        "childNodes[2]": processingInstruction,
        "childNodes[3]": xmlComment,

        // Document
        URL: "about:blank",
        compatMode: "CSS1Compat",
        characterSet: "UTF-8",
        contentType: "application/xml",
        //doctype: ,
        //documentElement: ,
    },
    xmlElement: {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: xmlDoc,
        parentNode: xmlDoc,
        parentElement: null,
        "childNodes.length": 1,
        "childNodes[0]": xmlTextNode,
        previousSibling: xmlDoctype,
        nextSibling: processingInstruction,
        textContent: "do re mi fa so la ti",

        // Element
        namespaceURI: null,
        prefix: null,
        localName: "igiveuponcreativenames",
        tagName: "igiveuponcreativenames",
        previousElementSibling: null,
        nextElementSibling: null,
        childElementCount: 0,
    },
    detachedXmlElement: {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: xmlDoc,
        parentNode: null,
        parentElement: null,
        "childNodes.length": 0,
        previousSibling: null,
        nextSibling: null,
        textContent: "",

        // Element
        namespaceURI: null,
        prefix: null,
        localName: "everyone-hates-hyphenated-element-names",
        tagName: "everyone-hates-hyphenated-element-names",
        previousElementSibling: null,
        nextElementSibling: null,
        childElementCount: 0,
    },
    detachedTextNode: {
        // Node
        nodeType: Node.TEXT_NODE,
        ownerDocument: document,
        parentNode: null,
        parentElement: null,
        previousSibling: null,
        nextSibling: null,
        nodeValue: "Uvwxyzab",

        // Text
        wholeText: "Uvwxyzab",
    },
    foreignTextNode: {
        // Node
        nodeType: Node.TEXT_NODE,
        ownerDocument: foreignDoc,
        parentNode: foreignDoc.body,
        parentElement: foreignDoc.body,
        previousSibling: foreignPara2,
        nextSibling: null,
        nodeValue: "I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now.",

        // Text
        wholeText: "I admit that I harbor doubts about whether we really need so many things to test, but it's too late to stop now.",
    },
    detachedForeignTextNode: {
        // Node
        nodeType: Node.TEXT_NODE,
        ownerDocument: foreignDoc,
        parentNode: null,
        parentElement: null,
        previousSibling: null,
        nextSibling: null,
        nodeValue: "Cdefghij",

        // Text
        wholeText: "Cdefghij",
    },
    xmlTextNode: {
        // Node
        nodeType: Node.TEXT_NODE,
        ownerDocument: xmlDoc,
        parentNode: xmlElement,
        parentElement: xmlElement,
        previousSibling: null,
        nextSibling: null,
        nodeValue: "do re mi fa so la ti",

        // Text
        wholeText: "do re mi fa so la ti",
    },
    detachedXmlTextNode: {
        // Node
        nodeType: Node.TEXT_NODE,
        ownerDocument: xmlDoc,
        parentNode: null,
        parentElement: null,
        previousSibling: null,
        nextSibling: null,
        nodeValue: "Klmnopqr",

        // Text
        wholeText: "Klmnopqr",
    },
    processingInstruction: {
        // Node
        nodeType: Node.PROCESSING_INSTRUCTION_NODE,
        ownerDocument: xmlDoc,
        parentNode: xmlDoc,
        parentElement: null,
        previousSibling: xmlElement,
        nextSibling: xmlComment,
        nodeValue: 'Did you know that ":syn sync fromstart" is very useful when using vim to edit large amounts of JavaScript embedded in HTML?',

        // ProcessingInstruction
        target: "somePI",
    },
    detachedProcessingInstruction: {
        // Node
        nodeType: Node.PROCESSING_INSTRUCTION_NODE,
        ownerDocument: xmlDoc,
        parentNode: null,
        parentElement: null,
        previousSibling: null,
        nextSibling: null,
        nodeValue: "chirp chirp chirp",

        // ProcessingInstruction
        target: "whippoorwill",
    },
    comment: {
        // Node
        nodeType: Node.COMMENT_NODE,
        ownerDocument: document,
        parentNode: testDiv,
        parentElement: testDiv,
        previousSibling: paras[5],
        nextSibling: null,
        nodeValue: "Alphabet soup?",
    },
    detachedComment: {
        // Node
        nodeType: Node.COMMENT_NODE,
        ownerDocument: document,
        parentNode: null,
        parentElement: null,
        previousSibling: null,
        nextSibling: null,
        nodeValue: "Stuvwxyz",
    },
    foreignComment: {
        // Node
        nodeType: Node.COMMENT_NODE,
        ownerDocument: foreignDoc,
        parentNode: foreignDoc,
        parentElement: null,
        previousSibling: foreignDoc.documentElement,
        nextSibling: null,
        nodeValue: '"Commenter" and "commentator" mean different things.  I\'ve seen non-native speakers trip up on this.',
    },
    detachedForeignComment: {
        // Node
        nodeType: Node.COMMENT_NODE,
        ownerDocument: foreignDoc,
        parentNode: null,
        parentElement: null,
        previousSibling: null,
        nextSibling: null,
        nodeValue: "אריה יהודה",
    },
    xmlComment: {
        // Node
        nodeType: Node.COMMENT_NODE,
        ownerDocument: xmlDoc,
        parentNode: xmlDoc,
        parentElement: null,
        previousSibling: processingInstruction,
        nextSibling: null,
        nodeValue: "I maliciously created a comment that will break incautious XML serializers, but Firefox threw an exception, so all I got was this lousy T-shirt",
    },
    detachedXmlComment: {
        // Node
        nodeType: Node.COMMENT_NODE,
        ownerDocument: xmlDoc,
        parentNode: null,
        parentElement: null,
        previousSibling: null,
        nextSibling: null,
        nodeValue: "בן חיים אליעזר",
    },
    docfrag: {
        // Node
        nodeType: Node.DOCUMENT_FRAGMENT_NODE,
        ownerDocument: document,
        "childNodes.length": 0,
        textContent: "",
    },
    foreignDocfrag: {
        // Node
        nodeType: Node.DOCUMENT_FRAGMENT_NODE,
        ownerDocument: foreignDoc,
        "childNodes.length": 0,
        textContent: "",
    },
    xmlDocfrag: {
        // Node
        nodeType: Node.DOCUMENT_FRAGMENT_NODE,
        ownerDocument: xmlDoc,
        "childNodes.length": 0,
        textContent: "",
    },
    doctype: {
        // Node
        nodeType: Node.DOCUMENT_TYPE_NODE,
        ownerDocument: document,
        parentNode: document,
        previousSibling: null,
        nextSibling: document.documentElement,

        // DocumentType
        name: "html",
        publicId: "",
        systemId: "",
    },
    foreignDoctype: {
        // Node
        nodeType: Node.DOCUMENT_TYPE_NODE,
        ownerDocument: foreignDoc,
        parentNode: foreignDoc,
        previousSibling: null,
        nextSibling: foreignDoc.documentElement,

        // DocumentType
        name: "html",
        publicId: "",
        systemId: "",
    },
    xmlDoctype: {
        // Node
        nodeType: Node.DOCUMENT_TYPE_NODE,
        ownerDocument: xmlDoc,
        parentNode: xmlDoc,
        previousSibling: null,
        nextSibling: xmlElement,

        // DocumentType
        name: "qorflesnorf",
        publicId: "abcde",
        systemId: "x\"'y",
    },
    "paras[0]": {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: testDiv,
        parentElement: testDiv,
        "childNodes.length": 1,
        previousSibling: null,
        nextSibling: paras[1],
        textContent: "A\u0308b\u0308c\u0308d\u0308e\u0308f\u0308g\u0308h\u0308\n",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        id: "a",
        previousElementSibling: null,
        nextElementSibling: paras[1],
        childElementCount: 0,
    },
    "paras[1]": {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: testDiv,
        parentElement: testDiv,
        "childNodes.length": 1,
        previousSibling: paras[0],
        nextSibling: paras[2],
        textContent: "Ijklmnop\n",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        id: "b",
        previousElementSibling: paras[0],
        nextElementSibling: paras[2],
        childElementCount: 0,
    },
    "paras[2]": {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: testDiv,
        parentElement: testDiv,
        "childNodes.length": 1,
        previousSibling: paras[1],
        nextSibling: paras[3],
        textContent: "Qrstuvwx",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        id: "c",
        previousElementSibling: paras[1],
        nextElementSibling: paras[3],
        childElementCount: 0,
    },
    "paras[3]": {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: testDiv,
        parentElement: testDiv,
        "childNodes.length": 1,
        previousSibling: paras[2],
        nextSibling: paras[4],
        textContent: "Yzabcdef",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        id: "d",
        previousElementSibling: paras[2],
        nextElementSibling: paras[4],
        childElementCount: 0,
    },
    "paras[4]": {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: testDiv,
        parentElement: testDiv,
        "childNodes.length": 1,
        previousSibling: paras[3],
        nextSibling: paras[5],
        textContent: "Ghijklmn",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        id: "e",
        previousElementSibling: paras[3],
        nextElementSibling: paras[5],
        childElementCount: 0,
    },
    "paras[5]": {
        // Node
        nodeType: Node.ELEMENT_NODE,
        ownerDocument: document,
        parentNode: testDiv,
        parentElement: testDiv,
        "childNodes.length": 3,
        previousSibling: paras[4],
        nextSibling: comment,
        textContent: "123456789012",

        // Element
        namespaceURI: "http://www.w3.org/1999/xhtml",
        prefix: null,
        localName: "p",
        tagName: "P",
        previousElementSibling: paras[4],
        nextElementSibling: null,
        childElementCount: 0,
    }
};

for (var node in expected) {
    // Now we set various default values by node type.
    switch (expected[node].nodeType) {
    case Node.ELEMENT_NODE:
        expected[node].nodeName = expected[node].tagName;
        expected[node].nodeValue = null;
        expected[node]["children.length"] = expected[node].childElementCount;

        if (expected[node].id === undefined) {
            expected[node].id = "";
        }
        if (expected[node].className === undefined) {
            expected[node].className = "";
        }

        var len = expected[node].childElementCount;
        if (len === 0) {
            expected[node].firstElementChild =
            expected[node].lastElementChild = null;
        } else {
            // If we have expectations for the first/last child in children,
            // use those.  Otherwise, at least check that .firstElementChild ==
            // .children[0] and .lastElementChild == .children[len - 1], even
            // if we aren't sure what they should be.
            expected[node].firstElementChild = expected[node]["children[0]"]
                ? expected[node]["children[0]"]
                : eval(node).children[0];
            expected[node].lastElementChild =
                expected[node]["children[" + (len - 1) + "]"]
                ? expected[node]["children[" + (len - 1) + "]"]
                : eval(node).children[len - 1];
        }
        break;

    case Node.TEXT_NODE:
        expected[node].nodeName = "#text";
        expected[node]["childNodes.length"] = 0;
        expected[node].textContent = expected[node].data =
            expected[node].nodeValue;
        expected[node].length = expected[node].nodeValue.length;
        break;

    case Node.PROCESSING_INSTRUCTION_NODE:
        expected[node].nodeName = expected[node].target;
        expected[node]["childNodes.length"] = 0;
        expected[node].textContent = expected[node].data =
            expected[node].nodeValue;
        expected[node].length = expected[node].nodeValue.length;
        break;

    case Node.COMMENT_NODE:
        expected[node].nodeName = "#comment";
        expected[node]["childNodes.length"] = 0;
        expected[node].textContent = expected[node].data =
            expected[node].nodeValue;
        expected[node].length = expected[node].nodeValue.length;
        break;

    case Node.DOCUMENT_NODE:
        expected[node].nodeName = "#document";
        expected[node].ownerDocument = expected[node].parentNode =
            expected[node].parentElement = expected[node].previousSibling =
            expected[node].nextSibling = expected[node].nodeValue =
            expected[node].textContent = null;
        expected[node].documentURI = expected[node].URL;
        expected[node].charset = expected[node].inputEncoding =
            expected[node].characterSet;
        break;

    case Node.DOCUMENT_TYPE_NODE:
        expected[node].nodeName = expected[node].name;
        expected[node]["childNodes.length"] = 0;
        expected[node].parentElement = expected[node].nodeValue =
            expected[node].textContent = null;
        break;

    case Node.DOCUMENT_FRAGMENT_NODE:
        expected[node].nodeName = "#document-fragment";
        expected[node].parentNode = expected[node].parentElement =
            expected[node].previousSibling = expected[node].nextSibling =
            expected[node].nodeValue = null;
        break;
    }

    // Now we set some further default values that are independent of node
    // type.
    var len = expected[node]["childNodes.length"];
    if (len === 0) {
        expected[node].firstChild = expected[node].lastChild = null;
    } else {
        // If we have expectations for the first/last child in childNodes, use
        // those.  Otherwise, at least check that .firstChild == .childNodes[0]
        // and .lastChild == .childNodes[len - 1], even if we aren't sure what
        // they should be.
        expected[node].firstChild = expected[node]["childNodes[0]"]
            ? expected[node]["childNodes[0]"]
            : eval(node).childNodes[0];
        expected[node].lastChild =
            expected[node]["childNodes[" + (len - 1) + "]"]
            ? expected[node]["childNodes[" + (len - 1) + "]"]
            : eval(node).childNodes[len - 1];
    }
    expected[node]["hasChildNodes()"] = !!expected[node]["childNodes.length"];

    // Finally, we test!
    for (var prop in expected[node]) {
        test(function() {
            assert_equals(eval(node + "." + prop), expected[node][prop]);
        }, node + "." + prop);
    }
}

testDiv.parentNode.removeChild(testDiv);
</script>