// generic functions to obtain cooridinate offsets of HTML elements relative to certain nodes of interest
function elc_getOffsetCoordinates( el, parentId ) {
var c = new Object();
c.top = c.left = c.height = c.bottom = 0;
if (el) {
    c.height = el.offsetHeight;
    c.top = el.offsetTop;
    c.left = el.offsetLeft;
    c.bottom = c.top + c.height;
    if (!el.offsetParent) { return c; }
    el = el.offsetParent;
    do {
        c.top += el.offsetTop;
        c.left += el.offsetLeft;
        c.bottom += el.offsetTop;
    } while ((el = el.offsetParent) == true && (parentId ? el.id != parentId : true))
}
return c;
}

// starting from a supplied node, find the node whose offsetHeight + parentTop back
// to the starting node is a maximum. If the excludeElementId in the MAP matches a node, that
// node and its children are not included in the search
// options:
// thisNode and topElementId both null: use the top-most node of the document

// isRelativeToTop:
// true: calculates bottom thisNode and all of its included dependents relative to the top of the 
//       element specified by topElementId or the top-most element of the document.
// false: calculates bottom of thisNode's dependents exluding thisNode (the effective rendered offsetHeight)
//
// does not return the bottom relative to the top of thisNode, however that can be obtained via
// c = elc_getOffsetCoordinates( thisNode, thisNode );
// c.top + elc_getMaxBottom(thisNode, 0, thisNode.id, excludeElementIdMap, false );


// We should always exclude some nodes based on their nodeName
// assumes return value from node.nodeName is uppercase
// excluded nodes: (SELECT)
// don't include the node or its children
var brandExcludeElementNodeNameMap = new Object;
brandExcludeElementNodeNameMap.SELECT = true;

// passable nodes: (BODY, HTML)
// include the node in the search but don't take its height
var brandPassElementNodeNameMap = new Object;
brandPassElementNodeNameMap.BODY = true;
brandPassElementNodeNameMap.HTML = true;

function elc_getMaxBottom(thisNode, thisMaxBottom, topElementId, excludeElementIdMap, isRelativeToTop) {
    var maxBottom = thisMaxBottom;
    if (!maxBottom || maxBottom < 0) { maxBottom = 0; }

    if (!thisNode && topElementId) {
        thisNode = document.getElementById( topElementId );
        if (!thisNode) { topElementId = ""; } //likely a user error, undefine the topElementId and continue
    }
    if (!thisNode) { thisNode = document.documentElement; } // top-most node (generally will not have an id)
    if (typeof(isRelativeToTop) == "undefined") { isRelativeToTop = true; }

    // stopping conditions
    if (!thisNode) { return maxBottom; }
    var thisBottom = 0;
    if (thisNode.offsetHeight && !(brandPassElementNodeNameMap && brandPassElementNodeNameMap[thisNode.nodeName])
//        && (excludeElementIdMap ? !excludeElementIdMap[thisNode.id] : true)
//        && (brandExcludeElementNodeNameMap ? !brandExcludeElementNodeNameMap[thisNode.nodeName] : true )
        ) {
        thisBottom = (isRelativeToTop) ?
                       thisNode.offsetHeight + elc_getParentTop(thisNode, topElementId) :
                       0;
        if (thisBottom > maxBottom) { maxBottom = thisBottom; }
    }
    if (!thisNode.childNodes || thisNode.childNodes.length == 0) { return maxBottom; };

    // continuation
    for (var nodeIndex = 0; nodeIndex < thisNode.childNodes.length; nodeIndex++) {
        var thisBottom = 0;
        var childNode = thisNode.childNodes[nodeIndex];
        if (childNode
            && (excludeElementIdMap ? !excludeElementIdMap[childNode.id] : true)
            && (brandExcludeElementNodeNameMap ? !brandExcludeElementNodeNameMap[childNode.nodeName] : true)
            ) {
            if (childNode.offsetHeight) {
                thisBottom = (isRelativeToTop) ?
                               childNode.offsetHeight + elc_getParentTop(childNode, topElementId) :
                               childNode.offsetHeight;
            }
            if (childNode.childNodes && childNode.childNodes.length > 0) {
                // note: the topElementId and excludeElementId should not be necessary in the recursive descent
                thisBottom = elc_getMaxBottom( childNode, thisBottom, topElementId, excludeElementIdMap );
            }
            if (thisBottom < 1) continue;
            if (thisBottom > maxBottom) { maxBottom = thisBottom; }
            //alert( childNode + ' ' + childNode.nodeName + ' ' + childNode.id + ' ' + thisBottom + ' ' + maxBottom );
        }
    }
    return maxBottom;
}

// get offset to a parent Node identified
// by the topLevelId. If no topLevelId is supplied
// get offset to top of document
function elc_getParentTop( thisNode, topLevelId ) {
var oTop = 0;
if (thisNode.offsetParent) {
    do {
        oTop += thisNode.offsetTop;
        thisNode = thisNode.offsetParent;
    } while (thisNode && (topLevelId ? thisNode.id != topLevelId : true));
}
return oTop;
}

function elc_getNodeMaxBottom(thisNode, thisMaxBottom, topElementId, excludeElementIdMap) {
    thisMaxBottom = elc_getMaxBottom(thisNode, thisMaxBottom, topElementId, excludeElementIdMap, false );
    return thisMaxBottom;
}

function doNothing(){

}

function createCookie(name,value,days) {
 if (days) {
 var date = new Date();
 date.setTime(date.getTime()+(days*24*60*60*1000));
 var expires = "; expires="+date.toGMTString();
 }
 else var expires = "";
 document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
 var nameEQ = name + "=";
 var ca = document.cookie.split(';');
 for(var i=0;i < ca.length;i++) {
 var c = ca[i];
 while (c.charAt(0)==' ') c = c.substring(1,c.length);
 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
 }
 return null;
}

function eraseCookie(name) {
 createCookie(name,"",-1);
}