

/* tree class ################################################################
 *
 * Provides default functionality and gui output.
 * Currently only used as base class for extending.
 *
 */

/* Constructor
 */
function navTree () {
}

/* Inits the tree's instance variables. (should be called directly after instantiation)
 *
 * @param id the unique id of the tree. (used for requests to load further subtrees and should be named exactly like the tree instance variable)
 * @param rootId the unique id of the tree's root node. (if not provided, "root" is used as default id)
 * @param rootLabel the label for the tree's root node.
 */
navTree.prototype.init = function(id, rootId, rootLabel) {
	this.id = id;
	this.ROOT_HOME = rootId ? rootId : "root";
    this.frame = null; // the name of the frame to which the tree will be rendered
    this.target = null; // the id attribute of the html element inside that frame to which the tree will be rendered
    this.lastSelected = null; // the id of the last selected node
    this.nodes = new Array(); // the array to hold the node instances
    this.nodestates = new Array(); // the array to hold the node states (opened, selected, ...)
    this.aHTML = new Array(); // the array to hold the rendered html of the nodes
    this.allLoaded = false; // flag whether this tree is fully loaded
    this.level = 999;       // maximum depth of tree
    this.useCookies = false; // flag whether the tree node states (open/close) are stored in cookies
    // general configuration parameters
	this.config = {
		dataSourceUrl			: 'loadData.do',		// url to call for loading further subtrees
		actionParamName			: 'action',				// the request parameter name to store the action to call
		treeIdParamName			: 'treeId',				// the request parameter name to store the tree id
		nodeIdParamName			: 'nodeId',				// the request parameter name to store the node id (for example to load subnodes starting at this node)
        selectNodeIdParamName   : 'selectNodeId',       // node id's to select
        levelParamName			: 'level',				// the request parameter name to store the # of levels to load (counted from rootnode)
        forceLevelParamName     : 'forceLevel',         // force setting the level to the value given in levelParamName
        searchParamName			: 'searchString',		// the request parameter name to store the string to search for
        oidParamName            : 'oid',                // the request parameter name to store the oid to search for
        searchInfoTypeParamName	: 'searchInfoType',		// the request parameter name to store the infotype to filter the search against
        searchTypeParamName		: 'searchType',			// the request parameter name to store the search type
        searchSelectedParamName	: 'selectedForSearch',	// the request parameter name to store the list of selected node ids to search in
		tsParamName				: 'treeTimeStamp'		// the request parameter name to store the tree timestamp
	}
	// urls to icons
	this.icon = {
		root			: 'images/tree/empty.gif',
		folder			: 'images/tree/folder.gif',
		folderOpen		: 'images/tree/folder.open.gif',
		node			: 'images/tree/page.gif',
		empty			: 'images/tree/empty.gif',			// transparent dummy gif for spacing (should be same size as other icons)
		line			: 'images/tree/line.gif',			// vertical line
		lineJoin		: 'images/tree/line.join.gif',		// |- line
		lineBottom		: 'images/tree/line.bottom.gif',    // L line
		plus			: 'images/tree/plus.gif',			// plus icon with vertical line
		plusBottom		: 'images/tree/plus.bottom.gif',	// plus icon with L line
		nlPlus			: 'images/tree/plus.nolines.gif',	// plus icon with no lines
        unknown			: 'images/tree/plus.gif',
        unknownBottom	: 'images/tree/plus.bottom.gif',
        nlUnknown   	: 'images/tree/plus.nolines.gif',
//        unknown			: 'images/tree/unknown.gif',
//        unknownBottom	: 'images/tree/unknown.bottom.gif',
//        nlUnknown   	: 'images/tree/unknown.nolines.gif',
        minus			: 'images/tree/minus.gif',
		minusBottom		: 'images/tree/minus.bottom.gif',
		nlMinus			: 'images/tree/minus.nolines.gif',
		up				: 'images/tree/up.gif',				// up icon (used for collapsing textarea fields)
		down			: 'images/tree/down.gif',			// down icon (used for extending textarea fields)
		left			: 'images/tree/left.gif',
		right			: 'images/tree/right.gif',
        fullscreen      : 'images/tree/fullscreen.gif'
    };
	// messages for gui output
	this.msg = {
		found			: ' matches found.',
        notFound		: 'No matches found.',
		loading			: 'Loading.',
		loadingNode		: 'Loading data for node: ',
		addLabel		: 'Add ',
		resetLabel      : 'Reset',
        deleteLabel		: 'Delete',
        confirmDelete	: 'Do you really want to delete the node: ',
		invalidInput	: 'Your input contains invalid characters.',
		addToSearch     : 'Add to search form',
        addToBatch      : 'Add to batch update',
        addToMO         : 'Add to media',
        showMedia       : 'Show attached media',
        removeSearch    : 'Remove search query',
		loadQuery       : 'Load query',
        loadBatchJob    : 'Load batch job',
        loadBatchJobTemplate : 'Load batch job template',
        loadPreset      : 'Load preset'
	}
	// init the root node
	if (rootLabel) {
		this.addNode(this.ROOT_HOME,rootLabel);
	} else {
		this.addNode(this.ROOT_HOME,'');
	}
	this.nodestates[this.ROOT_HOME].expanded = true;
}

navTree.prototype.addNodeState = function(id, allLoaded, hidden, expanded, allExpanded, selected) {
    if (!this.nodestates[id]) {
        var newState = new navTreeNodeState();
        newState.allLoaded = (allLoaded==true) ? true : false;
        newState.hidden = (hidden==true) ? true : false;;
        newState.expanded = (expanded==true) ? true : false;;
        newState.allExpanded = (allExpanded==true) ? true : false;;
        newState.selected = (selected==true) ? true : false;;
        this.nodestates[id] = newState;
        if (this.nodes[id]) this.nodes[id].state = newState;
    } else {
        if (this.nodes[id]) {
            this.nodes[id].state = this.nodestates[id];
        }
        this.nodestates[id].allLoaded = (allLoaded==true) ? true : false;
    }
}

/* Resets the tree by deleting all nodes except the root node.
 *
 * @ param includeStates if true, deletes all node states as well
*/
navTree.prototype.reset = function(includeStates) {
	if (this.nodes[this.ROOT_HOME]) {
		var rootLabel = this.nodes[this.ROOT_HOME].label;
	}
    this.lastSelected = null;
    this.nodes = new Array();
    if (includeStates) this.nodestates = new Array();
    this.aHTML = new Array();
    this.allLoaded = false;
    //if (this.nodes[this.ROOT_HOME]) {
		if (rootLabel) {
			this.addNode(this.ROOT_HOME,rootLabel);
		} else {
			this.addNode(this.ROOT_HOME,'');
		}
		this.nodestates[this.ROOT_HOME].expanded = true;
	//}
}

/* Helper function to retrieve html output of all nodes
 *
 * @return the html output
 */
navTree.prototype.getHtml = function() {
	return this.aHTML.join('');
}

/* Loads the tree nodes from an external file that contains the addNode() statement for each node. (currently unused)
 *
 * @param filename the url of the file to load
 */
navTree.prototype.loadFromFile = function(filename) {
	var script = document.createElement("script");
	script.setAttribute("type", "text/javascript");
	script.setAttribute("src", filename);
	document.body.appendChild(script);
}

/* Returns the common parent node's id for given two nodes.
 *
 * @param nodeId1 the first node's id
 * @param nodeId2 the second node's id
 * @return the common parent node's id
 */
navTree.prototype.getCommonParentId = function(nodeId1, nodeId2) {
	var n1 = this.nodes[nodeId1];
	var n2 = this.nodes[nodeId2];
	while(n1.parentId != null) {
		//compare
		var temp = n2;
		while(n2.parentId != null) {
			if (n1.id==n2.id) {
				return n1.id;
			}
			n2 = this.nodes[n2.parentId];
		}
		n2 = temp;
        n1 = this.nodes[n1.parentId];
    }
    return this.ROOT_HOME;
}

/* Expands and selects all nodes matching a search text.
 *
 * @param searchText the text to match the search against
 * @param an optional search type (currently unused)
 */
navTree.prototype.search = function(searchText, searchType) {
	// check the search string for evil content :)
	if (this.validate(searchText)==false) {
		alert(this.msg.invalidInput);
		return;
	}
	// if the tree is not fully loaded, load it before the search
    if (this.allLoaded == false) {
    	this.loadAllSearch(searchText, -1, searchType);
	} else {
	    var matchCount = 0;
	    searchText = unescapeFilteredText(searchText).toLowerCase(); // search is case insensitive
	    // close and deselect all nodes first
	    for (var cn in this.nodes) {
            if (typeof this.nodes[cn] == 'function')
                continue;
            var curState = this.nodestates[cn];
            curState.expanded = false;
	        curState.allExpanded = false;
	        curState.selected = false;
	        curState.hidden = true;
	    }
	    this.lastSelected = null;
	    for (var cn in this.nodes) {
            if (typeof this.nodes[cn] == 'function')
                continue;
		    if (cn!=this.ROOT_HOME) {
		        var curNode = this.nodes[cn];
                if (curNode.label) {
			        var st = curNode.label.toLowerCase();
			        if (st.indexOf(searchText) > -1) {
				        // search hit
			            matchCount++;
			            curNode.state.selected = true;
			            curNode.state.hidden = false;
			            this.lastSelected = curNode.id;
			            while(curNode.parentId != null) {
			                curNode = this.nodes[curNode.parentId];
                            curNode.state.expanded = true;
			                curNode.state.hidden = false;
			            }
			        }
		        }
	        }
	    }
	    // set result message as root node label
	    this.nodes[this.ROOT_HOME].label = "<span class=\"nfo\">"+matchCount+" "+this.msg.found+" '"+searchText+"'.</span>";
	    this.nodestates[this.ROOT_HOME].hidden = false;
	    this.nodes[this.ROOT_HOME].expand();
	    // render the tree
	    this.print();
	    this.setStatus(matchCount+" "+this.msg.found+"'"+searchText+"'.");
	}
}

/* Checks a string for invalid characters (used to filter out javascript injection from search field)
 *
 * @see navTree#search
 *
 * @param value the input string
 *
 * @return true if valid, false if not
 */
navTree.prototype.validate = function(value) {
	/*var invalidChars = ";\"'";
	var temp;
	for (var i=0; i<value.length; i++) {
		temp = "" + value.substring(i, i+1);
		if (invalidChars.indexOf(temp) != "-1") return false;
	}*/
    // validate disabled since server queries should be encoded anyway
    // (thus removing the possibility of JS injection)
    return true;
}

/* Adds a node to the tree
 *
 * @see navTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 */
navTree.prototype.addNode = function(id, label, parentId, allLoaded, url, openIcon, closedIcon) {
    if ((!parentId || this.nodes[parentId]) && !this.nodes[id]) {
    	var newNode = new navTreeNode();
    	newNode.init(id,label,this, parentId,allLoaded, url, openIcon, closedIcon);
	}
}

// move node "id" after the node "afterNode" (in same hierarchy level!)
// if afterNode is not defined, id will be moved to top
navTree.prototype.moveNodeAfter = function(id, afterNode) {
    var treeNodes = this.nodes;
	if (treeNodes[id]) {
        var parentNode = treeNodes[treeNodes[id].parentId];
        if( !parentNode )
            return;
        var insertPos = 0;
        var orgPos = 0;
        //find positions
        for (i = 0; i < parentNode.treeChildren.length; i++) {
            if (afterNode && parentNode.treeChildren[i] == afterNode)
                insertPos = i + 1;
            if (parentNode.treeChildren[i] == id)
                orgPos = i;
        }
        if( insertPos == orgPos || insertPos > parentNode.treeChildren.length) {
            return;
        }
//        alert('insertPos='+insertPos+' orgPos='+orgPos);
        parentNode.treeChildren.splice(orgPos, 1);
        var startA = new Array();
        if( insertPos > 0) {
            startA = parentNode.treeChildren.slice(0, insertPos);
        }
//        alert('start:'+startA.join(','));
        var endA = parentNode.treeChildren.slice(insertPos);
//        alert('end:'+endA.join(','));
        parentNode.treeChildren = startA.concat(id, endA);
//        alert('final:'+parentNode.treeChildren.join(','));
        parentNode.reprint();
    }
}

/* Deletes a node from the tree.
 *
 * @param id the id of the node to delete.
 */
navTree.prototype.deleteNode = function(id) {
	var treeNodes = this.nodes;
	if (treeNodes[id]) {
		var parentNode = treeNodes[treeNodes[id].parentId];
		var found = false;
		for (i=0;i<parentNode.treeChildren.length;i++) {
			if (parentNode.treeChildren[i]==id) found = true;
			if (found) parentNode.treeChildren[i] = parentNode.treeChildren[i+1];
		}
		parentNode.treeChildren.length--;
		treeNodes[id].remove();
//        alert('deleted '+id+' printing '+parentNode.id+' now...');
        parentNode.reprint();
    }
}

/* Deletes a node's direct subnodes. Subnodes of the deleted subnodes are not handled by this method, but must be deleted manually.
 *
 * @param id the id of the node to delete.
 */
navTree.prototype.deleteNodeChildren = function(id) {
	var treeNodes = this.nodes;
	if (treeNodes[id]) {
		for (var i=0;i<treeNodes[id].treeChildren.length;i++) {
			this.deleteNodeChildren(treeNodes[id].treeChildren[i]);
			delete treeNodes[treeNodes[id].treeChildren[i]];
		}
		treeNodes[id].treeChildren = new Array();
	}
}

/* Outputs the tree to the page.
 */
navTree.prototype.print = function(loadCookie) {
    if (this.nodes[this.ROOT_HOME].removeAllHtmlEditors) {
        this.nodes[this.ROOT_HOME].removeAllHtmlEditors();
    }
    if (this.useCookies && loadCookie) {
	    this.loadCookie();
	}
	this.aHTML = new Array();
	this.nodes[this.ROOT_HOME].print();
	this.render();
}

/* Expands all nodes of the tree
 */
navTree.prototype.expandAll = function() {
	this.loadAll();
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
	    var curNode = this.nodes[cn];
        var curState = this.nodestates[cn];
        if (curNode.parentId==this.ROOT_HOME) {
	    	curState.expanded = true;
	    	curState.allExpanded = true;
	    	curState.hidden = false;
    	}
	}
	this.print();
}

/* Collapses all nodes of the tree
 */
navTree.prototype.collapseAll = function() {
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.expanded = false;
	    curState.allExpanded = false;
	    curState.hidden = false;
	}
	this.nodestates[this.ROOT_HOME].expanded = true;
	this.print();
}

/* deselects all nodes of the tree and clears root node label
*/
navTree.prototype.deselectAll = function(reprint) {
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.selected = false;
	}
    if (reprint) {
        this.print();
    }
}

/* If all tree nodes are collapsed, all nodes are expanded, otherwise all nodes are collapsed.
 */
navTree.prototype.toggleAll = function() {
	var collapsed = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		if (cn!=this.ROOT_HOME && this.nodestates[cn].expanded == true) {
			collapsed = false;
		}
	}
	if (collapsed) {
		this.expandAll();
	} else {
//		this.deselectAll();
//      this.collapseAll();
        /*for (var cn in this.nodes) {
            var curState = this.nodestates[cn];
            curState.selected = false;
            curState.expanded = false;
	        curState.allExpanded = false;
	        curState.hidden = false;
        }
        this.nodestates[this.ROOT_HOME].expanded = true;*/
        this.reset(true);
        this.reload(2);
    }
    if (this.useCookies) {
	    this.updateCookie();
	}
}

/* If clicked node is not selected, selects the node and unselects all other nodes
 */
navTree.prototype.handleRightClick = function(obj,idname) {
    var idnametemp = "oid";
    if (idname) {
        idnametemp = idname;
    }
    var clickedNode = obj.getAttribute(idnametemp);
	var cNode = this.nodes[clickedNode];
    var cState = this.nodestates[clickedNode];
    if (cNode && cState && !cState.selected) {
	    for (var cn in this.nodes) {
            if (typeof this.nodes[cn] == 'function')
                continue;
            var curState = this.nodestates[cn];
	        curState.selected = false;
	    }
		cState.selected = true;
		this.lastSelected = clickedNode;
		this.print();
	}
}

/* Stores the list of currently selected nodes for cutting.
 */
navTree.prototype.getSelectedOids = function() {
	var sNodes = new Array();
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
	    var curNode = this.nodes[cn];
        var curState = this.nodestates[cn];
        if (curState.selected) {
	    	sNodes.push(curNode.id);
    	}
	}
	if (sNodes.length>0)
		return sNodes.join(',');
	else
		return '';
}

/* Stores the list of currently selected nodes for cutting.
 */
navTree.prototype.cut = function() {
	// put all selected nodes into a list to cut
	var sNodes = new Array();
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
	    var curNode = this.nodes[cn];
        var curState = this.nodestates[cn];
        if (curState.selected) {
	    	sNodes.push(curNode.id);
    	}
	}
	this.cutOids = sNodes.join(',');
	this.copyOids = null;
}

/* Stores the list of currently selected nodes for copying.
 */
navTree.prototype.copy = function() {
	// put all selected nodes into a list to cut
	var sNodes = new Array();
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
	    var curNode = this.nodes[cn];
        var curState = this.nodestates[cn];
        if (curState.selected) {
	    	sNodes.push(curNode.id);
    	}
	}
	this.copyOids = sNodes.join(',');
	this.cutOids = null;
}

/* Helper method to load the tree and search for a text on load.
 *
 * @see navTree#search
 */
navTree.prototype.loadAllSearch = function(searchText, infoType, searchType) {
	this.setStatus(this.msg.loading);
	var script = document.createElement("script");
	script.setAttribute("type","text/javascript");
	script.setAttribute("src",	this.config.dataSourceUrl+"?"+
								this.config.actionParamName+"=load&"+
								this.config.treeIdParamName+"="+this.id+"&"+
								this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
								this.config.levelParamName+"=999&"+
								this.config.searchTypeParamName+"="+searchType+"&"+
								this.config.searchInfoTypeParamName+"="+infoType+"&"+
                                this.config.searchSelectedParamName+"="+this.getSelectedOids()+"&"+
								this.config.searchParamName+"="+searchText);
	document.body.appendChild(script);
	this.allLoaded = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		this.nodestates[cn].allLoaded = true;
	}
}

/* Loads all tree nodes.
 */
navTree.prototype.loadAll = function() {
	if (this.allLoaded == false) {
		this.setStatus(this.msg.loading);
		var script = document.createElement("script");
		script.setAttribute("type","text/javascript");
		script.setAttribute("src",	this.config.dataSourceUrl+"?"+
									this.config.actionParamName+"=loadall&"+
									this.config.treeIdParamName+"="+this.id+"&"+
									this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
									this.config.levelParamName+"=999");
		document.body.appendChild(script);
		this.allLoaded = true;
		for (var cn in this.nodes) {
            if (typeof this.nodes[cn] == 'function')
                continue;
			this.nodestates[cn].allLoaded = true;
		}
	}
}

/* Loads all tree nodes up to a given level.
 *
 * @param level the maximum level of the node hierarchy to load
 */
navTree.prototype.reload = function(level, selectIds) {
	this.setLoading();
	this.setStatus(this.msg.loading);
    var script = document.createElement("script");
	script.setAttribute("type","text/javascript");
	script.setAttribute("src",	this.config.dataSourceUrl+"?"+
								this.config.actionParamName+"=reload&"+
								this.config.treeIdParamName+"="+this.id+"&"+
								this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
								this.config.levelParamName+"="+level+
                                (selectIds?"&"+this.config.selectNodeIdParamName+"="+selectIds:""));
	document.body.appendChild(script);
	this.allLoaded = false;
}

/* Loads all tree nodes starting at a given node.
 *
 * @param nodeId the node to load the subtree for
 */
navTree.prototype.loadBranch = function(nodeId) {
    if (!this.nodes[nodeId])
        return;
    this.setStatus(this.msg.loadingNode+(this.nodes[nodeId].label ? this.nodes[nodeId].label : ""));
	var script = document.createElement("script");
	script.setAttribute("type","text/javascript");
	script.setAttribute("src",	this.config.dataSourceUrl+"?"+
								this.config.actionParamName+"=load&"+
								this.config.treeIdParamName+"="+this.id+"&"+
								this.config.nodeIdParamName+"="+nodeId+"&"+
								this.config.levelParamName+"="+this.getLoadLevel(nodeId)+"&"+
                                this.config.forceLevelParamName+"="+this.isForceLevel(nodeId));
	document.body.appendChild(script);
	this.nodestates[nodeId].allLoaded = true;
	this.nodes[nodeId].expand();
	this.nodes[nodeId].reprint();
	// check if the whole tree is loaded now
	this.allLoaded = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		if (this.nodestates[cn].allLoaded == false) {
			this.allLoaded = false;
		}
	}
}
/**
 * Return the maximum level to be loaded for the given node ID (starting at the node level).
 */
navTree.prototype.getLoadLevel = function(nodeId) {
    // use default depth (or maximum) by default
    return 999;
}

/**
 * Return true if the level parameter of a tree load should be enforced
 * on the server-side. Otherside, level parameters < 2 will be set to 2
 * because the child-node information is lost otherwise. However, for some
 * applications like the MARS topics tree this information is not relevant in all cases
 */
navTree.prototype.isForceLevel = function(nodeId) {
    return false;
}

/* Loads all tree nodes like on a complete reset
 *
 */
navTree.prototype.loadReset = function() {
	var script = document.createElement("script");
	script.setAttribute("type","text/javascript");
	script.setAttribute("src",	this.config.dataSourceUrl+"?"+
								this.config.actionParamName+"=load&"+
								this.config.treeIdParamName+"="+this.id+"&"+
								this.config.levelParamName+"=999");
	document.body.appendChild(script);
}

/* Shows a loading message in the root node label.
 */
navTree.prototype.setLoading = function() {
    this.setStatus(this.msg.loading);
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
	    curState.expanded = false;
	    curState.allExpanded = false;
	    curState.hidden = false;
	}
    this.nodes[this.ROOT_HOME].label = "<span class=\"nfo\">"+this.msg.loading+"</span>";
    this.nodestates[this.ROOT_HOME].hidden = false;
	this.print();
}

/* Outputs the tree html to the browser.
 *
 * @see navTree#print
 */
navTree.prototype.render = function(id) {
	if (id) {
        var frame = frames[this.frame] && frames[this.frame].document.getElementById(id) ? frames[this.frame] : parent.frames[this.frame];
        if (frame && frame.document.getElementById(id)) frame.document.getElementById(id).innerHTML = this.aHTML.join('');
	} else {
        var frame = frames[this.frame] && frames[this.frame].document.getElementById(this.target) ? frames[this.frame] : parent.frames[this.frame];
        if (frame && frame.document.getElementById(this.target)) frame.document.getElementById(this.target).innerHTML = this.getPrefix()+this.aHTML.join('')+this.getSuffix();
	}
}

/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 *
 * @return the prefix
 */
navTree.prototype.getPrefix = function() {
	return '';
}

/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 * @see navTreeNode#getPrefix
 *
 * @return the suffix
 */
navTree.prototype.getSuffix = function() {
	return '';
}

/**
 * Returns the frame where the tree is rendered.
 */
navTree.prototype.getTreeFrame = function() {
    return frames[this.frame] ? frames[this.frame] : parent.frames[this.frame];
}

/**
 * Returns the document where the tree is rendered.
 */
navTree.prototype.getTreeDocument = function() {
    return this.getTreeFrame() != null ? this.getTreeFrame().document : document;
}

/* Sets the browser status line text
 *
 * @param text the text to show in the status line
 */
navTree.prototype.setStatus = function(text) {
	try {
		window.status = text;
	} catch(e) {}
}

/* Appends a text to the browser status line
 *
 * @param text the text to show in the status line
 */
navTree.prototype.appendStatus = function(txt) {
	window.status += txt;
}

/* Clears all cookies for this tree.
 */
navTree.prototype.clearCookie = function() {
	var now = new Date();
	var yesterday = new Date(now.getTime() - 1000 * 60 * 60 * 24);
	this.setCookie('tree.'+this.id+'.visible', 'cookieValue', yesterday);
	this.setCookie('tree.'+this.id+'.expanded', 'cookieValue', yesterday);
}

/* Helper function to set a cookie
 */
navTree.prototype.setCookie = function(cookieName, cookieValue, expires, path, domain, secure) {
    set_cookie(cookieName, cookieValue, expires, path, domain, secure);
}

/* Helper function to get a cookie
 *
 * @param cookieName the name of the cookie to retrieve
 *
 * @return the cookie value
 */
navTree.prototype.getCookie = function(cookieName) {
    return get_cookie(cookieName);
}

/* Updates the cookies representing the tree state
 *
 * @see navTree#loadCookie
 */
navTree.prototype.updateCookie = function() {
	//var strVisible = '';
	var strExpanded = '';
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		/*
		if (this.nodes[cn].hidden == false) {
			if (strVisible) strVisible += '%';
			strVisible += this.nodes[cn].id;
		}
		*/
		if (this.nodestates[cn].expanded == true) {
			if (strExpanded) strExpanded += '%';
			strExpanded += this.nodes[cn].id;
		}
	}
	//this.setCookie('tree.'+this.id+'.visible', strVisible);
	this.setCookie('tree.'+this.id+'.expanded', strExpanded);
}

/* Loads the tree's node states from cookies.
 *
 * @see navTree#updateCookie
 */
navTree.prototype.loadCookie = function() {
	/*
	var aVisible = this.getCookie('tree.'+this.id+'.visible').split('%');
	for (var n=0; n<aVisible.length; n++) {
		if (this.nodes[aVisible[n]]) {
			this.nodes[aVisible[n]].hidden = false;
		}
	}
	*/
	var aExpanded = this.getCookie('tree.'+this.id+'.expanded').split('%');
	for (var n=0; n<aExpanded.length; n++) {
		if (this.nodes[aExpanded[n]]) {
			this.nodestates[aExpanded[n]].expanded = true;
		}
	}
}



/* tree node class ###########################################################
 *
 * Provides default functionality and gui output.
 * Currently only used as base class for extending.
 *
 */

/*
* Constructor
*
* @see navTree#addNode
*/
function navTreeNodeState() {
}

/*
* Constructor
*
* @see navTree#addNode
*/
function navTreeNode() {
}

/* Inits the tree node's instance variables. (should be called directly after instantiation, i.e. in the tree's addNode method.)
 * @see navTree#addNode
 *
 * @param id the unique id of the node.
 * @param label the node's text label
 * @param currentTree the tree the node is being added to
 * @param parentId this node's parent id in the tree (the parent node must already exist in the tree)
 * @param allLoaded flag whether this node is already fully loaded
 * @param url the url the node links to
 * @param openIcon the icon displayed when the node is open
 * @param openIcon the icon displayed when the node is closed
 */
navTreeNode.prototype.init = function(id, label, currentTree, parentId, allLoaded, url, openIcon, closedIcon) {
    this.tree = currentTree;
    this.id = id;
    this.label = label;
    this.url = url;
    this.openIcon = openIcon;
    this.closedIcon = closedIcon;
    this.treeChildren = new Array();
    this.isLastSibling = false;
    this.parentId = parentId;
    currentTree.nodes[currentTree.length]= id;
    currentTree.nodestates[currentTree.length]= id;
    currentTree.length++;
    var thisPar = this.tree.nodes[parentId];
	if (parentId) {
      thisPar.treeChildren[thisPar.treeChildren.length] = id;
    }
    this.tree.nodes[id] = this;

    this.tree.addNodeState(id, allLoaded, false, false, false, false);
}

/* Expands the node by changing its state and re-rendering its subtree.
 * If the node is not fully loaded yet, its subtree is loaded.
 *
 * @see navTreeNode#toggle
 */
navTreeNode.prototype.expand = function() {
	this.state.expanded = true;
	this.state.allExpanded = true;
  	var treec = this.treeChildren;
    for (var i = 0, len = treec.length; i < len; i++) {
       this.tree.nodestates[treec[i]].hidden = false;
	}
  	this.reprint();
    if (!this.state.allLoaded) {
      setTimeout(this.tree.id+".loadBranch('"+this.id+"')",1);
    }
}

/* Collapses the node by changing its state and re-rendering its subtree.
 *
 * @see navTreeNode#toggle
 */
navTreeNode.prototype.collapse = function() {
	this.state.expanded = false;
	this.state.allExpanded = false;
	this.reprint();
}

/* Expands the node if it's currently collapsed, otherwise closes the node.
 *
 * @see navTreeNode#getToggler
 */
navTreeNode.prototype.toggle = function() {
    if (this.state.allExpanded == true && this.state.allLoaded == true) {
	    this.collapse();
    } else {
	    this.expand();
    }
    if (this.tree.useCookies) {
	    this.tree.updateCookie();
	}
}

/* Toggles the node's selected state and re-renders the node.
 */
navTreeNode.prototype.toggleSelect = function() {
	if (this.state.selected == true) {
		this.state.selected = false;
	} else {
		this.state.selected = true;
		this.tree.lastSelected = this.id;
	}
	this.reprint();
}

navTreeNode.prototype.doSomethingOnClick = function(event,id) {
    if (!event.ctrlKey && !event.metaKey) {
        this.toggleSelect();
        var dynamicBatchButton = getCSSRule("navframe",".dynamicbatchbutton");
        var dynamicEditButton = getCSSRule("navframe",".dynamiceditbutton");
        var dynamicDefaultButton = getCSSRule("navframe",".dynamicdefaultbutton");
        var dynamicAddToRechercheButton = getCSSRule("navframe",".dynamicaddtorecherchebutton");
        if (dynamicBatchButton.style.display == 'inline') {
            frames["navframe"].addSelectedSearchFieldsById(id);
        } else if (dynamicEditButton.style.display == 'inline') {
            frames["navframe"].attachMoNodeById(id);
        } else if (dynamicAddToRechercheButton.style.display == 'inline') {
            frames["navframe"].addSelectedSearchFieldsById(id);
        } else {
            frames["navframe"].searchMediaByIdWithKO(id,false);
        }
    }
}

/* Handles mouseclicks to imitate explorer selection behaviour.
 *
 * @param event the onclick event
 */
navTreeNode.prototype.handleClick = function(event) {

    if (!event.shiftKey) {
        var selectCount=0;
        if ((!event.ctrlKey && !event.metaKey) && this.tree.lastSelected!=null) {

            var wasSelected = this.state.selected==true ? true : false;
            for (var cn in this.tree.nodes) {
                if (typeof this.tree.nodes[cn] == 'function')
                    continue;
		        var curState = this.tree.nodestates[cn];
                if( curState.selected )
                    selectCount++;
                curState.selected = false;
            }
            if (wasSelected && selectCount==1) this.state.selected = true;
            this.tree.print();

            return this.doSomethingOnClick(event,this.id);
        } else if ((!event.ctrlKey && !event.metaKey) && this.tree.lastSelected == null) {
            return this.doSomethingOnClick(event,this.id);
        }
        this.toggleSelect();
    } else {
	    // shift select
    	if (this.tree.lastSelected!=null) {
        	// clear all selections
		    for (var cn in this.tree.nodes) {
                if (typeof this.tree.nodes[cn] == 'function')
                    continue;
		        var curState = this.tree.nodestates[cn];
		        curState.selected = false;
		    }
		    var cid = this.tree.getCommonParentId(this.tree.lastSelected,this.id);
        	this.tree.nodes[cid].selectRange(this.tree.lastSelected,this.id, false);
        	this.tree.print();
    	} else {
	    	this.toggleSelect();
    	}
    }
}

/* Selects the range of open/visible subnodes between two given subnodes.
 *
 * @see navTreeNode#handleClick
 *
 * @param nodeId1 the first node to define the range to select
 * @param nodeId2 the second node to define the range to select
 * @param inRange flag whether the current call is inside the range to select
 * @return flag whether the current ended inside the range to select
 */
navTreeNode.prototype.selectRange = function(nodeId1, nodeId2, inRange) {
	if (inRange==true && this.state.hidden==false) this.state.selected = true;
	if (this.id==nodeId1 || this.id==nodeId2) {
		inRange = inRange==true ? false : true;
		if (inRange==true && this.state.hidden==false) this.state.selected = true;
	}

	if (this.state.expanded) {
	    var treec = this.treeChildren;
	    for (var i=0;i<treec.length;i++) {
		    if (this.tree.nodestates[treec[i]].hidden==false)
		    	inRange = this.tree.nodes[treec[i]].selectRange(nodeId1,nodeId2,inRange);
	    }
	}
	return inRange;
}


/* Generates the node's full html output and recursively calls itself for all subnodes.
 *
 * @see navTree#print
 */
navTreeNode.prototype.print = function() {
	var aH = this.tree.aHTML;
	if (this.state.hidden == false) {
	    var treec = this.treeChildren;
	    // check if all children of the node are visible (required to display +/- icons)
	    if (this.state.expanded == true && treec.length > 0) {
		    this.state.allExpanded = true;
	        for (var i = 0, len = treec.length; i < len; i++) {
	        	if (this.tree.nodestates[treec[i]].hidden == true) this.state.allExpanded = false;
            }
	    }
	    // leaf nodes are always fully loaded
//	    if (treec.length==0) {
//		    this.state.allLoaded = true;
//	    }
	    aH.push(this.getPrefix());
	    aH.push(this.getToggler());
	    aH.push(this.getLabel());
	    if (this.state.expanded == true && treec.length > 0) {
		    var lastVisible = treec.length-1;
			if (this.state.allExpanded == false) {
				for (var i = treec.length-1; i > -1; i--) {
					if (this.tree.nodestates[treec[i]].hidden == false) {
						lastVisible = i;
						break;
					}
				}
			}
	        for (var i = 0; i < lastVisible; i++) {
	        	this.tree.nodes[treec[i]].isLastSibling = false;
	        	this.tree.nodes[treec[i]].print();
            }
        	this.tree.nodes[treec[lastVisible]].isLastSibling = true;
        	this.tree.nodes[treec[lastVisible]].print();
	    }
	    aH.push(this.getSuffix());
	}
}

/* Generates the node's html output (except the container elements) and recursively calls the print method for all subnodes.
 *
 * @see navTreeNode#print
 */
navTreeNode.prototype.reprint = function() {
	this.tree.aHTML = new Array();
	var aH = this.tree.aHTML;
	aH.push(this.getToggler());
	aH.push(this.getLabel());
	var treec = this.treeChildren;
	if (this.state.hidden == false) {
		if (this.state.expanded == true && treec.length > 0) {
		    var lastVisible = treec.length-1;
			if (this.state.allExpanded == false) {
				for (var i = treec.length-1; i > -1; i--) {
					if (this.tree.nodestates[treec[i]].hidden == false) {
						lastVisible = i;
						break;
					}
				}
			}
	        for (var i = 0; i < lastVisible; i++) {
	        	this.tree.nodes[treec[i]].isLastSibling = false;
	        	this.tree.nodes[treec[i]].print();
            }
        	this.tree.nodes[treec[lastVisible]].isLastSibling = true;
        	this.tree.nodes[treec[lastVisible]].print();
	    }
	}
	this.tree.render(this.id);
}

/* Deletes the node and all its subnodes.
 *
 * @see navTree#deleteSubTree
 */
navTreeNode.prototype.remove = function() {
  	var treec = this.treeChildren;
  	var treen = this.tree.nodes;
    for (var i = 0, len = treec.length; i < len; i++) {
       treen[treec[i]].remove();
	}
    delete treen[this.id];
}

/* Changes the parent node of this node
 */
navTreeNode.prototype.changeParentTo = function(newParent) {
	var pNode = this.tree.nodes[this.parentId];
	var npNode = this.tree.nodes[newParent];

	if (npNode) {
		var found = false;
		for (i=0;i<pNode.treeChildren.length;i++) {
			if (pNode.treeChildren[i]==this.id) found = true;
			if (found) pNode.treeChildren[i] = pNode.treeChildren[i+1];
		}
		pNode.treeChildren.length--;

		npNode.treeChildren[npNode.treeChildren.length] = this.id;
	}

	this.parentId = newParent;
}

/**
 * Returns the node level
 */
navTreeNode.prototype.getLevel = function() {
    return (this.parentId != -1 && this.tree.nodes[this.parentId] != null && this.tree.nodes[this.parentId].getLevel)
            ? this.tree.nodes[this.parentId].getLevel() + 1
            : 0;
}

/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 *
 * @return the prefix
 */
navTreeNode.prototype.getPrefix = function() {
	return "<div class=\"treeitem\" id=\""+this.id+"\" >";
}

/* Generates the node's html elements for indentation and toggling the node state. (i.e. +/- icons and connecting lines)
 *
 * @see navTreeNode#print
 *
 * @return the toggler
 */
navTreeNode.prototype.getToggler = function() {
	var plusIcon = (this.isLastSibling==true) ? this.tree.icon.plusBottom : this.tree.icon.plus;
	var minusIcon = (this.isLastSibling==true) ? this.tree.icon.minusBottom : this.tree.icon.minus;
	var lineOnly = (this.isLastSibling==true) ? this.tree.icon.lineBottom : this.tree.icon.lineJoin;
	var outerLine = '';
	var parentId = this.parentId;
	while (parentId && this.tree.nodes[parentId].parentId) {
		if (this.tree.nodes[parentId].isLastSibling) {
			outerLine = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>'+outerLine;
		} else {
			outerLine = '<img src="'+this.tree.icon.line+'" width="18" height="18" border="0" alt=""/>'+outerLine;
		}
		parentId = this.tree.nodes[parentId].parentId;
	}
	if (!this.parentId) {
		return '<img src="'+this.tree.icon.root+'" width="18" height="18" border="0" alt=""/>';
	} else if (this.treeChildren.length == 0 ) {
	    return outerLine+'<img src="'+lineOnly+'" width="18" height="18" border="0" alt=""/><img src="'+(this.openIcon ? this.openIcon : this.tree.icon.node)+'" width="18" height="18" border="0" alt=""/>';
	} else if (this.state.allExpanded == true) {
		return outerLine+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+minusIcon+'" width="18" height="18" border="0" alt="-"/><img src="'+(this.openIcon ? this.openIcon : this.tree.icon.folderOpen)+'" width="18" height="18" border="0" alt=""/><\/a>';
	} else {
    	return outerLine+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+plusIcon+'" width="18" height="18" border="0" alt="+"/><img src="'+(this.closedIcon ? this.closedIcon : this.tree.icon.folder)+'" width="18" height="18" border="0" alt=""/><\/a>';
	}
}

/* Generates the node's html label.
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
navTreeNode.prototype.getLabel = function() {
    if (this.state.selected == true) {
		cls = "label-selected";
    } else {
 		cls = "label";
	}
    //return '<span class="labelcontainer"><a class="'+cls+'" '+(this.url ? 'href="'+this.url+'"' : '')+' onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggleSelect();">'+this.label+'<\/a></span>';
    return '<span class="labelcontainer"><a class="'+cls+'" '+((this.url && this.url.length>0) ? 'href="'+this.url+'"' : '')+' >'+this.label+'</a></span>';
}

/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 * @see navTreeNode#getPrefix
 *
 * @return the suffix
 */
navTreeNode.prototype.getSuffix = function() {
	return "</div>";
}


/* admin tree class ##########################################################
 *
 * Used in the administration area of the mediamanager. This tree doesn't use
 * frame nesting to retain loaded nodes.
 *
 */
function adminNavTree () {}
adminNavTree.prototype = new navTree();
adminNavTree.prototype.constructor = navTree;
adminNavTree.prototype.baseClass = navTree.prototype.constructor;

/* Adds a node to the tree.
 *
 * @see adminNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param target the target attribute of the url the node links to
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 */
adminNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, target, openIcon, closedIcon) {
    if ((!parentId || this.nodes[parentId]) && !this.nodes[id]) {
    	var newNode = new adminNavTreeNode();
    	newNode.init(id,label,this, parentId,allLoaded, url, openIcon, closedIcon);
    	newNode.target = target;
	}
}

/* If all tree nodes are collapsed, all nodes are expanded, otherwise all nodes are collapsed.
 */
adminNavTree.prototype.toggleAll = function() {
	var collapsed = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		if (cn!=this.ROOT_HOME && this.nodestates[cn].expanded == true) {
			collapsed = false;
		}
	}
	if (collapsed) {
		this.expandAll();
	} else {
        this.deselectAll();
        this.collapseAll();
    }
    if (this.useCookies) {
	    this.updateCookie();
	}
}

/* admin tree node class #####################################################
 */
function adminNavTreeNode () {}
adminNavTreeNode.prototype = new navTreeNode();
adminNavTreeNode.prototype.constructor = navTreeNode;
adminNavTreeNode.prototype.baseClass = navTreeNode.prototype.constructor;

/* Generates the node's html elements for indentation and toggling the node state. (i.e. +/- icons and connecting lines)
 *
 * @see navTreeNode#print
 *
 * @return the toggler
 */
adminNavTreeNode.prototype.getToggler = function() {
	var plusIcon = (this.isLastSibling==true) ? this.tree.icon.plusBottom : this.tree.icon.plus;
	var minusIcon = (this.isLastSibling==true) ? this.tree.icon.minusBottom : this.tree.icon.minus;
	var lineOnly = (this.isLastSibling==true) ? this.tree.icon.lineBottom : this.tree.icon.lineJoin;
	var outerLine = '';
	var parentId = this.parentId;
	while (parentId && this.tree.nodes[parentId].parentId) {
		if (this.tree.nodes[parentId].isLastSibling) {
			outerLine = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>'+outerLine;
		} else {
			outerLine = '<img src="'+this.tree.icon.line+'" width="18" height="18" border="0" alt=""/>'+outerLine;
		}
		parentId = this.tree.nodes[parentId].parentId;
	}
	if (!this.parentId) {
		return '<img src="'+this.tree.icon.root+'" width="18" height="18" border="0" alt=""/>';
	} else if (this.treeChildren.length == 0 ) {
	    return outerLine+'<img src="'+lineOnly+'" width="18" height="18" border="0" alt=""/><img src="'+(this.openIcon ? this.openIcon : this.tree.icon.node)+'" width="18" height="18" border="0" alt=""/>';
	} else if (this.state.allExpanded == true) {
		return outerLine+'<a class="controlButton" onclick="'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+minusIcon+'" width="18" height="18" border="0" alt="-"/><img src="'+(this.openIcon ? this.openIcon : this.tree.icon.folderOpen)+'" width="18" height="18" border="0" alt=""/><\/a>';
	} else {
    	return outerLine+'<a class="controlButton" onclick="'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+plusIcon+'" width="18" height="18" border="0" alt="+"/><img src="'+(this.closedIcon ? this.closedIcon : this.tree.icon.folder)+'" width="18" height="18" border="0" alt=""/><\/a>';
	}
}

/*
 * Generates the node's html label.
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
adminNavTreeNode.prototype.getLabel = function() {
    if (this.state.selected == true) {
		cls = "label-selected";
    } else {
 		cls = "label";
	}
	var target = (this.target && this.target.length>0) ? 'target="'+this.target+'"' : '';
    return '<span class="labelcontainer"><a class="'+cls+'" '+((this.url && this.url.length>0) ? 'href="'+this.url+'" '+target : '')+' >'+this.label+'<\/a></span>';
}


/* topics tree class #########################################################
 *
 * Used in the archive area of the mediamanager
 */

function topicNavTree () {}
topicNavTree.prototype = new navTree();
topicNavTree.prototype.constructor = navTree;
topicNavTree.prototype.baseClass = navTree.prototype.constructor;

/* Adds a node to the tree.
 *
 * @see topicNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param infoType the infotype of the topic the node is representing
 * @param keywords the keywords of the topic the node is representing
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 */
topicNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, infoType, keywords, mayLink, openIcon, closedIcon) {
    if (!parentId || this.nodes[parentId]) {
        if (!this.nodes[id]) {
            var newNode = new topicNavTreeNode();
            newNode.init(id,label,this, parentId,allLoaded, url, openIcon, closedIcon);
        } else {
            var newNode = this.nodes[id];
            newNode.label = label;
            newNode.url = url;
            newNode.openIcon = openIcon;
            newNode.closedIcon = closedIcon;
            this.nodestates[id].allLoaded = allLoaded;
        }
        newNode.infoType = infoType;
        newNode.keywords = keywords;
        newNode.mayLink = mayLink;
    }
}

/* Helper method to check if a infotype is included in a list of given infotypes.
*
* @param the comma separated list of infotypes.
* @param the infotype to match against in the list
*
* @return true if the infotype was found in the list, false if not
*/
topicNavTree.prototype.checkInfotype = function(allInfotypes, infotype) {
	if (allInfotypes=='-1') return true;
	var tokens = allInfotypes.tokenize(',', ' ', true);
	for (var tk in tokens) {
        if (typeof tokens[tk] == 'function')
            continue;
		curToken = tokens[tk];
		if (curToken==infotype) return true;
	}
	return false;
}

/* Helper method to select all nodes matching a list of given oids.
 *
 * @see topicNavTree#search
 *
 * @param the comma-separated list of oids to select
 * @param the list of infotypes to filter the oids against (currently unused)
 * @param the search type (used to retain pre-selected nodes)
 */
topicNavTree.prototype.selectOids = function(oids, filterInfotypes, searchType) {
    var matchCount = 0;
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.expanded = false;
        curState.allExpanded = false;
        if (searchType<3) curState.selected = false;
        curState.hidden = true;
    }
    var tokens = oids.tokenize(',', ' ', true);
    var selectedForSearch = new Array();

    for (var tk in tokens) {
        if (typeof tokens[tk] == 'function')
            continue;
        curToken = tokens[tk];
        curNode = this.nodes['t'+curToken];
        if (curNode) {
            var it = curNode.infoType;
	        if ( this.checkInfotype(filterInfotypes, it) ) {
                if( matchCount == 0 )
                    this.lastSelected = curNode.id;
                matchCount++;
	            curNode.state.selected = true;
	            curNode.state.hidden = false;
	            while(curNode.parentId != null) {
	                curNode = this.nodes[curNode.parentId];
                    curNode.state.expanded = true;
	                curNode.state.hidden = false;
	            }
	        }
        }
	}

    this.nodestates[this.ROOT_HOME].hidden = false;
    this.nodestates[this.ROOT_HOME].expanded = true;
    this.nodestates[this.ROOT_HOME].allExpanded = true;
  	var treec = this.nodes[this.ROOT_HOME].treeChildren;
    for (var i = 0, len = treec.length; i < len; i++) {
       this.nodestates[treec[i]].hidden = false;
	}

    // set result message as root node label
    this.nodes[this.ROOT_HOME].label = "<span class=\"nfo\">"+matchCount+" "+this.msg.found+"</span>";
    // render the tree
    this.print();
    this.setStatus(matchCount+" "+this.msg.found);
}


/* Collapses all nodes of the tree
*/
topicNavTree.prototype.collapseAll = function() {
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.expanded = false;
	    curState.allExpanded = false;
	    curState.hidden = false;
	}
	this.nodestates[this.ROOT_HOME].expanded = true;
	this.print();
    this.checkValidity();
}

/* Checks validity of the node and update or remove the node accordingly.
*/
topicNavTree.prototype.checkNodeValidity = function(id) {
	if (this.nodes[id]) {
		var sNode = this.nodes[id];
		if (!sNode.ts) {
			sNode.ts = (this.ts!=null) ? this.ts : 0;
		}
		var script = document.createElement("script");
		script.setAttribute("type","text/javascript");
		script.setAttribute("src",	this.config.dataSourceUrl+"?"+
									this.config.actionParamName+"=check&"+
									this.config.treeIdParamName+"="+this.id+"&"+
									this.config.tsParamName+"="+sNode.ts+"&"+
									this.config.nodeIdParamName+"="+id);
		document.body.appendChild(script);
    }
}

/* Checks validity of the tree and reloads the tree if necessary
 */
topicNavTree.prototype.checkValidity = function() {
	var ts = (this.ts!=null) ? this.ts : 0;
	var script = document.createElement("script");
	script.setAttribute("type","text/javascript");
	script.setAttribute("src",	this.config.dataSourceUrl+"?"+
								this.config.actionParamName+"=check&"+
								this.config.treeIdParamName+"="+this.id+"&"+
                                this.config.levelParamName+"="+this.level+"&"+
								this.config.tsParamName+"="+ts);
    document.body.appendChild(script);
}

/* Expands and selects all nodes matching a search text.
 *
 * @param searchText the text to match the search against
 * @param the infotype to filter the search against. (value '-1' ignores the infotype)
 * @param the search type ('0' = standard, '1' = also match in keywords, '2' = match only in selected branches, '3' = match only in selected branches, but also in keywords
 */
topicNavTree.prototype.search = function(searchText, infoType, searchType) {
	if (this.validate(searchText)==false) {
		alert(this.msg.invalidInput);
		return;
	}
    this.setStatus(this.msg.loading);
    this.setLoading();
    var script = document.createElement("script");
    script.setAttribute("type","text/javascript");
    script.setAttribute("src",	this.config.dataSourceUrl+"?"+
                                this.config.actionParamName+"=search&"+
                                this.config.treeIdParamName+"="+this.id+"&"+
                                this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
                                this.config.levelParamName+"=999&"+
                                this.config.searchTypeParamName+"="+searchType+"&"+
                                this.config.searchParamName+"="+searchText+"&"+
                                this.config.searchInfoTypeParamName+"="+infoType+"&"+
                                this.config.searchSelectedParamName+"="+this.getSelectedOids()
                       );
    document.body.appendChild(script);

}

/* Expands and selects the node by the give oid.
 *
 * @param oid the oid to match the search against
 */
topicNavTree.prototype.searchByOid = function(oid) {
	this.setStatus(this.msg.loading);
    this.setLoading();
    var script = document.createElement("script");
    script.setAttribute("type","text/javascript");
    script.setAttribute("src",	this.config.dataSourceUrl+"?"+
                                this.config.actionParamName+"=searchbyoid&"+
                                this.config.treeIdParamName+"="+this.id+"&"+
                                this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
                                this.config.levelParamName+"=999&"+
                                this.config.oidParamName+"="+oid
                       );
    document.body.appendChild(script);

}

/* topics tree node class ####################################################
*/

function topicNavTreeNode () {}
topicNavTreeNode.prototype = new navTreeNode();
topicNavTreeNode.prototype.constructor = navTreeNode;
topicNavTreeNode.prototype.baseClass = navTreeNode.prototype.constructor;

/* Collapses the node by changing its state and re-rendering its subtree.
 *
 * @see navTreeNode#toggle
 */
topicNavTreeNode.prototype.collapse = function() {
	this.state.expanded = false;
	this.state.allExpanded = false;
	this.reprint();
	this.tree.checkNodeValidity(this.id);
}


/* Generates the node's html elements for indentation and toggling the node state. (i.e. +/- icons and connecting lines)
 * Topic nodes additionally have the oid attribute for storing the id.
 *
 * @see navTreeNode#print
 *
 * @return the toggler
 */
topicNavTreeNode.prototype.getToggler = function() {
	var plusIcon = (this.isLastSibling==true) ? this.tree.icon.plusBottom : this.tree.icon.plus;
	var minusIcon = (this.isLastSibling==true) ? this.tree.icon.minusBottom : this.tree.icon.minus;
    var unknownIcon = (this.isLastSibling==true) ? this.tree.icon.unknownBottom : this.tree.icon.unknown;
    var lineOnly = (this.isLastSibling==true) ? this.tree.icon.lineBottom : this.tree.icon.lineJoin;
	var outerLine = '';
	var parentId = this.parentId;
	while (parentId && this.tree.nodes[parentId].parentId) {
		if (this.tree.nodes[parentId].isLastSibling) {
			outerLine = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>'+outerLine;
		} else {
			outerLine = '<img src="'+this.tree.icon.line+'" width="18" height="18" border="0" alt=""/>'+outerLine;
		}
		parentId = this.tree.nodes[parentId].parentId;
	}
	if (!this.parentId) {
		return '<img src="'+this.tree.icon.root+'" width="18" height="18" border="0" alt="" oid="'+this.id+'"/>';
    } else if (this.state.allLoaded == false) {
        return outerLine+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+unknownIcon+'" width="18" height="18" border="0" alt="-"/><img src="'+(this.openIcon ? this.openIcon : this.tree.icon.folderOpen)+'" width="18" height="18" border="0" alt="" oid="'+this.id+'"/><\/a>';
	} else if (this.treeChildren.length == 0 ) {
	    return outerLine+'<a><img src="'+lineOnly+'" width="18" height="18" border="0" alt=""/><img src="'+(this.openIcon ? this.openIcon : this.tree.icon.node)+'" width="18" height="18" border="0" alt="" oid="'+this.id+'"/><\/a>';
	} else if (this.state.allExpanded == true) {
		return outerLine+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+minusIcon+'" width="18" height="18" border="0" alt="-"/><img src="'+(this.openIcon ? this.openIcon : this.tree.icon.folderOpen)+'" width="18" height="18" border="0" alt="" oid="'+this.id+'"/><\/a>';
	} else {
    	return outerLine+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+plusIcon+'" width="18" height="18" border="0" alt="+"/><img src="'+(this.closedIcon ? this.closedIcon : this.tree.icon.folder)+'" width="18" height="18" border="0" alt="" oid="'+this.id+'"/><\/a>';
	}
}

/* Generates the node's html label and additional buttons
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
topicNavTreeNode.prototype.getLabel = function() {
    if (this.state.selected == true) {
		cls = "label-selected";
    } else {
 		cls = "label";
	}
	if (this.mayLink == false) {
		cls = "label-maynotlink "+cls;
	}

    var label = '<span class="labelcontainer" oid="'+this.id+'"><a class="'+cls+' controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].handleClick(event);">'+this.label+'</a></span>';
	if (this.id!=this.tree.ROOT_HOME) {
        var tempId = "'"+this.id+"'";
        var loadMOButton      = '<a class="dynamicDefaultButton" href="javascript:searchMediaByIdWithKO('+tempId+',false);"><img src="images/tree/show_folder.gif" border="0" alt="'+this.tree.msg.showMedia+'" title="'+this.tree.msg.showMedia+'" align="bottom"></a>';
        var attachToMOButton  = '<a class="dynamicEditButton" href="javascript:attachMoNodeById('+tempId+');"><img src="images/tree/add.gif" border="0" alt="'+this.tree.msg.addToMO+'" title="'+this.tree.msg.addToMO+'" align="bottom"></a>';
        var addToSearchButton = '<a class="dynamicaddtorecherchebutton" href="javascript:addSelectedSearchFieldsById('+tempId+');"><img src="images/tree/add.gif" border="0" alt="'+this.tree.msg.addToSearch+'" title="'+this.tree.msg.addToSearch+'" align="bottom"></a>';
        var addToBatchButton  = '<a class="dynamicBatchButton" href="javascript:addSelectedSearchFieldsById('+tempId+');"><img src="images/tree/add.gif" border="0" alt="'+this.tree.msg.addToBatch+'" title="'+this.tree.msg.addToBatch+'" align="bottom"></a>';
		return label+loadMOButton+attachToMOButton+addToSearchButton+addToBatchButton;//loadButton+deleteButton;
	} else
		return label;

    //return '<span class="labelcontainer"><a class="'+cls+'" '+((this.url && this.url.length>0) ? 'href="'+this.url+'"' : '')+' onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggleSelect();">'+this.label+'<\/a></span>';
}

/* Helper method to recursively search this node's subnodes.
 *
 * @see topicNavTreNode#search
 *
 * @param searchText the text to match the search against
 * @param the infotype to filter the search against. (value '-1' ignores the infotype)
 * @param the search type ('0' = standard, '1' = also match in keywords, '2' = match only in selected branches, '3' = match only in selected branches, but also in keywords
 * @param matchCount the current number of search hits
 *
 * @returns the number of search hits for this node's subtree
 */
topicNavTreeNode.prototype.searchChildren = function(searchText, infoType, searchType, matchCount) {
    if (this.label) {
        var lb = this.label.toLowerCase();
        var it = this.infoType;
        var kw = (this.keywords && searchType=='4') ? this.keywords.toLowerCase() : '';
        if ( ( (lb.indexOf(searchText) > -1) || (kw.indexOf(searchText) > -1) ) && ( (infoType=='-1') || (it==infoType) ) && (this.state.selected==false)) {
            matchCount++;
            this.state.selected = true;
            this.state.hidden = false;
            var curNode = this;
            var tree = this.tree;
            while(curNode.parentId != null) {
                curNode = tree.nodes[curNode.parentId];
                curNode.state.expanded = true;
                curNode.state.hidden = false;
            }
        }
    }
  	var treec = this.treeChildren;
    for (var i = 0, len = treec.length; i < len; i++) {
       matchCount = this.tree.nodes[treec[i]].searchChildren(searchText, infoType, searchType, matchCount);
	}

    return matchCount;
}

/* preset tree class #########################################################
 *
 * Used in the archive area of the mediamanager
 */

function presetNavTree () {}
presetNavTree.prototype = new topicNavTree();
presetNavTree.prototype.constructor = topicNavTree;
presetNavTree.prototype.baseClass = topicNavTree.prototype.constructor;

/* Adds a node to the tree.
 *
 * @see presetNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param infoType the infotype of the topic the node is representing
 * @param keywords the keywords of the topic the node is representing
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 * @param nodeType node type to control display of additional label buttons
 */
presetNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, infoType, keywords, openIcon, closedIcon, nodeType) {
    if (!parentId || this.nodes[parentId]) {
        if (!this.nodes[id]) {
            var newNode = new presetNavTreeNode();
            newNode.init(id,label,this, parentId,allLoaded, url, openIcon, closedIcon);
        } else {
            var newNode = this.nodes[id];
            newNode.label = label;
            newNode.url = url;
            newNode.openIcon = openIcon;
            newNode.closedIcon = closedIcon;
            this.nodestates[id].allLoaded = allLoaded;
        }
        newNode.infoType = infoType;
        newNode.keywords = keywords;
        newNode.nodeType = nodeType;
    }
}

/* Expands and selects all nodes matching a search text.
 *
 * @param searchText the text to match the search against
 */
presetNavTree.prototype.search = function(searchText) {
	if (this.validate(searchText)==false) {
		alert(this.msg.invalidInput);
		return;
	}
    var matchCount = 0;
    searchText = searchText.toLowerCase();
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.expanded = false;
        curState.allExpanded = false;
        curState.selected = false;
        curState.hidden = true;
    }
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curNode = this.nodes[cn];
        if (cn!=this.ROOT_HOME && curNode.label) {
            var sLabel = curNode.label.toLowerCase();
            var sFieldValue = (curNode.fieldValue) ? curNode.fieldValue.toLowerCase() : '';
            if ( (sLabel.indexOf(searchText) > -1) || (sFieldValue.indexOf(searchText) > -1) ) {
                matchCount++;
                curNode.state.selected = true;
                curNode.state.hidden = false;
                while(curNode.parentId != null) {
                    curNode = this.nodes[curNode.parentId];
                    curNode.state.expanded = true;
                    curNode.state.hidden = false;
                }
            }
        }
    }
    this.nodes[this.ROOT_HOME].label = "<span class=\"nfo\">"+matchCount+" "+this.msg.found+" '"+searchText+"'.</span>";
    this.nodestates[this.ROOT_HOME].hidden = false;
    this.print();
    this.setStatus(matchCount+" "+this.msg.found+"'"+searchText+"'.");
}

/* If all tree nodes are collapsed, all nodes are expanded, otherwise all nodes are collapsed.
 */
presetNavTree.prototype.toggleAll = function() {
	var collapsed = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		if (cn!=this.ROOT_HOME && this.nodestates[cn].expanded == true) {
			collapsed = false;
		}
	}
	if (collapsed) {
		this.expandAll();
	} else {
        this.deselectAll();
        this.collapseAll();
    }
    if (this.useCookies) {
	    this.updateCookie();
	}
}


/* preset tree node class ####################################################
 */

function presetNavTreeNode () {}
presetNavTreeNode.prototype = new topicNavTreeNode();
presetNavTreeNode.prototype.constructor = topicNavTreeNode;
presetNavTreeNode.prototype.baseClass = topicNavTreeNode.prototype.constructor;

/* Generates the node's html label and additional buttons
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
presetNavTreeNode.prototype.getLabel = function() {
    if (this.state.selected == true) {
		cls = "label-selected";
    } else {
 		cls = "label";
	}
	var preset = (this.nodeType=='preset') ? ' preset="true"' : "";
	//var label = '<span class="labelcontainer" oid="'+this.id+'"' + preset + '><a class="'+cls+' controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].handleSelect(event);">'+this.label+'</a></span>';
    var label = '';
    if (this.id.substring(0,1)=='i')
        label = '<span class="labelcontainer" oid="'+this.id+'"' + preset + '><a class="'+cls+' controlButton"  onclick="parent.presetTree.nodes[\''+this.id+'\'].toggle();">'+this.label+'</a></span>';
    else
        label = '<span class="labelcontainer" oid="'+this.id+'"' + preset + '><a class="'+cls+' controlButton" onclick="javascript:loadPreset(\''+this.id+'\');">'+this.label+'</a></span>';
    var loadButton = '<a href="'+this.url+'"><img src="images/tree/right.gif" border="0" title="' + this.tree.msg.loadPreset + '" align="bottom"></a>';

	if (this.nodeType=='preset')
		return label+loadButton;
	else
		return label;
}

/* dc tree class #############################################################
 *
 * Used in the main navigation of mars DC.
 */

function dcNavTree () {}
dcNavTree.prototype = new topicNavTree();
dcNavTree.prototype.constructor = topicNavTree;
dcNavTree.prototype.baseClass = topicNavTree.prototype.constructor;
dcNavTree.prototype.maxLevel = 3;   // maximum level for displayed nodes

/* Adds a node to the tree.
 *
 * @see dcNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param infoType the infotype of the topic the node is representing
 * @param keywords the keywords of the topic the node is representing
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 */
dcNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, infoType, keywords, openIcon, closedIcon) {
    if (!parentId || this.nodes[parentId]) {
        if (!this.nodes[id]) {
            var newNode = new dcNavTreeNode();
            newNode.init(id,label,this, parentId,allLoaded, url, openIcon, closedIcon);
        } else {
            var newNode = this.nodes[id];
            newNode.label = label;
            newNode.url = url;
            newNode.openIcon = openIcon;
            newNode.closedIcon = closedIcon;
            this.nodestates[id].allLoaded = allLoaded;
        }
        newNode.infoType = infoType;
        newNode.keywords = keywords;
    }
}

dcNavTree.prototype.getLoadLevel = function(nodeId) {
    // load at least 2 and at most this.maxLevel levels
    return Math.min(2, this.maxLevel - this.nodes[nodeId].getLevel());
}

dcNavTree.prototype.isForceLevel = function(nodeId) {
    // allow level 1 loads since the maximum displayed level may
    // be an odd number
    return true;
}


/* dc tree node class #######################################################
 */

 function dcNavTreeNode () {}
dcNavTreeNode.prototype = new topicNavTreeNode();
dcNavTreeNode.prototype.constructor = topicNavTreeNode;
dcNavTreeNode.prototype.baseClass = topicNavTreeNode.prototype.constructor;

/* Expands the node by changing its state and re-rendering its subtree.
 * Also, all other nodes in the tree are closed.
 * If the node is not fully loaded yet, its subtree is loaded.
 *
 * @see navTreeNode#toggle
 */
dcNavTreeNode.prototype.expand = function() {
	var nodes = this.tree.nodes;
	for (var cn in nodes) {
        if (typeof nodes[cn] == 'function')
            continue;
        var curState = this.tree.nodestates[cn];
        curState.expanded = false;
	    curState.allExpanded = false;
	    curState.selected = false;
	}

	curNode = this;
	curNode.state.hidden = false;
	curNode.state.selected = true;
    while(curNode.parentId != null) {
        curNode = nodes[curNode.parentId];
        curNode.state.expanded = true;
        curNode.state.hidden = false;
    }

	this.state.expanded = true;
	this.state.allExpanded = true;
  	var treec = this.treeChildren;
    for (var i = 0, len = treec.length; i < len; i++) {
       this.tree.nodestates[treec[i]].hidden = false;
	}
  	nodes[this.tree.ROOT_HOME].reprint();

    if (!this.state.allLoaded && this.getLevel() < this.tree.maxLevel) {
        // fetch children
        setTimeout(this.tree.id+".loadBranch('"+this.id+"')",1);
    }
}

/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 *
 * @return the prefix
 */
dcNavTreeNode.prototype.getPrefix = function() {
	return "<div id=\""+this.id+"\" >";
}

/* Simplified toggler: always expand on click
 */
dcNavTreeNode.prototype.toggle = function(skipCheck) {
    this.expand();
    // TODO - this check generates a request for EVERY click on the DC topics navigation tree
    if( skipCheck && skipCheck==true) {
        //skip the check ...
    } else
        this.tree.checkValidity();
    if (this.tree.useCookies) {
	    this.tree.updateCookie();
	}
}

// returns the window height in standards compliance mode
function getWindowHeight(document) {
    var windowHeight = 0;
    if (document.documentElement && document.documentElement.clientHeight) {
        windowHeight = document.documentElement.clientHeight;
    } else if (document.body && document.body.clientHeight) {
        windowHeight = trimPx(document.body.clientHeight);
    }
    if (window.innerHeight) {
        windowHeight = window.innerHeight;
    }
    return windowHeight;
}

/* Generates the node's html elements for indentation.
 *
 * @see navTreeNode#print
 *
 * @return the toggler
 */
dcNavTreeNode.prototype.getToggler = function() {
	var outerLine = '';
	var parentId = this.parentId;
	var indent = 0;
	while (parentId && this.tree.nodes[parentId].parentId) {
		parentId = this.tree.nodes[parentId].parentId;
		indent += 18;
	}
    var scroll = false;
    if (getTop().frames['navframe'] != null) {
        if (getTop().frames['navframe'].document.getElementById('topicsTreeDiv') != null) {
            if (getWindowHeight(getTop().frames['navframe'].document) > 624) {
                scroll = true;
            }
        }
    }
    if (this.state.selected == true) {
        sc = "mars-label-selected";
        sc2 = !scroll?"mars-labelcontainer-selected":"mars-labelcontainer-selected-scroll";
    } else {
        sc = "mars-label";
        sc2 = !scroll?"mars-labelcontainer":"mars-labelcontainer-scroll";
    }
	if (this.parentId == this.tree.ROOT_HOME) {
		sc += " mars-top-label";
	}
	if (!this.parentId) {
		return '';
	} else {
    	//return '<div oid="'+this.id+'" class="'+sc2+'"><div style="float:left;width:'+indent+'px;"></div><div style="float:right;width:'+(200-indent)+'px"><div class="'+sc+'">';
        return '<div oid="'+this.id+'" class="'+sc2+'"><div style="margin-left:'+indent+'px" class="'+sc+'">';
    }
}

/* Generates the node's html label.
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
dcNavTreeNode.prototype.getLabel = function() {

	var label = '<a href="instance.do?action=instance.load&id='+(this.id).substring(1)+'" target="conframe" class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();return parent.'+this.tree.id+'.nodes[\''+this.id+'\'].checkOpen();">'+this.label+'</a></div></div>';
	if (!this.parentId) {
		return '';
	} else {
    	return label;
	}
}

/* Helper method to check if the node is currently expanded.
 *
 * @see dcNavTreeNode#getLabel
 *
 * @return true if the node is expanded, false if not
 */
dcNavTreeNode.prototype.checkOpen = function() {
	return this.state.expanded;
}


/* dc sitemap tree class ####################################################
 *
 * Used in the sitemap of mars DC.
 */

function dcSiteMapNavTree () {}
dcSiteMapNavTree.prototype = new topicNavTree();
dcSiteMapNavTree.prototype.constructor = topicNavTree;
dcSiteMapNavTree.prototype.baseClass = topicNavTree.prototype.constructor;

/* Inits the tree's instance variables. (should be called directly after instantiation)
 * Main change to navTree#init are the icon urls.
 *
 * @param id the unique id of the tree. (used for requests to load further subtrees and should be named exactly like the tree instance variable)
 * @param rootId the unique id of the tree's root node. (if not provided, "root" is used as default id)
 * @param rootLabel the label for the tree's root node.
 */
dcSiteMapNavTree.prototype.init = function(id, rootId, rootLabel) {
	this.id = id;
	this.ROOT_HOME = rootId ? rootId : "root";
    this.frame = null;
    this.target = null;
    this.lastSelected = null;
    this.nodes = new Array();
    this.nodestates = new Array();
    this.aHTML = new Array();
    this.allLoaded = false;
    this.useCookies = false;
	this.config = {
		dataSourceUrl			: 'loadData.do',
		actionParamName			: 'action',
		treeIdParamName			: 'treeId',
		nodeIdParamName			: 'nodeId',
		levelParamName			: 'level',
		searchParamName			: 'searchString',
		searchInfoTypeParamName	: 'searchInfoType',
		searchTypeParamName		: 'searchType',
        searchSelectedParamName	: 'selectedForSearch',
		loaderFrame				: 'treeLoaderFrame',
        tsParamName				: 'treeTimeStamp',
        selectedSearchFiltersParamName: 'selectedSearchFilters'    // the request parameter name to store the selected search filters (extended search)
    }
	this.icon = {
		root			: 'images/tree/empty.gif',
		empty			: 'images/tree/empty.gif',
		line			: 'images/tree/dc/line.gif',
		lineJoin		: 'images/tree/dc/line.join.gif',
		lineBottom		: 'images/tree/dc/line.bottom.gif',
		plus			: 'images/tree/dc/plus.gif',
		plusBottom		: 'images/tree/dc/plus.bottom.gif',
		nlPlus			: 'images/tree/dc/plus.nolines.gif',
//        unknown			: 'images/tree/dc/unknown.gif',
//        unknownBottom	: 'images/tree/dc/unknown.bottom.gif',
//        nlUnknown		: 'images/tree/dc/unknown.nolines.gif',
        unknown			: 'images/tree/dc/plus.gif',
        unknownBottom	: 'images/tree/dc/plus.bottom.gif',
        nlUnknown		: 'images/tree/dc/plus.nolines.gif',
        minus			: 'images/tree/dc/minus.gif',
		minusBottom		: 'images/tree/dc/minus.bottom.gif',
        minusRoot       : 'images/tree/dc/minus.root.gif',
        nlMinus			: 'images/tree/dc/minus.nolines.gif'
	};
	this.msg = {
		found			: ' matches found.',
		notFound		: 'No matches found.',
		loading			: 'Loading.',
		loadingNode		: 'Loading data for node: ',
		addLabel		: 'Add ',
		resetLabel      : 'Reset',
        deleteLabel		: 'Delete',
		confirmDelete	: 'Do you really want to delete the node: ',
		addToSearch     : 'Add to search form'
	}
	if (rootLabel) {
		this.addNode(this.ROOT_HOME,rootLabel);
	} else {
		this.addNode(this.ROOT_HOME,'');
	}
	this.nodestates[this.ROOT_HOME].expanded = true;
}

/* Adds a node to the tree.
 *
 * @see dcSiteMapNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param infoType the infotype of the topic the node is representing
 * @param keywords the keywords of the topic the node is representing
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 */
dcSiteMapNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, infoType, keywords, openIcon, closedIcon) {
    if (!parentId || this.nodes[parentId]) {
        if (!this.nodes[id]) {
            var newNode = new dcSiteMapNavTreeNode();
            newNode.init(id,label,this, parentId,allLoaded, url, openIcon, closedIcon);
        } else {
            var newNode = this.nodes[id];
            newNode.label = label;
            newNode.url = url;
            newNode.openIcon = openIcon;
            newNode.closedIcon = closedIcon;
            this.nodestates[id].allLoaded = allLoaded;
        }
        newNode.infoType = infoType;
        newNode.keywords = keywords;
    }
}


/* Submits a backend search to expand and select all nodes matching the extended search of mars DC.
 *
 * @param searchText the first extended searchfield
 * @param searchText2 the second extended searchfield
 * @param searchText3 the third extended searchfield
 * @param filterInfotypes the list of infotypes to filter the search against (currently unused)
 * @param searchType the first extended searchfield's search type
 * @param searchType2 the second extended searchfield's search type
 * @param searchType3 the third extended searchfield's search type
 */
dcSiteMapNavTree.prototype.search = function(searchText, searchText2, searchText3, filterInfotypes, searchType, searchType2, searchType3, selectedSearchFilters) {
	if (this.validate(searchText)==false) {
		alert(this.msg.invalidInput);
		this.print();
		return;
	}
    //this.setStatus(this.msg.loading);
    var scriptUrl =	this.config.dataSourceUrl+"?"+
                    this.config.actionParamName+"=search&"+
                    this.config.treeIdParamName+"=parent."+this.id+"&"+
                    this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
                    this.config.levelParamName+"=999&"+
                    this.config.searchTypeParamName+"="+searchType+"&"+
                    this.config.searchTypeParamName+"2="+searchType2+"&"+
                    this.config.searchTypeParamName+"3="+searchType3+"&"+
                    this.config.searchInfoTypeParamName+"="+filterInfotypes+"&"+
                    this.config.searchParamName+"="+searchText+"&"+
                    this.config.searchParamName+"2="+searchText2+"&"+
                    this.config.searchParamName+"3="+searchText3+"&"+
                    this.config.searchSelectedParamName+"="+''+"&"+"loadFromFrame=true&"+
                    this.config.selectedSearchFiltersParamName+"="+selectedSearchFilters;
    this.loadFrame(this.config.loaderFrame, scriptUrl);
}

/* Helper method to select all nodes matching a list of given oids.
*
* @see dcSiteMapNavTree#search
*
* @param the comma-separated list of oids to select
* @param the list of infotypes to filter the oids against (currently unused)
* @param the search type (used to retain pre-selected nodes)
*/
dcSiteMapNavTree.prototype.selectOids = function(oids, filterInfotypes, searchType) {
    var matchCount = 0;
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.expanded = false;
        curState.allExpanded = false;
        if (searchType<3) curState.selected = false;
        curState.hidden = true;
    }
    var tokens = oids.tokenize(',', ' ', true);
    var selectedForSearch = new Array();

    for (var tk in tokens) {
        if (typeof tokens[tk] == 'function')
            continue;
        curToken = tokens[tk];
        curNode = this.nodes['t'+curToken];
        if (curNode) {
            var it = curNode.infoType;
	        if ( this.checkInfotype(filterInfotypes, it) ) {
	            matchCount++;
	            curNode.state.selected = true;
	            curNode.state.hidden = false;
	            while(curNode.parentId != null) {
	                curNode = this.nodes[curNode.parentId];
                    curNode.state.expanded = true;
	                curNode.state.hidden = false;
	            }
	        }
        }
	}

    this.nodestates[this.ROOT_HOME].hidden = false;
    this.nodestates[this.ROOT_HOME].expanded = true;
    this.nodestates[this.ROOT_HOME].allExpanded = true;
  	var treec = this.nodes[this.ROOT_HOME].treeChildren;
    for (var i = 0, len = treec.length; i < len; i++) {
       this.nodestates[treec[i]].hidden = false;
	}

    // render the tree
    this.print();
    this.setStatus(matchCount+" "+this.msg.found);
}


/* Helper method to load a given url into a given frame
*
* @see dcSiteMapNavTree#search,dcSiteMapNavTree#loadAllSearch, dcSiteMapNavTree#loadAll, dcSiteMapNavTree#reload, dcSiteMapNavTree#loadBranch
*
* @param targetFrame the frame to load the url into
* @param targetUrl the url to load
*/
dcSiteMapNavTree.prototype.loadFrame = function(targetFrame, targetUrl) {
    if (document.images) {
        getTop().frames[targetFrame].location.replace(targetUrl);
    } else {
        getTop().frames[targetFrame].location.href = targetUrl;
    }
}

/* Submits a backend search to expand and select all nodes matching the extended search of mars DC and
 * at the same time loads all tree nodes.
 *
 * @see dcSiteMapNavTree#search
 *
 * @param searchText the first extended searchfield
 * @param searchText2 the second extended searchfield
 * @param searchText3 the third extended searchfield
 * @param filterInfotypes the list of infotypes to filter the search against (currently unused)
 * @param searchType the first extended searchfield's search type
 * @param searchType2 the second extended searchfield's search type
 * @param searchType3 the third extended searchfield's search type
 */
dcSiteMapNavTree.prototype.loadAllSearch = function(searchText, searchText2, searchText3, infoType, searchType, searchType2, searchType3) {
	this.setStatus(this.msg.loading);
	var scriptUrl =	this.config.dataSourceUrl+"?"+
					this.config.actionParamName+"=load&"+
					this.config.treeIdParamName+"=parent."+this.id+"&"+
					this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
					this.config.levelParamName+"=999&"+
					this.config.searchTypeParamName+"="+searchType+"&"+
					this.config.searchTypeParamName+"2="+searchType2+"&"+
					this.config.searchTypeParamName+"3="+searchType3+"&"+
					this.config.searchInfoTypeParamName+"="+infoType+"&"+
					this.config.searchParamName+"="+searchText+"&"+
					this.config.searchParamName+"2="+searchText2+"&"+
					this.config.searchParamName+"3="+searchText3+"&"+
                    this.config.searchSelectedParamName+"="+this.getSelectedOids()+"&"+
					"loadFromFrame=true";
	this.loadFrame(this.config.loaderFrame, scriptUrl);
	this.allLoaded = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		this.nodestates[cn].allLoaded = true;
	}
}

/* Loads all tree nodes from the backend.
 */
dcSiteMapNavTree.prototype.loadAll = function() {
	if (this.allLoaded == false) {
		this.setStatus(this.msg.loading);
		var scriptUrl = this.config.dataSourceUrl+"?"+
						this.config.actionParamName+"=loadall&"+
						this.config.treeIdParamName+"=parent."+this.id+"&"+
						this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
						this.config.levelParamName+"=999&loadFromFrame=true";
		this.loadFrame(this.config.loaderFrame, scriptUrl);
		this.allLoaded = true;
		for (var cn in this.nodes) {
            if (typeof this.nodes[cn] == 'function')
                continue;
			this.nodestates[cn].allLoaded = true;
		}

	}
}

/* Resets the tree and loads all tree nodes from the backend.
 *
 * @param level the number of tree levels to load.
 */
dcSiteMapNavTree.prototype.reload = function(level) {
	this.setLoading();
	this.setStatus(this.msg.loading);
	var scriptUrl =	this.config.dataSourceUrl+"?"+
					this.config.actionParamName+"=load&"+
					this.config.treeIdParamName+"=parent."+this.id+"&"+
					this.config.nodeIdParamName+"="+this.ROOT_HOME+"&"+
					this.config.levelParamName+"="+level+"&loadFromFrame=true";
	this.loadFrame(this.config.loaderFrame, scriptUrl);
	this.allLoaded = false;
}

/* Loads the subtree starting at a given node.
 *
 * @param nodeId the node to load the subtree for
 */
dcSiteMapNavTree.prototype.loadBranch = function(nodeId) {
	this.setStatus(this.msg.loadingNode+(this.nodes[nodeId].label ? this.nodes[nodeId].label : ""));
	var scriptUrl =	this.config.dataSourceUrl+"?"+
					this.config.actionParamName+"=load&"+
					this.config.treeIdParamName+"=parent."+this.id+"&"+
					this.config.nodeIdParamName+"="+nodeId+"&"+
					this.config.levelParamName+"=999&loadFromFrame=true";
	this.loadFrame(this.config.loaderFrame, scriptUrl);
	this.nodestates[nodeId].allLoaded = true;
	this.nodes[nodeId].expand();
	this.nodes[nodeId].reprint();
	// check if the whole tree is loaded now
	this.allLoaded = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		if (this.nodestates[cn].allLoaded == false) {
			this.allLoaded = false;
		}
	}
}

/* Checks validity of the node and update or remove the node accordingly.
 */
dcSiteMapNavTree.prototype.checkNodeValidity = function(id) {
    if (this.nodes[id]) {
		var sNode = this.nodes[id];
		if (!sNode.ts) {
			sNode.ts = (this.ts!=null) ? this.ts : 0;
		}
        var scriptUrl =	this.config.dataSourceUrl+"?"+
						this.config.actionParamName+"=check&"+
						this.config.treeIdParamName+"=parent."+this.id+"&"+
						this.config.tsParamName+"="+sNode.ts+"&"+
						this.config.nodeIdParamName+"="+id;
        this.loadFrame(this.config.loaderFrame, scriptUrl);
	}
}

/* Checks validity of the tree and reloads the tree if necessary
 */
dcSiteMapNavTree.prototype.checkValidity = function() {
    var ts = (this.ts!=null) ? this.ts : 0;
	var scriptUrl =	this.config.dataSourceUrl+"?"+
					this.config.actionParamName+"=check&"+
					this.config.treeIdParamName+"=parent."+this.id+"&"+
					this.config.tsParamName+"="+ts;
    this.loadFrame(this.config.loaderFrame, scriptUrl);
}


/* dc sitemap tree node class ##############################################
 */

function dcSiteMapNavTreeNode () {}
dcSiteMapNavTreeNode.prototype = new topicNavTreeNode();
dcSiteMapNavTreeNode.prototype.constructor = topicNavTreeNode;
dcSiteMapNavTreeNode.prototype.baseClass = topicNavTreeNode.prototype.constructor;

/* Generates the node's html elements for indentation and toggling the node state. (i.e. +/- icons and connecting lines)
 * Main change to navTreeNode#getToggler is the missing node icon
 *
 * @see navTreeNode#print
 *
 * @return the toggler
 */
dcSiteMapNavTreeNode.prototype.getToggler = function() {
	var plusIcon = (this.isLastSibling==true) ? this.tree.icon.plusBottom : this.tree.icon.plus;
	var minusIcon = (this.isLastSibling==true) ? this.tree.icon.minusBottom : this.tree.icon.minus;
    var unknownIcon = (this.isLastSibling==true) ? this.tree.icon.unknownBottom : this.tree.icon.unknown;
	var lineOnly = (this.isLastSibling==true) ? this.tree.icon.lineBottom : this.tree.icon.lineJoin;
	var outerLine = '';
	var parentId = this.parentId;
	while (parentId && this.tree.nodes[parentId].parentId) {
		if (this.tree.nodes[parentId].isLastSibling) {
			outerLine = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>'+outerLine;
		} else {
			outerLine = '<img src="'+this.tree.icon.line+'" width="18" height="18" border="0" alt=""/>'+outerLine;
		}
		parentId = this.tree.nodes[parentId].parentId;
	}
	if (!this.parentId) {
		return '<div class="treeitem"><a class="controlButton" onclick="parent.'+this.tree.id+'.collapseAll();parent.'+this.tree.id+'.deselectAll(true);"><img src="'+this.tree.icon.minusRoot+'" width="18" height="18" border="0" alt="-" /></a>';
    } else if (this.state.allLoaded == false) {
        return ''+outerLine+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+unknownIcon+'" width="18" height="18" border="0" alt="-"/><\/a>';
	} else if (this.treeChildren.length == 0 ) {
	    return ''+outerLine+'<a><img src="'+lineOnly+'" width="18" height="18" border="0" alt=""/></a>';
	} else if (this.state.allExpanded == true) {
		return ''+outerLine+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+minusIcon+'" width="18" height="18" border="0" alt="-"/><\/a>';
	} else {
    	return ''+outerLine+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+plusIcon+'" width="18" height="18" border="0" alt="+"/><\/a>';
	}
}

/* Generates the node's html label.
 * Each node is linked to the loading action of the instance it represents.
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
dcSiteMapNavTreeNode.prototype.getLabel = function() {
    var cls;
    if (this.state.selected == true) {
		cls = "sitemap-label-selected";
    } else {
 		cls = "sitemap-label";
	}
    var label = '<span class="labelcontainer" oid="'+this.id+'"><a href="instance.do?action=instance.load&id='+(this.id).substring(1)+'" class="'+cls+' controlButton" target="conframe">'+this.label+'</a></span>';
	if (!this.parentId) {
		return '<span class="labelcontainer" oid="'+this.id+'"><a href="javascript:parent.'+this.tree.id+'.collapseAll();parent.'+this.tree.id+'.deselectAll(true);" class="sitemap-rootlabel controlButton" target="conframe">'+this.label+'</a></span></div>';//'<span class="sitemap-labelcontainer sitemap-label labelcontainer">'+this.label+'</span>';//''+this.label+'</div>';
	} else {
    	return label;
	}
}

/* search tree class #########################################################
 *
 * Used in the archive area of the mediamanager.
 */

function searchNavTree () {}
searchNavTree.prototype = new navTree();
searchNavTree.prototype.constructor = navTree;
searchNavTree.prototype.baseClass = navTree.prototype.constructor;

/* Adds a node to the tree
 *
 * @see SearchNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 * @param nodeType the node's display type (current implementation uses empty or "removeable" type)
 * @param the optional query name (for nodes storing a query)
 */
searchNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, openIcon, closedIcon, nodeType, queryName, batchId) {
    if ((!parentId || this.nodes[parentId]) && !this.nodes[id]) {
    	var newNode = new searchNavTreeNode();
    	newNode.init(id,label,this, parentId,allLoaded, url, openIcon, closedIcon);
    	newNode.nodeType = nodeType;
    	newNode.queryName = queryName;
    	newNode.batchId = batchId;
	}
}

/* Selects nodes attached to a given infotype by matching all nodes that contain the "id", but that don't match the "excludeId".
 *
 * @param id the string that the node id has to contain to be selected
 * @param exludeId the string that the node id must not contain to be selected
 */
searchNavTree.prototype.searchInfoType = function(id, excludeId) {
    var matchCount = 0;
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.expanded = false;
        curState.allExpanded = false;
        curState.selected = false;
        curState.hidden = true;
    }
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
	    if (cn!=this.ROOT_HOME) {
	        var curNode = this.nodes[cn];
            if ((curNode.id && curNode.id.indexOf(id) > -1) && (!excludeId || curNode.id.indexOf(excludeId)==-1) && (curNode.nodeType=='nocardinality')){
	            matchCount++;
	            curNode.state.selected = true;
	            curNode.state.hidden = false;
	            while(curNode.parentId != null) {
	                curNode = this.nodes[curNode.parentId];
	                curNode.state.expanded = true;
	                curNode.state.hidden = false;
	            }
	        }
        }
    }
    this.nodestates[this.ROOT_HOME].hidden = false;
    this.nodes[this.ROOT_HOME].expand();
    this.print();
}

/* Loads all stored search queries into the tree.
 */
searchNavTree.prototype.loadQueries = function() {
	this.setStatus(this.msg.loading);
	var script = document.createElement("script");
	script.setAttribute("type","text/javascript");
	script.setAttribute("src",	this.config.dataSourceUrl+"?"+
								this.config.treeIdParamName+"="+this.id+"&"+
								"action=loadqueries");
	document.body.appendChild(script);
}


searchNavTree.prototype.toggleAll = function() {
	var collapsed = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		if (cn!=this.ROOT_HOME && this.nodestates[cn].expanded == true) {
			collapsed = false;
		}
	}
	if (collapsed) {
		this.expandAll();
	} else {
        this.deselectAll();
        this.collapseAll();
    }
    if (this.useCookies) {
	    this.updateCookie();
	}
}

/* search tree node class ####################################################
 */
function searchNavTreeNode () {}
searchNavTreeNode.prototype = new navTreeNode();
searchNavTreeNode.prototype.constructor = navTreeNode;
searchNavTreeNode.prototype.baseClass = navTreeNode.prototype.constructor;

/* Generates the node's html label.
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
searchNavTreeNode.prototype.getLabel = function() {
    if (this.state.selected == true) {
		cls = "label-selected";
    } else {
 		cls = "label";
	}

    // label = '<span class="labelcontainer" oid="'+this.id+'"><a class="'+cls+' controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].handleClick(event);" '+(this.url ? 'ondblclick="'+this.url+'"' : '')+'">'+this.label+'</a></span>';
    var label = '<span class="labelcontainer" oid="'+this.id+'"><a class="'+cls+' controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].handleClick(event);">'+this.label+'</a></span>';
    if (this.nodeType == "removeable") {
        var msg = this.tree.msg.loadQuery;
        var addToSearchButton = '<a '+(this.url ? 'href="'+this.url+'"' : '')+' title="' + msg + '"><img src="images/tree/right.gif" border="0" alt="' + msg + '" title="' + msg + '" align="bottom"></a>';
        var addToBatchButton = '';
    } else if (this.nodeType == "batchJob") {
        var msg = this.tree.msg.loadBatchJob;
        var addToSearchButton = '<a '+(this.url ? 'href="'+this.url+'"' : '')+' title="' + msg + '"><img src="images/tree/right.gif" border="0" alt="' + msg + '" title="' + msg + '" align="bottom"></a>';
        var addToBatchButton = '';
    } else if (this.nodeType == "batchTemplate") {
        var msg = this.tree.msg.loadBatchJobTemplate;
        var addToSearchButton = '<a '+(this.url ? 'href="'+this.url+'"' : '')+' title="' + msg + '"><img src="images/tree/right.gif" border="0" alt="' + msg + '" title="' + msg + '" align="bottom"></a>';
        var addToBatchButton = '';
    } else {
        var msg = this.tree.msg.addToSearch;
        var msg2 = this.tree.msg.addToBatch;
        var addToSearchButton = '<a class="dynamicAddToRechercheButton" '+(this.url ? 'href="'+this.url+'"' : '')+' title="' + msg + '"><img src="images/tree/add.gif" border="0" alt="' + msg + '" title="' + msg + '" align="bottom"></a>';
        var addToBatchButton = '<a class="dynamicBatchButton" '+(this.url ? 'href="'+this.url+'"' : '')+' title="' + msg + '"><img src="images/tree/add.gif" border="0" alt="' + msg2 + '" title="' + msg2 + '" align="bottom"></a>';
    }
    // var deleteButton = '<a href="javascript: deleteSearchQuery(\''+this.queryName+'\',\''+this.id+'\');" title="' + this.tree.msg.removeSearch + '"><img src="images/tree/remove.gif" border="0" alt="' + this.tree.msg.removeSearch + '" align="bottom"></a>';
    if (this.nodeType=='removeable') {
	    return "<span nodeId='" + this.id + "' searchQuery='" + this.queryName + "'>" + label+addToSearchButton+addToBatchButton + "</span>";
    } else if (this.nodeType=='batchJob') {
        return "<span nodeId='" + this.id + "' batchJobId='" + this.batchId + "'>" + label+addToSearchButton+addToBatchButton + "</span>";
    } else if (this.nodeType=='batchTemplate') {
        return "<span nodeId='" + this.id + "' batchTemplate='" + this.batchId + "'>" + label+addToSearchButton+addToBatchButton + "</span>";
    } else if (this.url && this.url.length>0) {
	    return "<span nodeId='" + this.id + "' addCommand=\"" + this.url + "\">" + label+addToSearchButton+addToBatchButton + "</span>";
    } else {
    	return label;
	}
}

/* Handles mouseclicks to imitate explorer selection behaviour.
 *
 * @param event the onclick event
 */
searchNavTreeNode.prototype.handleClick = function(event) {
    if (!event.shiftKey) {
        var selectCount=0;
        if ((!event.ctrlKey && !event.metaKey) && this.tree.lastSelected!=null) {
            var wasSelected = this.state.selected==true ? true : false;
            for (var cn in this.tree.nodes) {
                if (typeof this.tree.nodes[cn] == 'function')
                    continue;
                var curState = this.tree.nodestates[cn];
                if( curState.selected )
                    selectCount++;
                curState.selected = false;
            }
            if (wasSelected && selectCount==1) this.state.selected = true;
            this.tree.print();
        }
        this.toggleSelect();
    } else {
        // shift select
        if (this.tree.lastSelected!=null) {
            // clear all selections
            for (var cn in this.tree.nodes) {
                if (typeof this.tree.nodes[cn] == 'function')
                    continue;
                var curState = this.tree.nodestates[cn];
                curState.selected = false;
            }
            var cid = this.tree.getCommonParentId(this.tree.lastSelected,this.id);
            this.tree.nodes[cid].selectRange(this.tree.lastSelected,this.id, false);
            this.tree.print();
        } else {
            this.toggleSelect();
        }
    }
}


/* document tree class #######################################################
 *
 * Used in the instance editor of the mediamanager.
 */

function documentNavTree () {}
documentNavTree.prototype = new navTree();
documentNavTree.prototype.constructor = navTree;
documentNavTree.prototype.baseClass = navTree.prototype.constructor;

/* Inits the tree's instance variables. (should be called directly after instantiation)
 * Main changes to navTree#init are a second icon set for the extended display mode.
 *
 * @param id the unique id of the tree. (used for requests to load further subtrees and should be named exactly like the tree instance variable)
 * @param rootId the unique id of the tree's root node. (if not provided, "root" is used as default id)
 * @param rootLabel the label for the tree's root node.
 */
documentNavTree.prototype.init = function(id, rootId, rootLabel) {
	this.id = id;
	this.ROOT_HOME = rootId ? rootId : "root";
    this.frame = null;
    this.target = null;
    this.lastSelected = null;
    this.nodes = new Array();
    this.nodestates = new Array();
    this.aHTML = new Array();
    this.allLoaded = true;
	this.config = {
		dataSourceUrl	: 'SearchNavigation.do'
	}
	this.icon = {
		root			: 'images/tree/infotype.gif',
		folder			: 'images/tree/folder.gif',
		folderOpen		: 'images/tree/folder.open.gif',
		node			: 'images/tree/page.gif',
		empty			: 'images/tree/empty.gif',
		line			: 'images/tree/line.24.gif',
		lineJoin		: 'images/tree/line.join.24.gif',
		lineBottom		: 'images/tree/line.bottom.24.gif',
		plus			: 'images/tree/plus.24.gif',
		plusBottom		: 'images/tree/plus.bottom.24.gif',
		nlPlus			: 'images/tree/plus.nolines.24.gif',
		minus			: 'images/tree/minus.24.gif',
		minusBottom		: 'images/tree/minus.bottom.24.gif',
		nlMinus			: 'images/tree/minus.nolines.24.gif',
		extLine			: 'images/tree/line.',
		extLineJoin		: 'images/tree/line.join.',
		extLineBottom	: 'images/tree/line.bottom.',
		extPlus			: 'images/tree/plus.',
		extPlusBottom	: 'images/tree/plus.bottom.',
		extNlPlus		: 'images/tree/plus.nolines.',
		extMinus		: 'images/tree/minus.',
		extMinusBottom	: 'images/tree/minus.bottom.',
		extNlMinus		: 'images/tree/minus.nolines.',
		up				: 'images/tree/up.gif',
		down			: 'images/tree/down.gif',
		left			: 'images/tree/left.gif',
		right			: 'images/tree/right.gif',
        fullscreen      : 'images/tree/fullscreen.gif'
    };
	this.msg = {
		found			: ' matches found.',
		loading			: 'Loading.',
		loadingNode		: 'Loading data for node: ',
		addLabel		: 'Add ',
        resetLabel      : 'Reset',
        deleteLabel		: 'Delete ',
		noAccess		: '(No Permission)'
	}
	this.showOptional = true;
	this.nestLevel = '';
}

/* Outputs the tree html to the browser.
 *
 * @see navTree#print
 */
var autosuggests = new Array();
documentNavTree.prototype.render = function(id) {
    var treeFrame = this.getTreeFrame();
    AutoSuggest = treeFrame.AutoSuggest;
    var doc = this.getTreeDocument();
	if (id && doc.getElementById(id)) {
	    doc.getElementById(id).innerHTML = this.aHTML.join('');
    } else if (doc.getElementById(this.target)) {
        doc.getElementById(this.target).innerHTML = this.getPrefix()+this.aHTML.join('')+this.getSuffix();
    }
	this.renderHidden();
    this.nodes[this.ROOT_HOME].addHtmlEditors();
    autosuggests = new Array();
    this.nodes[this.ROOT_HOME].addAutoSuggest();
}

/* Outputs hidden fields for all input fields of the tree.
 * This is necessary as hidden input fields are not submitted by browsers.
 *
 * @see documentNavTree#render
 */
documentNavTree.prototype.renderHidden = function() {
	var hiddenFields = '';
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		var curNode = this.nodes[cn];
		if (curNode.displayType!='read' && curNode.fieldValue && curNode.fieldValue.length>0)
			hiddenFields += '<input type="hidden" name="'+this.nestLevel+curNode.fieldName+'" value="'+replaceAll(curNode.fieldValue,'"',"&quot;")+'">\n';
	}
	if (parent.frames[this.frame]) {
        if (parent.frames[this.frame].document.getElementById(this.target+'Hidden')) parent.frames[this.frame].document.getElementById(this.target+'Hidden').innerHTML = hiddenFields;
    }
    else {
        if (document.getElementById(this.target+'Hidden')) document.getElementById(this.target+'Hidden').innerHTML = hiddenFields;
    }
}


/* Adds a node to the tree.
 *
 * @see dcSiteMapNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 * @param displayType determines how a node is rendered (current implementation has "read" and "edit" options for read and edit mode)
 * @param fieldType the field data type ("string", "translation", "optional", ...)
 * @param fieldName the name attribute of the property's input tag
 * @param fieldValue the value of the property's input tag
 * @param minCardinality the minimum cardinality of the property/group
 * @param maxCardinality the maximum cardinality of the property/group
 * @param deleteChildren flag whether to delete the node's children if the node already exists
 * @param insertBelowId the id of the node after which this node should be added to the tree (used to retain ordering)
 * @param mayRead flag whether user has read access to this property/group
 * @param mayCreate flag whether user has create access to this property/group
 * @param mayDelete flag whether user has delete access to this property/group
 * @param dlFieldName the default language field name (in case this node is of fieldType "translation")
 * @param dlFieldValue the default language field value (in case this node is of fieldType "translation")
 * @param dlSelected the current default language (in case this node is of fieldType "translation")
 */
documentNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, openIcon, closedIcon, displayType, fieldType, fieldName, fieldValue, minCardinality, maxCardinality, deleteChildren, insertBelowId, mayRead, mayCreate, mayDelete, dlFieldName, dlFieldValue, dlSelected, useHtmlEditor, multiline, maxlength) {
    if (!parentId || this.nodes[parentId]) {
        if (!this.nodes[id]) {
            var newNode = new documentNavTreeNode();
            newNode.init(id,label,this,parentId,allLoaded, url, openIcon, closedIcon, insertBelowId);
            newNode.extended = false;
        } else {
            var newNode = this.nodes[id];
            newNode.label = label;
            newNode.url = url;
            newNode.openIcon = openIcon;
            newNode.closedIcon = closedIcon;
            this.nodestates[id].allLoaded = allLoaded;
        }
        newNode.displayType = displayType;
        newNode.fieldType = fieldType;
        newNode.fieldName = fieldName;
        newNode.fieldValue = fieldValue;
        newNode.minCardinality = (minCardinality) ? minCardinality : 1;
        newNode.maxCardinality = (maxCardinality) ? maxCardinality : 1;
        newNode.mayRead = mayRead;
        newNode.mayCreate = mayCreate;
        newNode.mayDelete = mayDelete;
        newNode.dlFieldName = dlFieldName;
        newNode.dlFieldValue = dlFieldValue;
        newNode.dlSelected = (dlSelected==true) ? true : false;
        newNode.useHtmlEditor = useHtmlEditor;
        newNode.multiline = multiline;
        newNode.maxlength = maxlength;
        if ((fieldType == "string" || fieldType == "translation") && multiline && fieldValue != null && fieldValue.length > 0) {
            // automatically extend non-empty text nodes
            newNode.extended = true;
        }
        if (deleteChildren==true) this.deleteNodeChildren(newNode.id);
    }
}

/* Expands and selects all nodes matching a search text.
 *
 * @param searchText the text to match the search against
 */
documentNavTree.prototype.search = function(searchText) {
	if (this.validate(searchText)==false) {
		alert(this.msg.invalidInput);
		return;
	}
    var matchCount = 0;
    searchText = searchText.toLowerCase();
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.expanded = false;
        curState.allExpanded = false;
        curState.selected = false;
        curState.hidden = true;
    }
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curNode = this.nodes[cn];
        if (cn!=this.ROOT_HOME && curNode.label) {
            var sLabel = curNode.label.toLowerCase();
            var sFieldValue = (curNode.fieldValue) ? curNode.fieldValue.toLowerCase() : '';
            if ( (sLabel.indexOf(searchText) > -1) || (sFieldValue.indexOf(searchText) > -1) ) {
                matchCount++;
                curNode.state.selected = true;
                curNode.state.hidden = false;
                while(curNode.parentId != null) {
                    curNode = this.nodes[curNode.parentId];
                    curNode.state.expanded = true;
                    curNode.state.hidden = false;
                }
            }
        }
    }
    this.nodes[this.ROOT_HOME].label = "<span class=\"nfo\">"+matchCount+" "+this.msg.found+" '"+searchText+"'.</span>";
    this.nodestates[this.ROOT_HOME].hidden = false;
    this.print();
    this.setStatus(matchCount+" "+this.msg.found+"'"+searchText+"'.");
}

/* Toggles whether nodes of fieldType "optional" should be displayed.
 */
documentNavTree.prototype.toggleOptional = function() {
    if (this.showOptional == false) {
    	this.showOptional = true;
	} else {
		this.showOptional = false;
	}
    this.nodestates[this.ROOT_HOME].hidden = false;
    this.print();
}

/* Expands all nodes of the tree
 */
documentNavTree.prototype.expandAll = function() {
	this.loadAll();
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
	    var curState = this.nodestates[cn];
        curState.expanded = true;
    	curState.allExpanded = true;
    	curState.hidden = false;
	}
	this.print();
}

/* If all tree nodes are collapsed, all nodes are expanded, otherwise all nodes are collapsed.
 */
documentNavTree.prototype.toggleAll = function() {
	var collapsed = true;
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		if (cn!=this.ROOT_HOME && this.nodestates[cn].expanded == true) {
			collapsed = false;
            break;
        }
	}
	if (collapsed) {
        this.expandAll();
	} else {
        this.deselectAll(false);
        this.collapseAll();
        /*for (var cn in this.nodes) {
            var curState = this.nodestates[cn];
            curState.selected = false;
            curState.expanded = false;
	        curState.allExpanded = false;
	        curState.hidden = false;
        }
        this.nodestates[this.ROOT_HOME].expanded = true;*/
    }
    if (this.useCookies) {
	    this.updateCookie();
	}
}

/* Sets the nesting level for the form input elements.
 */
documentNavTree.prototype.setNestLevel = function(value) {
    var newLevel = this.nestLevel != value;
	this.nestLevel = value;
	/*if (this.nodes && this.ROOT_HOME && this.nodes[this.ROOT_HOME]) {
	    this.nodes[this.ROOT_HOME].hidden = false;
	    this.aHTML = new Array();
	    this.nodes[this.ROOT_HOME].print();
	    if (newLevel) this.render();
	}*/
}

/* document tree node class ##################################################
 */

function documentNavTreeNode () {}
documentNavTreeNode.prototype = new navTreeNode();
documentNavTreeNode.prototype.constructor = navTreeNode;
documentNavTreeNode.prototype.baseClass = navTreeNode.prototype.constructor;
documentNavTreeNode.superclass = navTreeNode.prototype;

/* Inits the tree node's instance variables. (should be called directly after instantiation, i.e. in the tree's addNode method.)
 * @see documentNavTree#addNode
 *
 * @param id the unique id of the node.
 * @param label the node's text label
 * @param currentTree the tree the node is being added to
 * @param parentId this node's parent id in the tree (the parent node must already exist in the tree)
 * @param allLoaded flag whether this node is already fully loaded
 * @param url the url the node links to
 * @param openIcon the icon displayed when the node is open
 * @param openIcon the icon displayed when the node is closed
 * @param insertBelowId the id of the node after which this node should be added to the tree (used to retain ordering)
 */
documentNavTreeNode.prototype.init = function(id, label, currentTree, parentId, allLoaded, url, openIcon, closedIcon, insertBelowId) {
    this.tree = currentTree;
    this.id = id;
    this.label = label;
    this.url = url;
    this.openIcon = openIcon;
    this.closedIcon = closedIcon;
    this.treeChildren = new Array();
    this.isLastSibling = false;
    this.htmlEditor = false;
    this.multiline = false;
    this.maxlength = -1;
    currentTree.nodes[currentTree.length]= id;
    currentTree.nodestates[currentTree.length]= id;
    currentTree.length++;
    this.parentId = parentId;
    var thisPar = this.tree.nodes[parentId];

	if (parentId) {
		var found = false;
		var tmp = null;

		var oldLength = thisPar.treeChildren.length;
		for (i=0;i<oldLength+1;i++) {
			if (thisPar.treeChildren[i-1] && thisPar.treeChildren[i-1]==insertBelowId) {
				tmp = thisPar.treeChildren[i];
				thisPar.treeChildren[i] = id;
				found = true;
			} else
			if (found) {
				var tmp2 = thisPar.treeChildren[i];
				thisPar.treeChildren[i] = tmp;
				tmp = tmp2;
			}
		}
		if (!found) thisPar.treeChildren[thisPar.treeChildren.length] = id;

    }
    this.tree.nodes[id] = this;

    this.tree.addNodeState(id, allLoaded, false, false, false, false);
}

/* Generates the node's full html output and recursively calls itself for all subnodes.
 *
 * @see navTree#print
 */
documentNavTreeNode.prototype.print = function() {
	if (this.fieldType=='optional' && this.tree.showOptional==false) return;
	var aH = this.tree.aHTML;
	if (this.state.hidden == false) {
	    var treec = this.treeChildren;
	    // check if all children of the node are visible (required to display +/- icons)
	    if (this.state.expanded == true && treec.length > 0) {
		    this.state.allExpanded = true;
	        for (var i = 0, len = treec.length; i < len; i++) {
	        	if (this.tree.nodestates[treec[i]].hidden == true) this.state.allExpanded = false;
            }
	    }
	    // leaf nodes are always fully loaded
	    if (treec.length==0) {
		    this.state.allLoaded = true;
	    }
	    aH.push(this.getPrefix());
	    aH.push(this.getToggler());
	    aH.push(this.getLabel());
	    if (this.state.expanded == true && treec.length > 0) {
		    var lastVisible = treec.length-1;
			if (this.state.allExpanded == false) {
				for (var i = treec.length-1; i > -1; i--) {
					if (this.tree.nodestates[treec[i]].hidden == false) {
						lastVisible = i;
						break;
					}
				}
			}
	        for (var i = 0; i < lastVisible; i++) {
	        	this.tree.nodes[treec[i]].isLastSibling = false;
	        	this.tree.nodes[treec[i]].print();
            }
        	this.tree.nodes[treec[lastVisible]].isLastSibling = true;
        	this.tree.nodes[treec[lastVisible]].print();
	    }
	    aH.push(this.getSuffix());
	}
}

/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 *
 * @return the prefix
 */
documentNavTreeNode.prototype.getPrefix = function() {
	return '<div id="'+this.id+'">';
}

/* Generates the node's html elements for indentation and toggling the node state. (i.e. +/- icons and connecting lines)
 *
 * @see navTreeNode#print
 *
 * @return the toggler
 */
documentNavTreeNode.prototype.getToggler = function() {
	var outerLine = '';
	var parentId = this.parentId;
    var normalHeight = 24;
    var extendedHeight = (this.displayType == "edit" && this.useHtmlEditor) ? 200 : 84;
    // create hierarchy lines
    while (parentId) {
        var icon = null;
        var height = normalHeight;
        if (this.tree.nodes[parentId].isLastSibling) {
            icon = this.tree.icon.empty;
        } else {
            icon = this.extended ? this.tree.icon.extLine+extendedHeight+'.gif' : this.tree.icon.line;
            height = this.extended ? extendedHeight : normalHeight;
        }
        outerLine = '<img src="'+icon+'" width="18" height="' + height + '" border="0" alt=""/>'+outerLine;
		parentId = this.tree.nodes[parentId].parentId;
	}
	var tableStart = '<table width="100%" cellpadding="0" cellspacing="0"><tr><td valign="top" width="250"><div class="treeitem">'+((this.parentId) ? outerLine : '');
	var labelIcon = '<img src="'+( this.parentId ? (this.openIcon ? this.openIcon : this.tree.icon.node) : this.tree.icon.root )+'" width="18" height="18" border="0" alt="" align="top">';
    var height = this.extended ? extendedHeight : normalHeight;
    if (!this.parentId) {
		if (this.state.allExpanded == true) {
			return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.nlMinus+'" width="18" height="'+height+'" border="0" alt="-"/>'+labelIcon+'</a>';
		} else {
			return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.nlPlus+'" width="18" height="'+height+'" border="0" alt="-"/>'+labelIcon+'</a>';
		}
	} else if (this.treeChildren.length == 0 ) {
		if (this.isLastSibling==true) {
			if (this.extended==false) {
				return tableStart+'<img src="'+this.tree.icon.lineBottom+'" width="18" height="'+height+'" border="0" alt=""/>'+labelIcon;
			} else {
				return tableStart+'<img src="'+this.tree.icon.extLineBottom+extendedHeight+'.gif" width="18" height="'+height+'" border="0" alt=""/>'+labelIcon;
			}
		} else {
			if (this.extended==false) {
				return tableStart+'<img src="'+this.tree.icon.lineJoin+'" width="18" height="'+height+'" border="0" alt=""/>'+labelIcon;
			} else {
				return tableStart+'<img src="'+this.tree.icon.extLineJoin+extendedHeight+'.gif" width="18" height="'+height+'" border="0" alt=""/>'+labelIcon;
			}
		}
	} else if (this.state.allExpanded == true) {
		if (this.isLastSibling==true) {
			if (this.extended==false) {
				return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.minusBottom+'" width="18" height="'+height+'" border="0" alt="-"/>'+labelIcon+'</a>';
			} else {
				return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.extMinusBottom+extendedHeight+'.gif" width="18" height="'+height+'" border="0" alt="-"/>'+labelIcon+'</a>';
			}
		} else {
			if (this.extended==false) {
				return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.minus+'" width="18" height="'+height+'" border="0" alt="-"/>'+labelIcon+'</a>';
			} else {
				return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.extMinus+extendedHeight+'.gif" width="18" height="'+height+'" border="0" alt="-"/>'+labelIcon+'</a>';
			}
		}
	} else {
		if (this.isLastSibling==true) {
			if (this.extended==false) {
		    	return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.plusBottom+'" width="18" height="'+height+'" border="0" alt="+"/>'+labelIcon+'</a>';
			} else {
		    	return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.extPlusBottom+extendedHeight+'.gif" width="18" height="'+height+'" border="0" alt="+"/>'+labelIcon+'</a>';
	    	}
		} else {
			if (this.extended==false) {
		    	return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.plus+'" width="18" height="'+height+'" border="0" alt="+"/>'+labelIcon+'</a>';
			} else {
		    	return tableStart+'<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggle();"><img src="'+this.tree.icon.extPlus+extendedHeight+'.gif" width="18" height="'+height+'" border="0" alt="+"/>'+labelIcon+'</a>';
	    	}
		}
	}
}

/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 * @see navTreeNode#getPrefix
 *
 * @return the suffix
 */
documentNavTreeNode.prototype.getSuffix = function() {
	return "</div>";
}

/* Toggles whether the node is displayed in extended mode.
 */
documentNavTreeNode.prototype.toggleExtent = function() {
	if (this.extended==false) {
		this.extended = true;
	} else {
		this.extended = false;
        this.removeHtmlEditor();
    }
	this.reprint();
}

documentNavTreeNode.prototype.addHtmlEditors = function() {
    // add HTML editors for child nodes
    for (var cn in this.treeChildren) {
        if (typeof this.treeChildren[cn] == 'function')
            continue;
        if (this.tree.nodestates[this.id].expanded && this.tree.nodes[this.treeChildren[cn]].addHtmlEditors) {
            this.tree.nodes[this.treeChildren[cn]].addHtmlEditors();
        }
    }
    if ((this.displayType && this.displayType == "edit") && this.extended && this.useHtmlEditor && !this.htmlEditor) {
        this.attachHtmlEditors();
    } else if (!this.useHtmlEditor) {
        this.htmlEditor = false;
    }
}

   documentNavTreeNode.prototype.addAutoSuggest = function() {
   for (var cn in this.treeChildren) {
       if (typeof this.treeChildren[cn] == 'function')
           continue;
        if (this.tree.nodestates[this.id].expanded) {
            this.tree.nodes[this.treeChildren[cn]].addAutoSuggest();
        }
    }
    if(!this.fieldName) return;
    for (i=0;i<autosuggests.length;i++) {
        if (autosuggests[i].fld == this.tree.nestLevel + this.fieldName) return;
        if (autosuggests[i].fld == this.tree.nestLevel + this.fieldName + ".display") return;
    }
    if (this.fieldType && this.fieldType=="Thesaurus" && this.displayType == "edit") {
                    var opt = new Object();
                    opt.timeout = 5000;
                    opt.minchars = 2;
                    opt.shownoresults = false;
                    opt.script = "admin/thesaurus.do?";
                    opt.varname = "action=autoSuggest&phrase";
                    opt.json =  true;
                    opt.meth = "POST";
                    autosuggests.push(new AutoSuggest(this.tree.nestLevel +  this.fieldName,opt, false));
   } else if (this.fieldType && this.fieldType=="Distinct" && this.displayType == "edit") {
                    var opt = new Object();
                    opt.timeout = 5000;
                    opt.minchars = 2;
                    opt.shownoresults = false;
                    opt.script = "autoComplete.jsp?property="+this.id.replace(this.parentId,"")+"&";
                    //if(languages != null && languages[0][1] != null)
                    //    opt.script += "lang="+languages[0][1]+"&";
                    opt.varname = "input";
                    opt.json =  true;
                    opt.meth = "POST";
                    autosuggests.push(new AutoSuggest(this.tree.nestLevel + this.fieldName,opt, false));
  } else if (this.fieldType && this.fieldType=="infotypeLink" && this.displayType == "edit") {
                    var opt = new Object();
                    opt.timeout = 5000;
                    opt.minchars = 2;
                    opt.shownoresults = false;
                    opt.script = "infotypeLink.jsp?property="+this.id.replace(this.parentId,"")+"&";
                    opt.varname = "input";
                    opt.json =  true;
                    opt.callback = function (node) {parent.frames["conframe"].document.getElementById(this.callbacktarget).value = node.id;};
                    opt.callbacktarget = this.tree.nestLevel +  this.fieldName;
                    opt.meth = "POST";
                    opt.id = this.id;
                    autosuggests.push(new AutoSuggest(this.tree.nestLevel +  this.fieldName+".display",opt, false));
  }
}

// overridden in translation tree
documentNavTreeNode.prototype.attachHtmlEditors = function() {
    this.htmlEditor = this.attachEditorComponent(this.tree.nestLevel + this.fieldName);
}

documentNavTreeNode.prototype.attachEditorComponent = function(formName) {
    // add HTML editor for this node
    var treeFrame = this.tree.getTreeFrame();
    var doc = this.tree.getTreeDocument();
    var element = doc.getElementById(formName);
    if (element) {
        element.style.height = "200px";
        var mid = ""+formName;
        treeFrame.tinyMCE.execCommand("mceAddControl", true, mid);
        return true;
    } else {
        return false;
    }
}

documentNavTreeNode.prototype.removeHtmlEditor = function() {
    if (this.htmlEditor) {
        // get value from inline HTML editor
        var treeFrame = this.tree.getTreeFrame();
        treeFrame.tinyMCE.triggerSave(true, true);
        this.fieldValue = this.detachHtmlEditor(this.tree.nestLevel + this.fieldName);
        this.htmlEditor = false;
    }
}

documentNavTreeNode.prototype.detachHtmlEditor = function(formName) {
    var treeFrame = this.tree.getTreeFrame();
    var doc = this.tree.getTreeDocument();
    var value = doc.getElementById(formName).value;
    // remove editor
    treeFrame.tinyMCE.execCommand("mceRemoveControl", false, formName);
    return value;
}

documentNavTreeNode.prototype.removeAllHtmlEditors = function() {
    if (this.treeChildren.length > 0) {
        for (var i in this.treeChildren) {
            if (typeof this.treeChildren[i] == 'function')
                continue;
            this.tree.nodes[this.treeChildren[i]].removeAllHtmlEditors();
        }
    }
    this.removeHtmlEditor();
}

documentNavTreeNode.prototype.collapse = function() {
    this.removeAllHtmlEditors();
    documentNavTreeNode.superclass.collapse.call(this);
}

/* Generates the node's html label.
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
documentNavTreeNode.prototype.getLabel = function() {
    if (this.state.selected == true) {
		cls = "label-selected";
    } else if (this.fieldType=='optional') {
	    cls = "label-optional";
    } else {
 		cls = "label";
	}
	if (!this.parentId) {
    	return 	'<span class="labelcontainer" style="vertical-align:top;"><a class="'+cls+'" '+((this.url && this.url.length>0) ? 'href="'+this.url+'"' : '')+' >'+this.label+'<\/a></span></div></td>'+
    			'<td></td><td width="5"><img src="'+this.tree.icon.empty+'" width="5" border="0"/></td><td></td></tr></table>';
	} else {
		var valueField = '<div style="width:100%">&nbsp;</div>'; //workaround for mozilla to stretch the table cell to max
		var extender = '';
		if (this.mayRead==false) {
			valueField = '<div class="readonlyfield"><i>'+this.tree.msg.noAccess+'</i></div>';
		} else if (this.displayType=='read') {
			if (this.fieldType=='string' || this.fieldType=='translation'  || this.fieldType=='Thesaurus' || this.fieldType=='Distinct'  || this.fieldType=='infotypeLink') {
                if (this.multiline) {
                    var fullscreen = ' onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].showFullscreen();" ';
                    if (this.extended) 	valueField = '<div class="readonlyfieldextended linkfield"'+fullscreen+'>'+this.fieldValue+'</div>';
                    else	 			valueField = '<div class="readonlyfield linkfield"'+fullscreen+'>'+this.fieldValue+'</div>';
                    extender = '<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggleExtent();"><img src="'+((this.extended==true) ? this.tree.icon.up : this.tree.icon.down )+'" width="18" height="18" border="0" alt=""/></a>';
                } else {
                    valueField = '<div class="readonlyfield">'+this.fieldValue+'</div>';
                    extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
                }
            } else if (this.fieldType=='group') {
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='multilanguage') {
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='complex') {
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='date') {
				var date = formatDate(this.fieldValue);
				valueField = '<div class="readonlyfield">'+date+'</div>';
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='language') {
				var langCode = this.fieldValue;
				var licLang = languages;
				for (var langId in licLang) {
                    if (typeof licLang[langId] == 'function')
                        continue;
					if (langCode==licLang[langId][0]) valueField = '<div class="readonlyfield"><img src="images/flags/'+licLang[langId][1]+'.gif" align="absmiddle" border="0" hspace="1" alt="'+licLang[langId][2]+'"/>'+licLang[langId][2]+'</div>';
				}
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='selectlist') {
				var listId = this.fieldValue.substring(0,this.fieldValue.indexOf(':'));
				var elementId = this.fieldValue.substring(this.fieldValue.indexOf(':')+1,this.fieldValue.length+1);
				if (this.tree.selectLists != null && this.tree.selectLists[listId] != null) {
    				var list = this.tree.selectLists[listId];
                    var i;
                    valueField = '';
                    for (i = 0; i < list.length; i++)
                        if (list[i][0] == elementId) {
                            valueField = '<div class="readonlyfield">'+list[i][1]+'</div>';
                            break;
                        }
                } else {
                    valueField = '(unknown selectlist)';
                }
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='boolean') {
				valueField = '<div class="readonlyfield"><input type="checkbox" DISABLED '+(this.fieldValue=='T' ? 'CHECKED' : '')+'></div>';
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='optional') {
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			}
		} else if (this.displayType=='edit') {
			if (this.fieldType=='string' || this.fieldType=='Thesaurus' || this.fieldType=='Distinct') {
                if (this.multiline) {
                    if (this.extended)	valueField = '<textarea onkeypress="checkInputValue(event,this);" class="multilineeditfieldextended" id="'+this.tree.nestLevel+this.fieldName+'" name="'+this.tree.nestLevel+this.fieldName+'" rows="5" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].fieldValue=document.getElementById(\''+this.tree.nestLevel+this.fieldName+'\').value;parent.'+this.tree.id+'.renderHidden();">'+this.fieldValue+'</textarea>';
                    else				valueField = '<textarea onkeypress="checkInputValue(event,this);" class="multilineeditfield" id="'+this.tree.nestLevel+this.fieldName+'" name="'+this.tree.nestLevel+this.fieldName+'" rows="1" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].fieldValue=document.getElementById(\''+this.tree.nestLevel+this.fieldName+'\').value;parent.'+this.tree.id+'.renderHidden();">'+this.fieldValue+'</textarea>';
                    extender = '<a style=\"margin-left:2px\" class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggleExtent();"><img src="'+((this.extended==true) ? this.tree.icon.up : this.tree.icon.down )+'" width="18" height="18" border="0" alt=""/></a>';
                    if (!this.useHtmlEditor) {
                        valueField = '<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td width=\"99%\">'+valueField+'</td><td><a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].showFullscreen();"><img src="'+this.tree.icon.fullscreen+'" width="18" height="18" border="0" alt=""/></a></td></tr></table>';
                    }
                } else {
                    var maxlength = this.maxlength > 0 ? " maxlength=\"" + this.maxlength + "\" " : "";
                    valueField = '<input type="text" '+maxlength+'class="editfield" onkeypress="checkInputValue(event,this);" id="'+this.tree.nestLevel+this.fieldName+'" name="'+this.tree.nestLevel+this.fieldName+'"onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].fieldValue=document.getElementById(\''+this.tree.nestLevel+this.fieldName+'\').value;parent.'+this.tree.id+'.renderHidden();" value="'+this.fieldValue+'">';
                    extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
                }
            } else if (this.fieldType=='infotypeLink') {
                    var maxlength = this.maxlength > 0 ? " maxlength=\"" + this.maxlength + "\" " : "";
                    valueField = '<input type="hidden" id="'+this.tree.nestLevel+this.fieldName+'" name="'+this.tree.nestLevel+this.fieldName+'" value="'+this.fieldValue+'"><input type="text" class="editfield" id="'+this.tree.nestLevel+this.fieldName+'.display" name="'+this.tree.nestLevel+this.fieldName+'.display" value="'+this.fieldValue+'">';
                    extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
            } else if (this.fieldType=='translation') {
				//var defLang = '<input type="radio" name="'+this.tree.nestLevel+this.dlFieldName+'" value="'+this.dlFieldValue+'" '+((this.dlSelected==true) ? "CHECKED" : "")+'>';
				var defLang = '';
                if (this.multiline) {
                    if (this.extended)	valueField = '<textarea onkeypress="checkInputValue(event,this);" class="multilineeditfieldextended" id="'+this.tree.nestLevel+this.fieldName+'" name="'+this.tree.nestLevel+this.fieldName+'" rows="5" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].fieldValue=document.getElementById(\''+this.tree.nestLevel+this.fieldName+'\').value;parent.'+this.tree.id+'.renderHidden();">'+this.fieldValue+'</textarea>';
                    else				valueField = '<textarea onkeypress="checkInputValue(event,this);" class="multilineeditfield" id="'+this.tree.nestLevel+this.fieldName+'" name="'+this.tree.nestLevel+this.fieldName+'" rows="1" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].fieldValue=document.getElementById(\''+this.tree.nestLevel+this.fieldName+'\').value;parent.'+this.tree.id+'.renderHidden();">'+this.fieldValue+'</textarea>';
                    extender = '<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggleExtent();"><img src="'+((this.extended==true) ? this.tree.icon.up : this.tree.icon.down )+'" width="18" height="18" border="0" alt=""/></a>';
                } else {
                    var maxlength = this.maxlength > 0 ? " maxlength=\"" + this.maxlength + "\" " : "";
                    valueField = '<input type="text" '+maxlength+'onkeypress="checkInputValue(event,this);" class="editfield" id="'+this.tree.nestLevel+this.fieldName+'" name="'+this.tree.nestLevel+this.fieldName+'" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].fieldValue=document.getElementById(\''+this.tree.nestLevel+this.fieldName+'\').value;parent.'+this.tree.id+'.renderHidden();" value="'+this.fieldValue+'">';
                    extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
                }
            } else if (this.fieldType=='group') {
				valueField = '<div class="readonlyfield"></div>';
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='multilanguage') {
				valueField = '<div class="readonlyfield"></div>';
				if (this.state.allExpanded) valueField = '<div class="readonlyfield">'+this.fieldValue+'</div>';
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='complex') {
				valueField = '<div class="readonlyfield"></div>';
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='date') {
				valueField = createDateInput(this.tree.id, this.id, this.fieldName, this.fieldValue);
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='language') {
				var langCode = this.fieldValue;
				var licLang = languages;
				valueField  = '<select class="editfield" id="'+this.tree.nestLevel+this.fieldName+'" name="'+this.tree.nestLevel+this.fieldName+'"  onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].fieldValue=document.getElementById(\''+this.tree.nestLevel+this.fieldName+'\').value;parent.'+this.tree.id+'.renderHidden();">';
				for (var langId in licLang) {
                    if (typeof licLang[langId] == 'function')
                        continue;
					if (langCode==licLang[langId][0]) valueField += '<option value="'+licLang[langId][0]+'" selected>'+licLang[langId][2]+'</option>';
					else valueField += '<option value="'+licLang[langId][0]+'">'+licLang[langId][2]+'</option>';
				}
				valueField += '</select>';
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='selectlist') {
				var sep = this.fieldValue.indexOf(":");
				valueField = createSelectListGroup(this.tree, this.id, this.fieldName, this.fieldValue.substring(0, sep), this.fieldValue.substring(sep+1, this.fieldValue.length), this.minCardinality == 0);
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='boolean') {
				valueField = '<input type="checkbox" value="T" name="'+this.tree.nestLevel+this.fieldName+'box" id="'+this.tree.nestLevel+this.fieldName+'box" onChange="changeHotfolder(instanceForm[\''+this.tree.nestLevel+this.fieldName+'box\']);" '+(this.fieldValue=='T' ? 'CHECKED' : '')+'><input type="hidden" name="'+this.tree.nestLevel+this.fieldName+'" id="'+this.tree.nestLevel+this.fieldName+'" value="'+this.fieldValue+'">';
				extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
			} else if (this.fieldType=='optional') {
				if (this.mayCreate==false) {
					//extender = '<img src="images/buttons/button.noaccess.gif" width="18" height="18" border="0" alt="'+this.tree.msg.noAccess+'('+this.tree.msg.addLabel+this.label+')">';
					extender = '';
				} else {
					extender = '<a title="'+this.tree.msg.addLabel+this.label+'" class="controlButton" onclick="callTreeNodeAction(\'instance.createnode\',\'document\',\''+this.id+'\');"><img src="images/buttons/icon.add.gif" class="treeb" alt="'+this.tree.msg.addLabel+this.label+'"></a>';
				}
			}
		}
		var remover = '';
		if (this.displayType=='edit' && (this.minCardinality==0 || this.getCardinalityCount()>this.minCardinality)) {
			if (this.mayDelete==false) {
				//remover = '<img src="images/buttons/button.noaccess.gif" width="18" height="18" border="0" alt="'+this.tree.msg.noAccess+'('+this.tree.msg.deleteLabel+this.label+')" hspace="2">';
			} else if (this.fieldType!='translation' ) {
				if (this.maxCardinality == 1) {
                    remover = '<a title="'+this.tree.msg.resetLabel+" "+this.label+'" class="controlButton" onclick="javascript: if (confirm(\''+this.tree.msg.confirmDelete+this.label+'\')) callTreeNodeAction(\'instance.deletenode\',\'document\',\''+this.id+'\');"><img src="images/buttons/icon.rubber.gif" class="treeb" alt="'+this.tree.msg.deleteLabel+this.label+'" hspace="2"></a>';
                }
                else {
                    remover = '<a title="'+this.tree.msg.deleteLabel+" "+this.label+'" class="controlButton" onclick="javascript: if (confirm(\''+this.tree.msg.confirmDelete+this.label+'\')) callTreeNodeAction(\'instance.deletenode\',\'document\',\''+this.id+'\');"><img src="images/buttons/icon.delete.gif" class="treeb" alt="'+this.tree.msg.deleteLabel+this.label+'" hspace="2"></a>';
                }
            }
		}
        valueField = valueField.replace(/&quot;/g, '"');
        var html =
			 	'<span class="labelcontainer" style="vertical-align:top;"><a class="'+cls+'" '+((this.url && this.url.length>0) ? 'href="'+this.url+'"' : '')+' >'+((this.dlSelected==true) ? "<u>":"")+this.label+((this.dlSelected==true) ? "</u>":"")+'</a></span></div></td>'+
				'<td width="10"><img src="'+this.tree.icon.empty+'" width="10" height="18" border="0" alt=""/></td>'+
				'<td width="20" valign="top">'+extender+'</td>'+
				((defLang) ?
					'<td width="25" valign="middle">'+defLang+'</td>'
				:
					'')+
				'<td width="*" valign="middle">'+valueField+'</td>'+
				((remover.length>0 && this.fieldType!='optional') ?
					'<td width="25" valign="middle">'+remover+'</td>'
				:
					'')+
				'</tr></table>';
		return html;
	}
}

/* Checks what the current cardinality count of this node's property/group is.
 *
 * @see documentNavTreeNode#getLabel
 *
 * @return the current cardinality
 */
documentNavTreeNode.prototype.getCardinalityCount = function() {
	var id = this.id;
	if (id.lastIndexOf(']')==id.length-1) id = id.substring(0,id.lastIndexOf(']'));
	var tree = this.tree;
	var parentNode = tree.nodes[this.parentId];
	var currentCount = 0;
	if (this.parentId) {
		for (var cid in parentNode.treeChildren) {
            if (typeof parentNode.treeChildren[cid] == 'function')
                continue;
			var cn = tree.nodes[parentNode.treeChildren[cid]];
			if ( ((cn.id.lastIndexOf(']')==cn.id.length-1) && (cn.id.substring(0,cn.id.lastIndexOf('[')==id))) || (cn.id == id) ) {
				currentCount++
			}
		}
	}
	return currentCount;
}

/* Opens a popup window containing only the current node value */
documentNavTreeNode.prototype.showFullscreen = function() {
    if (this.displayType == "edit") {

    } else if (this.displayType == "read") {

    }
    top.top_showFullscreenInfo("?tree="+this.tree.id+"&node="+this.id);
}

/* translation tree class ####################################################
 *
 * Used in the instance editor of the mediamanager.
 */

function translationNavTree () {}
translationNavTree.prototype = new documentNavTree();
translationNavTree.prototype.constructor = documentNavTree;
translationNavTree.prototype.baseClass = documentNavTree.prototype.constructor;

/* Outputs hidden fields for all input fields of the tree.
 * This is necessary as hidden input fields are not submitted by browsers.
 *
 * @see documentNavTree#render
 */
translationNavTree.prototype.renderHidden = function() {
	var hiddenFields = '';
	for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
		var curNode = this.nodes[cn];
		if (curNode.displayType!='read' && curNode.trans1 && curNode.trans1.length>0)
			hiddenFields += '<input type="hidden" name="'+this.nestLevel+curNode.fieldName1+'" value="'+replaceAll(curNode.trans1,'"',"&quot;")+'">\n';
		if (curNode.displayType!='read' && curNode.trans2 && curNode.trans2.length>0)
			hiddenFields += '<input type="hidden" name="'+this.nestLevel+curNode.fieldName2+'" value="'+replaceAll(curNode.trans2,'"',"&quot;")+'">\n';
	}
	if (parent.frames[this.frame].document.getElementById(this.target+'Hidden')) parent.frames[this.frame].document.getElementById(this.target+'Hidden').innerHTML = hiddenFields;
}

/* Adds a node to the tree.
 *
 * @see translationNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 * @param displayType determines how a node is rendered (current implementation has "read" and "edit" options for read and edit mode)
 * @param fieldName1 the name attribute of the property's input tag in the first translation column
 * @param fieldName2 the name attribute of the property's input tag in the second translation column
 * @param trans1 the value of the property's input tag in the first translation column
 * @param trans2 the value of the property's input tag in the second translation column
 * @param mayRead flag whether user has read access to this property/group
 */
translationNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, openIcon, closedIcon, displayType, fieldName1, fieldName2, trans1, trans2, mayRead, useHtmlEditor, multiline, maxlength) {
    if (!parentId || this.nodes[parentId]) {
        if (!this.nodes[id]) {
            var newNode = new translationNavTreeNode();
            newNode.init(id,label,this,parentId,allLoaded, url, openIcon, closedIcon);
            newNode.extended = false;
        } else {
            var newNode = this.nodes[id];
            this.nodestates[id].allLoaded = allLoaded;
        }
        newNode.displayType = displayType;
        newNode.fieldName1 = fieldName1;
        newNode.fieldName2 = fieldName2;
        newNode.trans1 = trans1;
        newNode.trans2 = trans2;
        newNode.mayRead = mayRead;
        newNode.useHtmlEditor = useHtmlEditor;
        newNode.multiline = multiline;
        newNode.maxlength = maxlength;
        if (((trans1 != null && trans1.length > 0) || (trans2 != null && trans2.length > 0)) && multiline) {
            // automatically extend non-empty text nodes
            newNode.extended = true;
        }
    }
}

/* Expands and selects all nodes matching a search text.
 *
 * @param searchText the text to match the search against
 */
translationNavTree.prototype.search = function(searchText) {
	if (this.validate(searchText)==false) {
		alert(this.msg.invalidInput);
		return;
	}
    var matchCount = 0;
    searchText = searchText.toLowerCase();
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curState = this.nodestates[cn];
        curState.expanded = false;
        curState.allExpanded = false;
        curState.selected = false;
        curState.hidden = true;
    }
    for (var cn in this.nodes) {
        if (typeof this.nodes[cn] == 'function')
            continue;
        var curNode = this.nodes[cn];
        if (cn!=this.ROOT_HOME && curNode.label) {
            var sLabel = curNode.label.toLowerCase();
            var sTrans1 = (curNode.trans1) ? curNode.trans1.toLowerCase() : '';
            var sTrans2 = (curNode.trans2) ? curNode.trans2.toLowerCase() : '';
            if ( (sLabel.indexOf(searchText) > -1) || (sTrans1.indexOf(searchText) > -1) || (sTrans2.indexOf(searchText) > -1) ) {
                matchCount++;
                curNode.state.selected = true;
                curNode.state.hidden = false;
                while(curNode.parentId != null) {
                    //this.appendStatus(".");
                    curNode = this.nodes[curNode.parentId];
                    curNode.state.expanded = true;
                    curNode.state.hidden = false;
                }
            }
        }
    }
    this.nodes[this.ROOT_HOME].label = "<span class=\"nfo\">"+matchCount+" "+this.msg.found+" '"+searchText+"'.</span>";
    this.nodestates[this.ROOT_HOME].hidden = false;
    this.print();
    this.setStatus(matchCount+" "+this.msg.found+"'"+searchText+"'.");
}

/* translation tree node class ###############################################
 */

function translationNavTreeNode () {}
translationNavTreeNode.prototype = new documentNavTreeNode();
translationNavTreeNode.prototype.constructor = documentNavTreeNode;
translationNavTreeNode.prototype.baseClass = documentNavTreeNode.prototype.constructor;

/* Generates the node's html label.
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
translationNavTreeNode.prototype.getLabel = function() {
    if (this.state.selected == true) {
		cls = "label-selected";
    } else {
 		cls = "label";
	}
	if (!this.parentId) {
    	return 	'<span class="labelcontainer" style="vertical-align:top;"><a class="'+cls+'" '+((this.url && this.url.length>0) ? 'href="'+this.url+'"' : '')+' >'+this.label+'<\/a></span></div></td>'+
    			'<td></td><td width="5"><img src="'+this.tree.icon.empty+'" width="5" border="0"></td><td></td></tr></table>';
	} else {
		var transField1 = '';
		var transField2 = '';
		var extender = '';
		if (this.mayRead==false) {
			transField1 = '<div class="readonlyfield"><i>'+this.tree.msg.noAccess+'</i></div>';
			transField2 = '<div class="readonlyfield"><i>'+this.tree.msg.noAccess+'</i></div>';
		} else if (this.displayType=='read') {
			if (this.extended) {
				transField1 = '<div class="readonlyfieldextended">'+this.trans1+'</div>';
				transField2 = '<div class="readonlyfieldextended">'+this.trans2+'</div>';
			} else {
				transField1 = '<div class="readonlyfield">'+this.trans1+'</div>';
				transField2 = '<div class="readonlyfield">'+this.trans2+'</div>';
			}
            if (this.multiline) {
                extender = '<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggleExtent();"><img src="'+((this.extended==true) ? this.tree.icon.up : this.tree.icon.down )+'" width="18" height="18" border="0" alt=""/></a>';
            } else {
                extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
            }
        } else if (this.displayType=='edit') {
            if (this.multiline) {
                if (this.extended) {
                    transField1 = '<textarea onkeypress="checkInputValue(event,this);" class="editfieldextended" id="'+this.tree.nestLevel+this.fieldName1+'" name="'+this.tree.nestLevel+this.fieldName1+'" rows="5" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].trans1=document.getElementById(\''+this.tree.nestLevel+this.fieldName1+'\').value;">'+this.trans1+'</textarea>';
                    transField2 = '<textarea onkeypress="checkInputValue(event,this);" class="editfieldextended" id="'+this.tree.nestLevel+this.fieldName2+'" name="'+this.tree.nestLevel+this.fieldName2+'" rows="5" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].trans2=document.getElementById(\''+this.tree.nestLevel+this.fieldName2+'\').value;">'+this.trans2+'</textarea>';
                } else {
                    transField1 = '<textarea onkeypress="checkInputValue(event,this);" class="editfield" id="'+this.tree.nestLevel+this.fieldName1+'" name="'+this.tree.nestLevel+this.fieldName1+'" rows="1" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].trans1=document.getElementById(\''+this.tree.nestLevel+this.fieldName1+'\').value;">'+this.trans1+'</textarea>';
                    transField2 = '<textarea onkeypress="checkInputValue(event,this);" class="editfield" id="'+this.tree.nestLevel+this.fieldName2+'" name="'+this.tree.nestLevel+this.fieldName2+'" rows="1" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].trans2=document.getElementById(\''+this.tree.nestLevel+this.fieldName2+'\').value;">'+this.trans2+'</textarea>';
                }
                extender = '<a class="controlButton" onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].toggleExtent();"><img src="'+((this.extended==true) ? this.tree.icon.up : this.tree.icon.down )+'" width="18" height="18" border="0" alt=""/></a>';
            } else {
                var maxlength = this.maxlength > 0 ? " maxlength=\"" + this.maxlength + "\" " : "";
                transField1 = '<input type="text"'+maxlength+' onkeypress="checkInputValue(event,this);" class="editfield" id="'+this.fieldName1+'" name="'+this.fieldName1+'" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].trans1=document.getElementById(\''+this.fieldName1+'\').value;" value="'+this.trans1+'">';
                transField2 = '<input type="text"'+maxlength+' onkeypress="checkInputValue(event,this);" class="editfield" id="'+this.fieldName2+'" name="'+this.fieldName2+'" onchange="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].trans2=document.getElementById(\''+this.fieldName2+'\').value;" value="'+this.trans2+'">';
                extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
            }
        } else if (this.displayType=='group') {
			extender = '<img src="'+this.tree.icon.empty+'" width="18" height="18" border="0" alt=""/>';
		}
        transField1 = transField1.replace(/&quot;/g, '"');
        transField2 = transField2.replace(/&quot;/g, '"');
        return 	'<span class="labelcontainer" style="vertical-align:top;"><a class="'+cls+'" '+((this.url && this.url.length>0) ? 'href="'+this.url+'"' : '')+' >'+this.label+'<\/a></span></div></td>'+
				'<td width="10"><img src="'+this.tree.icon.empty+'" width="10" height="18" border="0" alt=""/></td>'+
				'<td width="20" valign="top">'+extender+'</td>'+
				'<td width="35%">'+transField1+'</td>'+
				'<td width="5"><img src="'+this.tree.icon.empty+'" width="5" border="0"></td>'+
				'<td width="35%">'+transField2+'</td></tr></table>';
	}
}

translationNavTreeNode.prototype.attachHtmlEditors = function() {
    if (!this.htmlEditor1) {
        this.htmlEditor1 = this.attachEditorComponent(this.tree.nestLevel + this.fieldName1);
    }
    if (!this.htmlEditor2) {
        this.htmlEditor2 = this.attachEditorComponent(this.tree.nestLevel + this.fieldName2);
    }
}

translationNavTreeNode.prototype.removeHtmlEditor = function() {
    var treeFrame = this.tree.getTreeFrame();
    if (this.htmlEditor1) {
        // get value from inline HTML editor
        treeFrame.tinyMCE.triggerSave(true, true);
        this.trans1 = this.detachHtmlEditor(this.tree.nestLevel + this.fieldName1);
        this.htmlEditor1 = false;
    }
    if (this.htmlEditor2) {
        // get value from inline HTML editor
        treeFrame.tinyMCE.triggerSave(true, true);
        this.trans2 = this.detachHtmlEditor(this.tree.nestLevel + this.fieldName2);
        this.htmlEditor2 = false;
    }
}

/* dc instance tree class ####################################################
 *
 * Used in the instance viewer of mars DC.
 */

function dcInstanceNavTree () {}
dcInstanceNavTree.prototype = new documentNavTree();
dcInstanceNavTree.prototype.constructor = documentNavTree;
dcInstanceNavTree.prototype.baseClass = documentNavTree.prototype.constructor;
dcInstanceNavTree.printMode = false;
dcInstanceNavTree.printAll = true;

/* Adds a node to the tree.
 *
 * @see dcInstanceNavTreeNode
 *
 * @param id the unique node id
 * @param label the node label
 * @param parentId the parent node's id (must be an id that already exists in the tree)
 * @param allLoaded flag whether this node is fully loaded
 * @param url the url that the node label links to
 * @param openIcon the icon to show when the node is expanded
 * @param closedIcon the icon to show when the node is collapsed
 * @param displayType determines how a node is rendered (current implementation has "read" and "edit" options for read and edit mode)
 * @param fieldType the field data type ("string", "translation", "optional", ...)
 * @param fieldName the name attribute of the property's input tag
 * @param fieldValue the value of the property's input tag
 * @param minCardinality the minimum cardinality of the property/group
 * @param maxCardinality the maximum cardinality of the property/group
 * @param deleteChildren flag whether to delete the node's children if the node already exists
 * @param insertBelowId the id of the node after which this node should be added to the tree (used to retain ordering)
 * @param mayRead flag whether user has read access to this property/group
 * @param mayCreate flag whether user has create access to this property/group
 * @param mayDelete flag whether user has delete access to this property/group
 * @param dlFieldName the default language field name (in case this node is of fieldType "translation")
 * @param dlFieldValue the default language field value (in case this node is of fieldType "translation")
 * @param dlSelected the current default language (in case this node is of fieldType "translation")
 */
dcInstanceNavTree.prototype.addNode = function(id, label, parentId, allLoaded, url, openIcon, closedIcon, displayType, fieldType, fieldName, fieldValue, minCardinality, maxCardinality, deleteChildren, insertBelowId, mayRead, mayCreate, mayDelete, dlFieldName, dlFieldValue, dlSelected) {
    if (!parentId || this.nodes[parentId]) {
        if (!this.nodes[id]) {
            var newNode = new dcInstanceNavTreeNode();
            newNode.init(id,label,this,parentId,allLoaded, url, openIcon, closedIcon, insertBelowId);
            newNode.extended = false;
        } else {
            var newNode = this.nodes[id];
            newNode.label = label;
            newNode.url = url;
            newNode.openIcon = openIcon;
            newNode.closedIcon = closedIcon;
            this.nodestates[id].allLoaded = allLoaded;
        }
        newNode.displayType = displayType;
        newNode.fieldType = fieldType;
        newNode.fieldName = fieldName;
        newNode.fieldValue = fieldValue;
        newNode.minCardinality = (minCardinality) ? minCardinality : 1;
        newNode.maxCardinality = (maxCardinality) ? maxCardinality : 1;
        newNode.mayRead = mayRead;
        newNode.mayCreate = mayCreate;
        newNode.mayDelete = mayDelete;
        newNode.dlFieldName = dlFieldName;
        newNode.dlFieldValue = dlFieldValue;
        newNode.dlSelected = (dlSelected==true) ? true : false;
        if (deleteChildren==true) this.deleteNodeChildren(newNode.id);
    }
}

/* dc instance tree node class ###############################################
 */

function dcInstanceNavTreeNode () {}
dcInstanceNavTreeNode.prototype = new documentNavTreeNode();
dcInstanceNavTreeNode.prototype.constructor = documentNavTreeNode;
dcInstanceNavTreeNode.prototype.baseClass = documentNavTreeNode.prototype.constructor;
dcInstanceNavTreeNode.prototype.hasNeighboursWithLabel = false;

/* Expands the node by changing its state and re-rendering its subtree.
 * Also, all other nodes in the tree are closed.
 *
 * @see navTreeNode#toggle
 */
dcInstanceNavTreeNode.prototype.expand = function() {
	// hide all nodes
	var nodes = this.tree.nodes;
	for (var cn in nodes) {
        if (typeof nodes[cn] == 'function')
            continue;
        var curState = this.tree.nodestates[cn];
        curState.expanded = false;
	    curState.allExpanded = false;
	    curState.selected = false;
	}
	// hide search result detail iframe, if it exists
    var treeFrame = this.tree.getTreeFrame();
    var doc = this.tree.getTreeDocument();
	var detailContainer = doc.getElementById("detailContainer");
	if (detailContainer) {
	    detailContainer.style.width = detailContainer.style.height = "0px";
    }
	// show this node
	curNode = this;
	curNode.state.hidden = false;
	curNode.state.selected = true;
	// show all parent nodes of this node
    while(curNode.parentId != null) {
        curNode = nodes[curNode.parentId];
        curNode.state.expanded = true;
        curNode.state.selected = true;
        curNode.state.hidden = false;
    }
    // show all child nodes of this node
	this.state.expanded = true;
	this.state.allExpanded = true;
  	var treec = this.treeChildren;
    for (var i = 0, len = treec.length; i < len; i++) {
       this.tree.nodestates[treec[i]].hidden = false;
	}
	// re-render whole tree
  	nodes[this.tree.ROOT_HOME].reprint();
}

/* Helper method to generate the html for all direct childnodes of this node.
 *
 * @see dcInstanceNavTreeNode#print dcInstanceNavTreeNode#reprint
 */
dcInstanceNavTreeNode.prototype.printDirectChildren = function() {
	var aH = this.tree.aHTML;
	var treec = this.treeChildren;
    var pressreport = false;
    if (this.tree.printMode && this.tree.printView == "group" && treec.length > 0) {
        // print mode: plain tree view
        if (this.id==this.tree.ROOT_HOME && this.tree.thumbnail) {
            aH.push(this.tree.thumbnail);
            aH.push("<br clear=\"all\">");
        }
        if (this.id != this.tree.ROOT_HOME && this.fieldType != "optional" && this.displayType != "relations" && this.displayType != "subnodes"
                && (this.tree.printAll || this.state.expanded)) {
            aH.push(this.getPrefix() + this.getToggler() + this.getLabel() + this.getSuffix());
        }
        for (var i = 0; i < treec.length; i++) {
            var curChild = this.tree.nodes[treec[i]];
            if (curChild.fieldType != "optional" && curChild.fieldType != "group" &&
                    curChild.displayType != "relations" && curChild.displayType != "subnodes" &&
                    (this.tree.printAll || this.state.expanded)) {
                aH.push(curChild.getPrefix() + curChild.getToggler() + curChild.getLabel() + curChild.getSuffix());
            }
        }
        for (var i = 0; i < treec.length; i++) {
            this.tree.nodes[treec[i]].print();
        }

    } else if (this.state.expanded && this.displayType!='relations' && this.displayType!='subnodes') {
        // only print child nodes if this node is expanded, has children and is no of no special type
        if (this.id != this.tree.ROOT_HOME) {
            this.tree.viewType = "group";
        }
        var lastVisible = treec.length-1;
	    /* calculate last visible child node - not necessary for this instance tree at the moment
		for (var i = treec.length-1; i > -1; i--) {
			var curChild = this.tree.nodes[treec[i]];
			if (curChild.hidden == false && ( (this.id!=this.tree.ROOT_HOME && curChild.treeChildren.length==0) || (this.id==this.tree.ROOT_HOME && curChild.treeChildren.length>0) ) ) {
				lastVisible = i;
				break;
			}
		}
		*/
		// check if any non-leaf nodes exist and if any of them is currently open
		var firstOpen = false;
		for (var i = 0; i < treec.length; i++) {
	        var curChild = this.tree.nodes[treec[i]];
            if(curChild.id=="PRESSREPORT.RSSDESC" || curChild.id=="pressreport.rssdesc") pressreport = true;
            if ((curChild.fieldType=='group' || curChild.displayType=='relations' || curChild.displayType=='subnodes') && curChild.state.expanded) {
		        firstOpen = true;
		        break;
	        }
        }
        // insert thumbnail at root node
    	if (this.id==this.tree.ROOT_HOME) {
            if (this.tree.thumbnail) {
                aH.push('<div style="float:left;width:25%;margin-bottom:10px;">'+this.tree.thumbnail+'</div><div style="float:right;width:70%;margin-bottom:10px;">');
            } else {
                aH.push('<div style="float:left;width:100%;margin-bottom:10px;">');
            }
        }
    	// print groups before properties or vice versa for root node
    	if (this.id==this.tree.ROOT_HOME) {
            if(pressreport && !this.tree.printMode) aH.push('<div class="instance-properties-container" id="pressreport" style="height:200px">');
            else aH.push('<div class="instance-properties-container">');
    	} else {
        	aH.push('<div class="instance-groups-container">');
    	}

        // filter printed nodes
        var printNodes = new Array();
        var hasLabel = false;
        for (var i = 0; i < treec.length; i++) {
            var curChild = this.tree.nodes[treec[i]];
            if ((this.id!=this.tree.ROOT_HOME && curChild.fieldType=='group') || (this.id==this.tree.ROOT_HOME && curChild.fieldType!='group' && curChild.fieldType!='optional')) {
                curChild.isLastSibling = i==lastVisible ? true : false;
                hasLabel = hasLabel || (curChild.label != null && curChild.label.length > 0);
                if (this.id!=this.tree.ROOT_HOME && !firstOpen) {
                    curChild.state.selected = true;
                    curChild.state.expanded = true;
                    curChild.state.allExpanded = true;
                    firstOpen = true;
                }
                printNodes.push(curChild);
            }
        }
        if (this.tree.orderLabel != null && this.id == this.tree.ROOT_HOME) {
            aH.push("<div class=\"instance-label\" style=\"margin-top:15px;\">"
                + "<a class=\"main\" href=\"javascript:getTop().addToCart(" + this.tree.oid
                + ",false,false," + this.tree.orderResolveMedia + ")\""
                + " style=\"background-image:url('images/cart.gif');padding-left:20px;padding-top:2px\""
                + ">"
                + this.tree.orderLabel + "</a></div>");
        }
        // render nodes
        for (var i = 0; i < printNodes.length; i++) {
            var curChild = printNodes[i];
            curChild.hasNeighboursWithLabel = hasLabel;
            aH.push(curChild.getPrefix());
            if (curChild.id!=this.tree.ROOT_HOME) {
                aH.push(curChild.getToggler());
                aH.push(curChild.getLabel());
            }
            aH.push(curChild.getSuffix());
        }
        if(pressreport && !this.tree.printMode) aH.push('</div><div><a class="tabs" href="javascript: top.openpressreport()" id="pressreportbutton">' + showPresseReport + '</a></div>');
        aH.push('</div>');
    	if (this.id==this.tree.ROOT_HOME) {
        	aH.push('</div>');
    	}
    	if (this.id==this.tree.ROOT_HOME) {
        	aH.push('<div class="instance-groups-container">');
    	} else {
        	aH.push('<div class="instance-properties-container">');
    	}
    	var leavesFound = false;
        if (!this.tree.printMode) {
            printNodes = new Array();
            hasLabel = false;
            for (var i = 0; i < treec.length; i++) {
                var curChild = this.tree.nodes[treec[i]];
                if (((this.id!=this.tree.ROOT_HOME && curChild.fieldType!='group' && curChild.fieldType!='optional')
                        || (this.id==this.tree.ROOT_HOME && curChild.fieldType=='group'))) {
                    hasLabel = hasLabel || (curChild.label != null && curChild.label.length > 0);
                    curChild.isLastSibling = i==lastVisible ? true : false;
                    if (curChild.treeChildren.length==0) leavesFound = true;
                    if (this.id==this.tree.ROOT_HOME && !firstOpen) {
                        curChild.state.selected = true;
                        curChild.state.expanded = true;
                        curChild.state.allExpanded = true;
                        firstOpen = true;
                    }
                    printNodes.push(curChild);
                }
            }
            for (var i = 0; i < printNodes.length; i++) {
                var curChild = printNodes[i];
                curChild.hasNeighboursWithLabel = hasLabel;
                aH.push(curChild.getPrefix());
                if (curChild.id!=this.tree.ROOT_HOME) {
                    aH.push(curChild.getToggler());
                    aH.push(curChild.getLabel());
                }
                aH.push(curChild.getSuffix());
            }
        }
        // print horizontal bar to separate this node's children from the rest
    	if (this.id!=this.tree.ROOT_HOME && leavesFound) {
        	aH.push('<div style="height:3px;background-color:#A6A8BD;line-height:1px;"><br></div>');
    	}
    	aH.push('</div>');
    	// output the child nodes
        for (var i = 0; i < treec.length; i++) {
            this.tree.nodes[treec[i]].print();
        }
    }
    // print special nodes
    if (this.state.expanded == true && this.displayType=='relations') {
	    aH.push(this.getRelations());
    }
    if (this.state.expanded == true && this.displayType=='subnodes') {
	    aH.push(this.getSubnodes());
    }
}

/* Generates the node's full html output and recursively calls itself for all subnodes.
 *
 * @see navTree#print
 */
dcInstanceNavTreeNode.prototype.print = function() {
	var aH = this.tree.aHTML;
	if (this.state.hidden == false) {
		if (this.id==this.tree.ROOT_HOME) {
			aH.push(this.getPrefix());
		}
	    var treec = this.treeChildren;
	    // check if all children of the node are visible (required to display +/- icons)
	    if (this.state.expanded == true && treec.length > 0) {
		    this.state.allExpanded = true;
	        for (var i = 0, len = treec.length; i < len; i++) {
	        	if (this.tree.nodestates[treec[i]].hidden == true) this.state.allExpanded = false;
            }
	    }
	    // leaf nodes are always fully loaded
	    if (treec.length==0) {
		    this.state.allLoaded = true;
	    }
	    this.printDirectChildren();
 		if (this.id==this.tree.ROOT_HOME) {
			aH.push(this.getSuffix());
		}

	}
}

/* Generates the node's html output (except the container elements) and recursively calls the print method for all subnodes.
 *
 * @see navTreeNode#print
 */
dcInstanceNavTreeNode.prototype.reprint = function() {
	this.tree.aHTML = new Array();
	var aH = this.tree.aHTML;
	var treec = this.treeChildren;
	if (this.state.hidden == false) {
	    this.printDirectChildren();
	}
	this.tree.render(this.id);
}

/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 *
 * @return the prefix
 */
dcInstanceNavTreeNode.prototype.getPrefix = function() {
	if (this.id==this.tree.ROOT_HOME) {
		// root node container
		return '<div id="'+this.id+'">';
	} else if (this.fieldType!='group' && this.fieldType!='optional') {
		// property node container
		return '<div id="'+this.id+'" class="propertyCell">';
	} else {
		// group node container
		return '<span id="'+this.id+'">';
	}
}

/* Generates the node's html container elements.
 *
 * @see navTreeNode#print
 *
 * @return the toggler
 */
dcInstanceNavTreeNode.prototype.getToggler = function() {
	return '';
}

/* Generates the node's html label.
 *
 * @see navTreeNode#print
 *
 * @return the label
 */
dcInstanceNavTreeNode.prototype.getLabel = function() {
	if (this.fieldType!='group' && this.fieldType!='optional') {
        // PROPERTY NODE
        var cls = 'instance-label';
	    if (this.state.selected == true) {
			cls = "instance-label-selected";
	    } else if (this.fieldType=='optional') {
		    cls = "instance-label-optional";
	    }
		var label = '<span class="'+cls+'">'+this.label+'</span>';

        var valueField = '';
        if (this.mayRead==false) {
            valueField = '<i>'+this.tree.msg.noAccess+'</i>';
        } else {
            if (this.fieldType=='string' || this.fieldType=='translation' || this.fieldType=='Thesaurus' || this.fieldType=='Distinct'  || this.fieldType=='infotypeLink') {
                valueField = this.fieldValue+'';
            } else if (this.fieldType=='multilanguage') {

            } else if (this.fieldType=='date') {
                valueField = this.fieldValue;
            } else if (this.fieldType=='language') {
                var langCode = this.fieldValue;
                var licLang = languages;
                for (var langId in licLang) {
                    if (typeof licLang[langId] == 'function')
                        continue;
                    if (langCode==licLang[langId][0]) valueField = '<img src="images/flags/'+licLang[langId][1]+'.gif" align="absmiddle" border="0" hspace="1" alt="'+licLang[langId][2]+'">'+licLang[langId][2];
                }
            } else if (this.fieldType=='selectlist') {
                /*var listId = this.fieldValue.substring(0,this.fieldValue.indexOf(':'));
                var elementId = this.fieldValue.substring(this.fieldValue.indexOf(':')+1,this.fieldValue.length+1);
                valueField = '';
                if (this.tree.selectLists != null && this.tree.selectLists[listId]) {
                    var list = this.tree.selectLists[listId];
                    var i;
                    for (i = 0; i < list.length; i++)
                        if (list[i][0] == elementId) {
                            valueField = list[i][1]+'';
                            break;
                        }
                } else {
                    valueField = 'unknown selectlist';
                }*/
                valueField = this.fieldValue + '';
            } else if (this.fieldType=='boolean') {
                valueField = '<input type="checkbox" DISABLED '+(this.fieldValue=='T' ? 'CHECKED' : '')+'>';
            }
        }
        valueField = valueField.replace(/&quot;/g, '"');
        var hasLabel = this.label != null && this.label.length > 0;
        var needValueMargin = this.hasNeighboursWithLabel || hasLabel;
        var out = '';
        if (hasLabel || needValueMargin) {
            out = '<div style="float:left;width:150px;margin-left:0px;font-size:11px;padding-top:2px;padding-bottom:1px;">' +
                  (hasLabel ? label : '')+'</div>';
        }
        if (this.fieldType=='infotypeLink' && this.id != "INFOTYPE_PICTURE.PHOTOGRAPHERS_ID") {
        return out +
               '<ul class="instanceList"><li><a href="instance.do?action=instance.load&id='+(this.url)+'" style="font-size:11px;padding-top:2px;padding-bottom:1px;margin-left:'+
               (needValueMargin ? '200px' : '0px')+';'+(document.all ? 'height:1%;' : '') + '">'+valueField+'&nbsp;</a></li></ul>' +
               '<div style="clear:both;"></div>';
        } else {
        return out +
               '<div style="font-size:11px;padding-top:2px;padding-bottom:1px;margin-left:'+
               (needValueMargin ? '200px' : '0px')+';'+(document.all ? 'height:1%;' : '') + '">'+valueField+'&nbsp;</div>' +
               '<div style="clear:both;"></div>';
        }
        /*
        if (!this.label || this.label.length==0) {
            return '<div style="font-size:11px;padding-top:2px;padding-bottom:1px;margin-left:'+
                   (this.hasNeighboursWithLabel ? '200px' : '0px')+';'+(document.all ? 'height:1%;' : '') + '">'+valueField+'&nbsp;</div>' +
                   '<div style="clear:both;"></div>';
        } else {
            return '<div style="float:left;width:150px;margin-left:0px;font-size:11px;padding-top:2px;padding-bottom:1px;">'+label+'</div>' +
                   '<div style="margin-left:200px;font-size:11px;padding-top:2px;padding-bottom:1px;' +
                   (document.all ? 'height:1%;' : '') + '">'+valueField+'&nbsp;<br clear="all"></div>' +
                   '<div style="clear:both;"></div>';*/
            /*var resultString = null;

            resultString = '<table cellpadding="0" cellspacing="0" border="0" class="instancedetail">';
            resultString += '   <tr>';
            resultString += '       <td width="150" valign=\"top\" nowrap>'+label+'</td>';
            resultString += '       <td width="50">&nbsp;</td>';
            resultString += '       <td>'+valueField+'&nbsp;</td>';
            resultString += '   </tr>';
            resultString += '</table>';

            return resultString;*/
        //}
    } else {
        // GROUP NODE
        var cls = 'tabs';
	    if (this.state.selected == true || this.tree.printMode) {
			cls = 'tabsselected';
	    } else if (this.fieldType=='optional') {
		    cls = 'tabsoptional';
	    }
        return this.tree.printMode ?
               '<a class="'+cls+'">'+'&nbsp;'+this.label+'</a>' :
               '<a class="'+cls+'" '+((this.url && this.url.length>0 && this.displayType!='relations' && this.displayType!='subnodes') ? 'href="'+this.url+'"' : 'href="javascript:parent.'+this.tree.id+'.nodes[\''+this.id+'\'].expand();"')+' onclick="parent.'+this.tree.id+'.nodes[\''+this.id+'\'].expand();">'+'&nbsp;'+this.label+'</a>';
	}
}


/* Generates the node's html prefix.
 *
 * @see navTreeNode#print
 * @see navTreeNode#getPrefix
 *
 * @return the suffix
 */
dcInstanceNavTreeNode.prototype.getSuffix = function() {
	if (this.id==this.tree.ROOT_HOME) {
		// root node container
		return '</div>';
	} else if (this.fieldType!='group' && this.fieldType!='optional') {
		// property node container
		return '</div>';
	} else {
		// group node container
		return '</span>';
	}
}

/* Helper method for generating the container for the relations group.
 *
 * @see dcInstanceNavTreeNode#printDirectChildren
 *
 * @return the html container
 */
dcInstanceNavTreeNode.prototype.getRelations = function() {
    this.tree.viewType = "relations";
    return this.tree.printMode ? '':'<div style="padding:0px;width:100%;"><iframe name="searchframe" id="searchframe" frameborder="no" height="auto" width="100%" scrolling="no" src="'+this.url+'" allowTransparency="true"></iframe><div id="resultContainer" style="visibility:hidden"></div><div id="resultFooter" style="display:block;clear:both;padding-top:20px;padding-left:75px"></div></div>';
}

/* Helper method for generating the container for the subnodes group.
 *
 * @see dcInstanceNavTreeNode#printDirectChildren
 *
 * @return the html container
 */
dcInstanceNavTreeNode.prototype.getSubnodes = function() {
    this.tree.viewType = "subnodes";
	return this.tree.printMode ? '':'<div style="padding:0px;width:100%;"><iframe name="searchframe" id="searchframe" frameborder="no" height="auto" width="100%" scrolling="no" src="'+this.url+'" allowTransparency="true"></iframe><div id="resultContainer" style="visibility:hidden"></div><div id="resultFooter" style="display:block;clear:both;padding-top:20px;padding-left:75px"></div></div>';
}










// ------------ DatePicker JS, adapted from searchForm.js ---------------

function makeJsFieldName(fieldName) {
    return fieldName.replace("\.", "_").replace("\[", "_").replace("\]", "_");
}

function createDateInput(idTree, idField, fieldName, value) {
    // use date picker component
    var myIdField = makeJsFieldName(idField);
    var internalName = myIdField.replace('(', '_').replace(')', '_').replace('[', '_').replace(']', '_');
    var myDate = (value != null && value.length > 0) ? new Date(parseInt(value)) : null;       // create date from milliseconds
    var day = myDate != null ? myDate.getDate() : "";
    var month = myDate != null ? (myDate.getMonth() + 1) : "";
    var year = myDate != null ? myDate.getFullYear() : "";
    var datestr = _dt2dtstr(myDate);     // string representation for datepicker component
    var onChange = "updateDateValue(parent." + idTree + ", '" + idField + "', '" + myIdField + "', '" + fieldName + "');";
    var myHidden = "<input type=\"hidden\" name=\"" + fieldName + "\" id=\"" + fieldName + "\" value=\"" + datestr + "\">";
    output = myHidden +
    	"<span style=\"white-space:nowrap\">" +
        "<input class=\"datePickerField\" type=\"hidden\" id=\"date_" + myIdField + "\" onChange=\"" + onChange + "\" " +
        "NAME=\"date_" + myIdField + "\"  VALUE=\"" + datestr + "\">" +
        "<input onChange=\"" + onChange +
        "\" class=\"datePickerField\" type=\"text\" id=\"date_" + internalName + "DD\"" +
        "NAME=\"date_" + internalName + "DD\" SIZE=\"2\"  MAXLENGTH=\"2\" VALUE=\"" + day + "\">"+
        "<img src=\"images/dummy.gif\" width=\"5\" border=\"0\" alt=\"\">"+
        "<input " +
        "onChange=\"" + onChange + "\" class=\"datePickerField\" type=\"text\"" +
        "id=\"date_" + internalName + "MM\" NAME=\"date_" + internalName + "MM\" SIZE=\"2\"  MAXLENGTH=\"2\" VALUE=\"" +
        month + "\">"+
        "<img src=\"images/dummy.gif\" width=\"5\" border=\"0\" alt=\"\">"+
        "<input onChange=\"" + onChange + "\" class=\"datePickerField\"" +
        "type=\"text\" id=\"date_" + internalName + "YY\" NAME=\"date_" + internalName + "YY\" SIZE=\"4\"  MAXLENGTH=\"4\"" +
        "VALUE=\"" + year + "\">"+
        "<img src=\"images/dummy.gif\" width=\"5\" border=\"0\" alt=\"\">"+
        "<img src=\"images/buttons/icon.calendar.gif\" " +
        "class=\"tbb\" id=\"date_" + myIdField + "Ancor\" name=\"date_" + myIdField + "Ancor\" " +
        "onMouseUp=\"positionPicker(event, 'date_" + myIdField + "');\" style=\"vertical-align:top\"></div>"+
        "</span>";
    return output;
}

function updateDateValue(tree, idField, formIdField, fieldName) {
    flexCalendar.doupdate("date_" + formIdField);
    var internalName = formIdField.replace('(', '_').replace(')', '_').replace('[', '_').replace(']', '_');
    var day = document.getElementById("date_" + internalName + "DD").value;
    var month = parseInt(document.getElementById("date_" + internalName + "MM").value) - 1;
    var year = document.getElementById("date_" + internalName + "YY").value;
    var date = new Date(year, month, day).getTime();
    tree.nodes[idField].fieldValue = date;
    document.getElementById(fieldName).value = date;
    tree.renderHidden();
}

  function _dt2dtstr (dt_datetime) {
    if (dt_datetime == null)
        return "";
    var result = "";
    if ((dt_datetime.getDate())<10) { result+="0"; }
    result+=  dt_datetime.getDate();
    result+="-";
    if ((dt_datetime.getMonth()+1)<10) { result+="0"; }
    result+=dt_datetime.getMonth()+1;
    result+="-";
    result+=dt_datetime.getFullYear();
    return result;
  }


// -------------- Select Lists JS, adapted from searchForm.js ---------------

// Creates a select list
function createSelectList(tree, idField, selectListId, selectedItemId, elementParentGroup, emptyElement) {
    if (tree.selectLists == null || tree.selectLists[selectListId] == null) {
        //alert("Undefined selectlist: " + selectListId);
        return null;
    }
    var list = tree.selectLists[selectListId];
    var name = idField + "_LISTVALUE_" + selectListId;
    var select = document.createElement("select");
    select.setAttribute("size", 1);
    select.setAttribute("name", name);
    select.setAttribute("id", name);
    //select.className = "selectinputfield";

    if (emptyElement) {
        // append empty element
        var option = document.createElement("option");
        option.value = -1;
        option.innerHTML = "-";
        select.appendChild(option);
        select.setAttribute("emptyElement", "true");
    }
    var i;
    var firstElement = null;
    for (i = 0; i < list.length; ++i) {
        var elementId = list[i][0];
        var elementLabel = list[i][1];
        if ((elementParentGroup == null || tree.selectElementParent[elementId] == elementParentGroup) &&
            ((tree.selectElementPermissions[elementId] != null && tree.selectElementPermissions[elementId] == true) ||
             (elementId == selectedItemId))) {
            if (firstElement == null)
                firstElement = elementId;
            var option = document.createElement("option");
            option.value = elementId;
            option.innerHTML = elementLabel;
            if (elementId == selectedItemId) {
                option.selected = true;
                option.defaultSelected = true;
                select.setAttribute("selectedItemId", selectedItemId);
            }
            select.appendChild(option);
        }
    }
    if (select.getAttribute("selectedItemId") == null && firstElement != null) {
        // no item was selected, set listItemId to first list element for correct "chaining"
        select.setAttribute("selectedItemId", firstElement);
    }
    if (select.options.length > 0) {
        select.setAttribute("fieldValue", selectListId + ":" + select.options[select.selectedIndex].value);
    }
    return select;
}

// Creates a linked select list "group" (including this select list's parent(s) if there is one)
function createSelectListGroup(tree, idItem, fieldName, selectListId, fieldValue, allowEmpty) {
    var input = document.createElement("input");
    input.setAttribute("type", "hidden");
    input.setAttribute("name", fieldName);
    input.setAttribute("id", fieldName);
    if (tree.selectLists == null || tree.selectListParent == null)
        return "Select lists not defined";
    if (tree.selectListParent[selectListId] == null) {   // not linked to another select list
        var group = document.createElement("span");
        var select = createSelectList(tree, idItem, selectListId, fieldValue, null, fieldName);
        if (select) {
            select.setAttribute("onchange", "updateSelectValue(parent." + tree.id + ", '" + idItem + "', '" + fieldName + "', " + selectListId + ")");
            if (select.getAttribute("fieldValue") != null) {
                input.value = tree.nodes[idItem].fieldValue = select.getAttribute("fieldValue");
            }
            group.appendChild(input);
            group.appendChild(select);
            return "<span>" + group.innerHTML + "</span>";
        } else {
            return "";
        }
    }
    else {
        var lists = new Array();
        lists.push(selectListId);
        var listValues = new Array();
        listValues.push((fieldValue != null && fieldValue.length > 0) ? parseInt(fieldValue) : -1);
        var parentList = tree.selectListParent[selectListId];
        while (parentList != null) {
            lists.push(parentList);
            listValues.push(tree.selectElementParent[listValues[listValues.length - 1]]);
            parentList = tree.selectListParent[parentList];
        }
        var i;
        var select;
        var group = document.createElement("span");
        var lastValue = null;
        var selectLists = new Array();
        for (i = lists.length - 1; i >= 0; i--) {
            select = createSelectList(tree, idItem, lists[i], listValues[i], lastValue, i == 0 && allowEmpty);
            if (select) {
                if (i > 0)
                    makeSelectListOnChange(tree.id, select, idItem, lists[i], lists[i-1]);
                else
                    select.setAttribute("onchange", "updateSelectValue(parent." + tree.id + ", '" + idItem + "', '" + fieldName + "', " + lists[i] + ")");
                selectLists.push(select);
                lastValue = select.getAttribute("selectedItemId");
            } else {
                break;
            }
        }
        if (select && select.getAttribute("fieldValue") != null) {
            input.value = tree.nodes[idItem].fieldValue = select.getAttribute("fieldValue");
        }
        group.appendChild(input);
        for (i = 0; i < selectLists.length; i++)
            group.appendChild(selectLists[i]);
        return "<span>" + group.innerHTML + "</span>";
    }
}

// update a single select list value
function updateSelectValue(tree, idItem, fieldName, selectListId) {
    var selectVal = "";
    var elem = document.getElementById(idItem + "_LISTVALUE_" + selectListId);
    if (elem.options.length > 0)
        selectVal = elem.options[document.getElementById(idItem + "_LISTVALUE_" + selectListId).selectedIndex].value;
    tree.nodes[idItem].fieldValue = selectListId + ":" + selectVal;
    document.getElementById(fieldName).value = selectListId + ":" + selectVal;
    tree.renderHidden();
}

// update a group of select lists
function onSelectListChanged(tree, idItem, selectListId, childSelectListId) {
    var element = document.getElementById(idItem + "_LISTVALUE_" + selectListId);
    var child = document.getElementById(idItem + "_LISTVALUE_" + childSelectListId);
    var newval = element.options[element.selectedIndex].value; // id of selected item determines which items are available in the child select list

    // remove all list items in child
    while (child.childNodes.length > 0)
        child.removeChild(child.firstChild);

    if (child.getAttribute("emptyElement") == "true") {
        // insert empty element
        var option = document.createElement("option");
        option.value = -1;
        option.innerHTML = "-";
        child.appendChild(option);
    }
    // insert all list items whose parent is newval
    var i;
    var childlist = tree.selectLists[childSelectListId];
    for (i = 0; i < childlist.length; ++i) {
        if (tree.selectElementParent[childlist[i][0]] == newval) {
            var option = document.createElement("option");
            option.value = childlist[i][0];
            option.appendChild(document.createTextNode(childlist[i][1]));
            child.appendChild(option);
        }
    }
    if (child.onchange) child.onchange();   // propagate
    tree.renderHidden();
}

function makeSelectListOnChange(idTree, select, idItem, selectListId, childSelectListId) {
    select.setAttribute("onchange", "onSelectListChanged(parent." + idTree + ", '" + idItem + "', " + selectListId + ", " + childSelectListId + ")");
}


// -------------- String Tokenizer ---------------


String.prototype.tokenize = tokenize;

function tokenize()
  {
     var input             = "";
     var separator         = " ";
     var trim              = "";
     var ignoreEmptyTokens = true;

     try {
       String(this.toLowerCase());
     }
     catch(e) {
       window.alert("Tokenizer Usage: string myTokens[] = myString.tokenize(string separator, string trim, boolean ignoreEmptyTokens);");
       return;
     }

     if (typeof(this) != "undefined")
       {
          input = String(this);
       }

     if (typeof(tokenize.arguments[0]) != "undefined")
       {
          separator = String(tokenize.arguments[0]);
       }

     if (typeof(tokenize.arguments[1]) != "undefined")
       {
          trim = String(tokenize.arguments[1]);
       }

     if (typeof(tokenize.arguments[2]) != "undefined")
       {
          if (!tokenize.arguments[2])
            ignoreEmptyTokens = false;
       }

     var array = input.split(separator);

     if (trim)
       for(var i=0; i<array.length; i++)
         {
           while(array[i].slice(0, trim.length) == trim)
             array[i] = array[i].slice(trim.length);
           while(array[i].slice(array[i].length-trim.length) == trim)
             array[i] = array[i].slice(0, array[i].length-trim.length);
         }

     var token = new Array();
     if (ignoreEmptyTokens)
       {
          for(var i=0; i<array.length; i++)
            if (array[i] != "")
              token.push(array[i]);
       }
     else
       {
          token = array;
       }

     return token;
  }


