var DOMUtilities = function() {
    // exclude: don't look at this node or any of its children
    this.excludeElementIdMap = new Object();
    this.excludeElementNodeNameMap = new Object();
    // pass: ignore this node but decend through its children
    this.passElementIdMap = new Object();
    this.passElementNodeNameMap = new Object();

    // defaults
    this.excludeElementNodeNameMap["SELECT"]=true;
    this.passElementNodeNameMap["BODY"]=true;
    this.passElementNodeNameMap["HTML"]=true;
}

DOMUtilities.prototype.getOffsetCoordinates=function(el,parentNodeId){
    var c = new Object();
    c.top = 0;
    c.left = 0;
    c.height = 0;
    c.bottom = 0;

    if(!el || el==null || el.nodeType!=1) { return c };
    c.top = el.offsetTop;
    c.left = el.offsetLeft;
    c.height = el.offsetHeight;
    c.bottom = c.top + c.height;

    if (!(el = el.offsetParent)) { return c };
    do {
        c.top += el.offsetTop;
        c.left += el.offsetLeft;
        c.bottom += el.offsetTop;
    } while ((el = el.offsetParent) == true && (parentNodeId ? el.id != parentNodeId : true) && el.nodeType==1);

    return c;
}

DOMUtilities.prototype.getMaxBottom = function(currentNode, currentMaxBottom, topElementId, isRelativeToTop, localPassElementIdMap, localExcludeElementIdMap){
    if (!currentNode || currentNode == null || (typeof(currentNode)!="object")) { return 0 };
    var maxBottom = currentMaxBottom;
    if (!maxBottom || maxBottom < 0) { maxBottom = 0 }
    if (typeof(isRelativeToTop) == "undefined") { isRelativeToTop = true }

    // stopping conditions
    if (!currentNode || currentNode == null || currentNode.nodeType != 1) { return maxBottom }
    if (this.excludeElementIdMap[currentNode.id]
        || this.excludeElementNodeNameMap[currentNode.nodeName]
        || (localExcludeElementIdMap && localExcludeElementIdMap[currentNode.id])
        ) { return maxBottom }

    var thisBottom = 0;
    if (currentNode.offsetHeight
        && !this.passElementIdMap[currentNode.id]
        && !this.passElementNodeNameMap[currentNode.nodeName]
        && !(localPassElementIdMap && localPassElementIdMap[currentNode.id])
        ){
        if (isRelativeToTop && (!currentNode.childNodes || currentNode.childNodes.length > 0)) {
            thisBottom = currentNode.offsetHeight + this.getParentTop(currentNode,topElementId);
        } else {
            // THE POINT: we are interested in the height of the nodes inside our reference element
            // thus we exclude this height from the calculation and ensure isRelativeToTop is true for the children
            thisBottom = 0;
        }
	maxBottom = (thisBottom > maxBottom) ? thisBottom : maxBottom;
    }

    if (!currentNode.childNodes || currentNode.childNodes.length==0) { return maxBottom }
    for (var i=0;i<currentNode.childNodes.length;i++){
        // During the recursion, isRelativeToTop must be true
        maxBottom = this.getMaxBottom(
                                      currentNode.childNodes[i],
                                      maxBottom,
                                      topElementId,
                                      true,
                                      localPassElementIdMap,
                                      localExcludeElementIdMap
                                     );
    }
    return maxBottom;
}

DOMUtilities.prototype.getParentTop=function(thisNode,topLevelId){
    var oTop = 0;
    if (thisNode.offsetParent){
        do {
            oTop += thisNode.offsetTop;
            thisNode = thisNode.offsetParent;
        } while (thisNode && (topLevelId ? thisNode.id != topLevelId : true));
    }
    return oTop;
}

/*
 * Override for getMaxBottom for WebCollage Syndication on IE6 and under only
 * Maintains the same interface, but we cannot leverage the recursion to determine rendered height
 * of arbitrary elements.
 * If we are presented with the case where the supplied currentNode's id does not match the supplied topElementId
 * we report the bottom of the currentNode and do not attempt to walk the DOM tree
 * Otherwise, we do a linear scan over the DOM tree.
 */
/*@cc_on @*/
/*@if (@_jscript_version <= 5.6)
    if((typeof(isWebCollageSyndication) != "undefined") && isWebCollageSyndication){
        DOMUtilities.prototype.getMaxBottom = function(currentNode, currentMaxBottom, topElementId, isRelativeToTop, localPassElementIdMap, localExcludeElementIdMap){
            var maxBottom = 0;
            if (currentNode && currentNode.id && topElementId && (currentNode.id != topElementId)) {
                return(currentNode.offsetTop + currentNode.offsetHeight);
            }
            if (document.getElementsByTagName && !document.all) {
                tagAry = docment.getElementsByTagName('div');
                for(i=0;i<tagAry.length;i++){
                    if ((tagAry[i].offsetTop + tagAry[i].offsetHeight) > maxBottom){maxBottom = (tagAry[i].offsetTop + tagAry[i].offsetHeight);}
                }
            } else if(document.all.tags) {
                len = document.all.tags('div').length;
                for(i=0;i<len;i++){
                    ieHeight = parseInt(document.all.tags('div')[i].offsetHeight);
                    ieTop = parseInt(document.all.tags('div')[i].offsetTop);
                    if ((parseInt(document.all.tags('div')[i].offsetHeight) + parseInt(document.all.tags('div')[i].offsetTop)) > maxBottom){maxBottom=(parseInt(document.all.tags('div')[i].offsetHeight) + parseInt(document.all.tags('div')[i].offsetTop));}
                }
            }
            return maxBottom;
        }
    }
/*@end @*/
