function HierachicalNodeScanner( formNode, hierachyName, maxScanLevel ) {
    this.formNode = formNode;
    this.hierachyName = hierachyName;
    this.maximumScanLevel = maxScanLevel;
    if( this.maximumScanLevel < 0 ) {
        this.maximumScanLevel = 0
    }
    if( this.maximumScanLevel > 20 ) {
        this.maximumScanLevel = 20
    }
    this.hierachicalComponents = new Array();
};

HierachicalNodeScanner.prototype.findFirstDepthLayer = function() {
    var firstLayerNodes = new Array();

    for( var level = 0; level <= this.maximumScanLevel && firstLayerNodes.length == 0 ; level++ ) {
        firstLayerNodes = getNodesWithNameAndAttributeValue( this.formNode, "DIV", "class", "depth" + level );
    }
    return firstLayerNodes;
};

HierachicalNodeScanner.prototype.getFirstDepthLayerLevel = function() {
    var firstLevel = -1;
    for( var level = 0; level <= this.maximumScanLevel && firstLevel == -1 ; level++ ) {

        var firstLayerNodes = getNodesWithNameAndAttributeValue( this.formNode, "DIV", "class", "depth" + level );
        if( firstLayerNodes.length > 0 ) {
            firstLevel = level;
        }
    }
    return firstLevel;
};

HierachicalNodeScanner.prototype.getHierachicalComponents = function() {
    if( this.hierachicalComponents.length == 0 ) {
        this.scanHierachy();
    }
    return this.hierachicalComponents;
};

HierachicalNodeScanner.prototype.scanHierachy = function() {

    this.hierachicalComponents = new Array();

    var firstLevel = this.getFirstDepthLayerLevel();

    if( firstLevel != -1 ) {
        var firstNodes = this.findFirstDepthLayer();

        // if there is level 0 node, there can only be one, its childs are all siblings

        if( firstLevel == 0 ) {
            var leve0Node = firstNodes[0];
            var parentFormNode = this.getFormNodeForLevel( leve0Node );
            if( parentFormNode != null ) {
                
                var nextNodeHierachy = new ComponentHierachy( parentFormNode , null, null );
                this.scanForChildNodes( nextNodeHierachy, leve0Node.parentNode , 0 );
                this.hierachicalComponents.push( nextNodeHierachy );
            }

        } else {

            for( var index = 0; index < firstNodes.length; index++ ) {
                var nextNode = firstNodes[ index ];
                var parentFormNode = this.getFormNodeForLevel( nextNode );
                // only create a hierachy where the depth > 0
                if( parentFormNode != null && this.levelHasChildNodes( nextNode, firstLevel ) ) {

                    var nextNodeHierachy = new ComponentHierachy( parentFormNode , null, null );
                    //alert( "parent Node " + parentFormNode.getValue() );
                    this.scanForChildNodes( nextNodeHierachy, nextNode, firstLevel );
                    this.hierachicalComponents.push( nextNodeHierachy );
                }
            }
        }
    }

};

HierachicalNodeScanner.prototype.levelHasChildNodes = function( nextLevelNode, scanLevel ) {
    var hasChilds = false;
    if( nextLevelNode != null ) {

        // verify for childNodes
        if( nextLevelNode.childNodes != null && nextLevelNode.childNodes.length > 0 ) {
            for( var index = 0; index < nextLevelNode.childNodes.length && hasChilds == false ; index++ ) {
                var nextNode = nextLevelNode.childNodes.item( index );
                if( this.isDepthNode( nextNode ) ) {

                    //alert( nextNode.getAttributeNode( "class" ).value + " != " + ("depth" + scanLevel) );
                    if( nextNode.getAttributeNode( "class" ).value != ("depth" + scanLevel) ) {
                        hasChilds = true;
                    }
                }
            }
        }
    }
    return hasChilds;
};

HierachicalNodeScanner.prototype.isDepthNode = function( someNode ) {
    var isDNode = false;
    if( someNode != null ) {

        if( someNode.nodeName == "DIV" ) {

            var attrClass = someNode.getAttributeNode( "class" );
            if( attrClass != null ) {
                isDNode = attrClass.value.indexOf( "depth" ) != -1;
            }
        }
    }
    return isDNode;
};

HierachicalNodeScanner.prototype.getFormNodeForLevel = function( depthNode ) {

    var depthFormNode = null;
    var inputNodes = getNodesWithNameAndAttributeValue( depthNode, FormNodeMetadata.ELEMENT_INPUT, "name", this.hierachyName );
    if( inputNodes.length > 0 ) {
        depthFormNode = new FormNode( inputNodes[0] );
    }
    return depthFormNode;
};

HierachicalNodeScanner.prototype.scanForChildNodes = function( parentComponent, nextLevelNode, currentScanLevel  ) {

    if( currentScanLevel < this.maximumScanLevel && this.levelHasChildNodes( nextLevelNode , currentScanLevel ) ) {

        // scan childs

        if( nextLevelNode.childNodes != null && nextLevelNode.childNodes.length > 0 ) {

            for( var index = 0; index < nextLevelNode.childNodes.length; index++ ) {
                var nextNode = nextLevelNode.childNodes.item( index );

                if( this.isDepthNode( nextNode ) ) {

                    var nextChildFormNode = this.getFormNodeForLevel( nextNode );
                    if( nextChildFormNode != null ) {

                        //alert( "found childnode: " + nextChildFormNode.getValue() + " for " + parentComponent.getFormNode().getValue() );
                        var nextChildComponent = new ComponentHierachy( nextChildFormNode, null, parentComponent );
                        parentComponent.addComponent( nextChildComponent );
                        this.scanForChildNodes( nextChildComponent, nextNode, currentScanLevel + 1  );

                    }

                }
            }

        }
    }
};

HierachicalNodeScanner.prototype.getComponentForNode = function( hierachyNode ) {

    var correspondingHierachy = null;
    var components = this.getHierachicalComponents();

    if( components != null && components.length > 0 ) {
        var asFormNode = new FormNode( hierachyNode );
        for( var index=0; index < components.length && correspondingHierachy == null ; index ++ ) {
            correspondingHierachy = components[ index ].findLeaf( asFormNode.getValue() );
        }
    }

    return correspondingHierachy;
};
