function toggleBranch(branchRow) {
    doTimed('toggleBranch', function() {
        var branchTbody = branchRow.parentNode;
        markFoldedState(branchTbody, !isFolded(branchTbody));
        fixBranchVisibility(branchTbody);

        if(isFolded(branchTbody)){
            //            Toggle to close, fix for tbody display
            var depth = getDepth(branchTbody);
            var row = nextElement(branchTbody);
            var rowDepth = getDepth(row);
            while (row && rowDepth>depth) {
                row.style.display ='none';
                row = nextElement(row);
                rowDepth = getDepth(row);
            }
            nextElement(branchTbody).style.display = 'none';
        } else {
            //            Toggle to open, fix for tbody display
            depth = getDepth(branchTbody);
            row = nextElement(branchTbody);
            rowDepth = getDepth(row);
            while (row && rowDepth>depth) {
                var row2 = row.firstChild;
                var noVisibleCildren = true;
                for(row2; row2; row2 = nextElement(row2)){
                    if (row2.style.display== ""){
                        noVisibleCildren = false;
                    }
                }
                if (noVisibleCildren == true){
                    row.style.display = 'none';
                } 
                row = nextElement(row);
                rowDepth = getDepth(row);
            }
        }
    });
}

/**
 * public function
 *
 * go up the tree and if we got a parent then unfold it.
 */
function showLeaf(leafTr) {
    
    var leafTBody = leafTr.parentNode;
    var highestAncestor;
    // unfold the row itself if it's a branch (ZOKOMO-85)
    for (var branchToBeUnfolded = isBranch(leafTBody) ? leafTBody : getParentInTree(leafTBody);
        branchToBeUnfolded;
        branchToBeUnfolded = getParentInTree(branchToBeUnfolded)
        ) {
        markFoldedState(branchToBeUnfolded, false);
        highestAncestor = branchToBeUnfolded;
        
    }
    
    fixBranchVisibility(highestAncestor);

    var depth = getDepth(highestAncestor);
    var row = nextElement(highestAncestor);
    var rowDepth = getDepth(row);
    var folded = false;
    while (row && rowDepth>depth) {
        if (isBranch(row)){
            if(isFolded(row)){
                folded = true;
            }

        } else {
            if(folded){
                setTbodyVisible(row, false, false);
            }
        }
        row = nextElement(row);
        rowDepth = getDepth(row);
    }

    setTrVisible(leafTr,true);  //speciaal voor IE
    leafTr.style.display='';
    leafTr.parentNode.style.display='';

//if (Prototype.Browser.IE && (!leaf.currentStyle.hasLayout)) leaf.setStyle({zoom: 1});
}


function markFoldedState(branch, folded) {
    replaceClassName(branch, folded ? 'unfolded' : 'folded', folded ? 'folded' : 'unfolded');
/*
	var input = branch.firstDescendant().firstDescendant().firstDescendant();
	if(input) {
		input.value = folded ? 'false' : 'true';
	}
     */
}
function isFolded(branch) {
    //	console.debug(branch);
    //	console.debug(typeof branch);
    return hasClassName(branch, 'folded'); 
}

/** note that this function can override the visibility of the row if
 * HIDE_EQUAL_ROWS = true */
function setTbodyVisible(tbodyTreeNode, show, fixVisibilityBasedOnHideEqualRows) {
    // hide branches if HIDE_EQUAL_ROWS and it's a branch and the branch-tr has class 'gelijk'
    if (fixVisibilityBasedOnHideEqualRows) {
        show = show && (!HIDE_EQUAL_ROWS || !isBranch(tbodyTreeNode) || !hasClassName($(tbodyTreeNode).down(), 'gelijk'));
    }
    if (Prototype.Browser.IE) fixIE6TbodyVisibility(tbodyTreeNode, show);
    else if (show) {
        tbodyTreeNode.style.display = '';
    } else {
        tbodyTreeNode.style.display = 'none';
    }
    // fix visibility based on HIDE_EQUAL_ROWS
    if (fixVisibilityBasedOnHideEqualRows && !isBranch(tbodyTreeNode)) {
        var trs2 = tbodyTreeNode.childNodes;
        var showTBody = false;
        for (var j=0; j<trs2.length; j++) {
            var tr2 = trs2[j];
            if (!Object.isElement(tr2)) continue;
            setTrVisible(tr2, show && (!HIDE_EQUAL_ROWS || !hasClassName(tr2, 'gelijk')));
            if (!hasClassName(tr2, 'gelijk')){
                showTBody = true;
            }
        }
        if(!showTBody){
            tbodyTreeNode.style.display='none';
        }
        if(!HIDE_EQUAL_ROWS){
            tbodyTreeNode.style.display='';
        }
        
    }
}
function isVisible(elem) {
    return /* Prototype.Browser.IE ? */ elem.style.display != 'none' 
//  : $(elem).hasClassName('opened');
}
function setTrVisible(row, show) {
    if (Prototype.Browser.IE) fixIE6TrVisibility(row, show);
    else if (show) {
        row.style.display = '';
    } else {
        row.style.display = 'none';
    }
    
}

/**
 * private function, use toggleBranch or showLeaf instead.
 */
function fixBranchVisibility(branch) {
    if(!FIRST_CALL_DONE){
        var depth = getDepth(branch);
        var row = nextElement(branch);
        var rowDepth = getDepth(row);
        while (row && rowDepth>depth) {
            row.firstChild.style.display ='';
            row = nextElement(row);
            rowDepth = getDepth(row);
        }
    }
    depth = getDepth(branch);
    var lastBranchRowAtLevel = [];
    lastBranchRowAtLevel[depth] = branch;
    row = nextElement(branch);
    rowDepth = getDepth(row);
    while (row && rowDepth>depth) {
        parentBranch = lastBranchRowAtLevel[rowDepth-1];
        // a node is visible when its parent is not folded and its parent is visible
        setTbodyVisible(row, !isFolded(parentBranch) && isVisible(parentBranch), true);
        if (isBranch(row)) lastBranchRowAtLevel[rowDepth] = row;
        row = nextElement(row);
        rowDepth = getDepth(row);
    }
}

/** returns the next sibling of elem that is a tag (not a text node).
 * Same as prototype's Element.next(), but slightly faster in IE. */
function nextElement(elem) {
    var result = elem.nextSibling;
    while (result && result.nodeType!=1) result = result.nextSibling;
    return result;
}
function previousElement(elem) {
    var result = elem.previousSibling;
    while (result && result.nodeType!=1) result = result.previousSibling;
    return result;
}

function getParentInTree(nodeTbody) {
    var depth = getDepth(nodeTbody);
    var result = previousElement(nodeTbody);
    while (result && result.tagName.toLowerCase()=="tbody" && getDepth(result) >= depth) result = previousElement(result);
    return result && result.tagName.toLowerCase()=="tbody" ? result : null;
}

function getChildren(branch) {
    var child = branch.next();

    var branchDepth = getNodeDepth(branch);
    var childDepth = getNodeDepth(child);
    var i = 0;
    var childArray = new Array;
	
    while (child != undefined && childDepth > branchDepth) {
        if (childDepth - 1 == branchDepth) {
            childArray[i] = child;
            i++;
        }
        child = child.next();
        if( child ) {
            childDepth = getNodeDepth(child);
        }
    }
	
    return childArray;
}

function determineAllBranchEqualities() {
    for (var tbodyElem = document.getElementById('1st_tbody');
        tbodyElem;
        tbodyElem = nextElement(tbodyElem)) {
        if (isBranch(tbodyElem)) {
            determineBranchEquality(tbodyElem);
            removeClassName(tbodyElem, 'calculate');
        }
    }
}

function determineBranchEquality(branch) {
    branch = $(branch);
    var branchTr = branch.down();
    if (hasClassName(branchTr, 'gelijk')) return true;
    else if (hasClassName(branchTr, 'anders')) return false;
    
    var depth = getDepth(branch);
    var child = nextElement(branch);
    var childDepth = getDepth(child);
    var equalSoFar = true;
    while (equalSoFar && childDepth > depth) {
        // only interested in children, not other descendants
        if (childDepth == depth+1) {
            equalSoFar = equalSoFar && (
                isBranch(child) ? determineBranchEquality(child) : determineLeafEquality(child)
                );
        }
        child = nextElement(child);
        childDepth = getDepth(child);
    }
    removeClassName(branchTr, equalSoFar ? 'anders' : 'gelijk');
    removeClassName(branchTr, 'calculate');
    addClassName(branchTr, equalSoFar ? 'gelijk' : 'anders');
    return equalSoFar;
}

/** returns true only when all the child rows have class name 'gelijk' */
function determineLeafEquality(leafTbody) {
    var trs = leafTbody.childNodes;
    for (var i=0; i<trs.length; i++) {
        var row = trs[i];
        if (!Object.isElement(row)) continue;
        if (!hasClassName(row, 'gelijk')) return false;
    }
    return true;
}

function isBranch(element) {
    return hasClassName(element, 'branch');
}

function getDepth(nodeTbody) {
    return nodeTbody ? parseInt(nodeTbody.getAttribute('depth')) : -1;
}

function replaceClassName(element, src, dest) {
    if(hasClassName(element,src)) {
        removeClassName(element,src);
        addClassName(element,dest);
    // return src;
    }
//return dest;
}
function hasClassName(element, className) {
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className ||
        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
}

function removeClassName(element, className) {
    element.className = element.className.replace(
        new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
}

function addClassName(element, className) {
    if (!hasClassName(element, className)){
        element.className += (element.className ? ' ' : '') + className;
    }
    return element;
}

function refreshDifferences() {
    var anchor = document.getElementById('view-differences-span');
    if(hasClassName(anchor, 'hide')) {
        hideEqualRows();
    }
}

var HIDE_EQUAL_ROWS = false;
var FIRST_CALL_DONE = false;

function hideEqualRows() {
    doTimedWithWaitCursor('hideEqualRows', function() {

        if(!FIRST_CALL_DONE){
            restoreAll();
        }

        determineAllBranchEqualities();

        HIDE_EQUAL_ROWS = true;    
        
        showAll(true);

        removeClassName(document.getElementById('view-all-span'),'hide');
        addClassName(document.getElementById('view-differences-span'),'hide');
        addClassName(document.getElementById('open-all-span'), 'hide');
        removeClassName(document.getElementById('close-all-span'),'hide');
    });
   

}
	
function showEqualRows() {
    doTimedWithWaitCursor('showEqualRows', function() {

        if(FIRST_CALL_DONE){
            restoreAll();
        }
        
        HIDE_EQUAL_ROWS = false;

        showAll(true);
        addClassName(document.getElementById('view-all-span'),'hide');
        removeClassName(document.getElementById('view-differences-span'),'hide');
        addClassName(document.getElementById('open-all-span'), 'hide');
        removeClassName(document.getElementById('close-all-span'),'hide');
    });

}

function showAll(fixVisibilityBasedOnHideEqualRows) {

    var row = document.getElementById('1st_tbody');
    for (row; row; row = nextElement(row)) {
        setTbodyVisible(row, true, fixVisibilityBasedOnHideEqualRows);
        if (isBranch(row)) markFoldedState(row, false);
    }

}

function fixIE6TbodyVisibility(tbodyElement, visible) {
    var st = visible ? '' : 'none';
    tbodyElement.style.display = st;

    var trs = tbodyElement.childNodes;
    for (var i = 0; i < trs.length; i++) {
        fixIE6TrVisibility(trs[i], visible);
    }
}
function fixIE6TrVisibility(trElement, visible) {
    var st = visible ? '' : 'none';
    trElement.style.display = st;
    var tds = trElement.childNodes;
    for (var j = 0; j < tds.length; j++) {
        tds[j].style.display = st;
    }
}

function openAll() {
    doTimedWithWaitCursor('openAll', function() {

        if(!FIRST_CALL_DONE){
            restoreAll();
        }
        FIRST_CALL_DONE = true;

        if(HIDE_EQUAL_ROWS){
            HIDE_EQUAL_ROWS = false;

            showAll(true);
        }
        showAll(true);
        showAll(false);

        addClassName(document.getElementById('view-all-span'),'hide');
        removeClassName(document.getElementById('view-differences-span'),'hide');
        addClassName(document.getElementById('open-all-span'), 'hide');
        removeClassName(document.getElementById('close-all-span'),'hide');
    });

}

function closeAll() {
    doTimedWithWaitCursor('closeAll', function() {
        if(!FIRST_CALL_DONE){
            restoreAll();
        }
        if(HIDE_EQUAL_ROWS){
            HIDE_EQUAL_ROWS = false;
            showAll(false);
        }
        
        var row = document.getElementById('1st_tbody');
        for (row; row; row = nextElement(row)) {
            
            if(!isBranch(row)){
                setTbodyVisible(row, false, false);
                 
            } else {
                if(getDepth(row) > 0 ) {
                    setTbodyVisible(row, false, false);
                } else {
                    setTbodyVisible(row, true, false);
                }
            }
            if (isBranch(row)) markFoldedState(row, true);
        }

        addClassName(document.getElementById('view-all-span'),'hide');
        removeClassName(document.getElementById('view-differences-span'),'hide');
        addClassName(document.getElementById('close-all-span'),'hide');
        removeClassName(document.getElementById('open-all-span'),'hide');

    });
    
}

function restoreAll(){

    if(!FIRST_CALL_DONE){
        //set everything visable
        var row = document.getElementById('1st_tbody');
        for (row; row; row = nextElement(row)) {
            row.style.display='';
            var row2 = row.firstChild;
            for(row2; row2; row2 = nextElement(row2)){
                row2.style.display ='';
            }
        }
    }
    FIRST_CALL_DONE = true;
}
function checkTree(){
    var allOpen = true;
    var branch = null;
    var subBranch = null ;
    var row = document.getElementById('1st_tbody');
    var prevRow = null;
    for (row; row; row = nextElement(row)) {
        var row2 = row.firstChild;
        var noVisibleCildren = true;
        for(row2; row2; row2 = nextElement(row2)){
            if (row2.style.display== ""){
                noVisibleCildren = false;
            }
        }
        if (noVisibleCildren == true){
            row.style.display = 'none';
        }

        //      Check the table for correct images
        if(prevRow != null){
            if(getDepth(prevRow) == 0){
                branch = prevRow;
                if(isBranch(branch) && !noVisibleCildren){
                    markFoldedState(branch, false);
                } else{
                    allOpen = false;
                }
            } else if (getDepth(prevRow) == 1){
                subBranch = prevRow;
                if(isBranch(subBranch) && !noVisibleCildren){
                    markFoldedState(subBranch, false);
                    markFoldedState(branch, false);
                } 
            }
        }
        prevRow = row;

    }
    if(allOpen){
        addClassName(document.getElementById('open-all-span'), 'hide');
        removeClassName(document.getElementById('close-all-span'),'hide');
    }

}
