// This implements
//    Element Traversal Specification
//    W3C Recommendation 22 December 2008
//    http://www.w3.org/TR/2008/REC-ElementTraversal-20081222/
//
// using general methods instead of attributes.
//
//   Element        firstElementChild(element);
//   Element        lastElementChild(element);
//   Element        previousElementSibling(element);
//   Element        nextElementSibling(element);
//   Number         childElementCount(element);


var firstElementChild      = null;
var lastElementChild       = null;
var previousElementSibling = null;
var nextElementSibling     = null;
var childElementCount      = null;

(function () {
    // create a dummy element to test the support of Element Traversal
    var d=document.createElement("H1");

    // Returns the first child element node of this element. null if
    // this element has no child elements.
    firstElementChild=function() {
	if (typeof d.firstElementChild=="undefined") {
	    return function(e) {
		var c=e.childNodes;
		for (var i=0; i < c.length; i++) {
		    var n=c.item(i);
		    if (n.nodeType==1) {
			return n;
		    }
		}
		return null;
	    };
	} else {
	    return function(e) {
		return e.firstElementChild;
	    };
	}
    }();
    
    // Returns the last child element node of this element. null if
    // this element has no child elements.
    lastElementChild=function() {
	if (typeof d.lastElementChild=="undefined") {
	    return function(e) {
		var c=e.childNodes;
		for (var i=c.length-1 ; i >= 0; i--) {
		    var n=c.item(i);
		    if (n.nodeType==1) {
			return n;
		    }
		}
		return null;
	    };
	} else {
	    return function(e) {
		return e.lastElementChild;
	    };
	}
    }();
    
    // Returns the previous sibling element node of this element. null
    // if this element has no element sibling nodes that come before
    // this one in the document tree.
    previousElementSibling=function() {
	if (typeof d.previousElementSibling=="undefined") {
	    return function(e) {
		if (e.parentNode===null) {
		    return null;
		}
		var c=e.parentNode.childNodes;
		var p=null;
		for (var i=0; i < c.length; i++) {
		    // this outperforms previousSibling in general
		    var n=c.item(i);
		    if (n==e) {
			return p;
		    }
		    if (n.nodeType==1) {
			p=n;
		    }
		}
		return null;
	    };
	} else {
	    return function(e) {
		return e.previousElementSibling;
	    };
	}
    }();

    // Returns the next sibling element node of this element. null if
    // this element has no element sibling nodes that come after this
    // one in the document tree.
    nextElementSibling=function() {
	if (typeof d.nextElementSibling=="undefined") {
	    return function(e) {
		if (e.parentNode===null) {
		    return null;
		}
		var c=e.parentNode.childNodes;
		var s=null;
		for (var i=c.length-1 ; i >= 0; i--) {
		    // this outperforms nextSibling in general
		    var n=c.item(i);
		    if (n==e) {
			return s;
		    }
		    if (n.nodeType==1) {
			s=n;
		    }
		}
		return null;
	    };
	} else {
	    return function(e) {
		return e.nextElementSibling;
	    };
	}
    }();

    // Returns the current number of element nodes that are children
    // of this element. 0 if this element has no child nodes that are
    // of nodeType 1.
    childElementCount=function() {
	if (typeof d.childElementCount=="undefined") {
	    return function(e) {
		var c=e.childNodes;
		var r=0;
		for (var i=0; i < c.length; i++) {
		    if (c.item(i).nodeType==1) {
			r++;
		    }
		}
		return r;
	    };
	} else {
	    return function(e) {
		return e.childElementCount;
	    };
	}
    }();
})();

