var useCopyShortcutKeys = false;

var TreeNodes = new Array();
var PathNodes = new Array();
var Permissions = new Array();
var IsTreeView = false;

var arrEvents = new Array();
function addEvent(iEventId, iRecieverId, sName, sAction, iChangemode, sWarningTitle, sWarningText, iParentEventId, 
		iUseInMultiSelect, sMultiSelectWarningTitle, sMultiSelectWarningText, iPlacement, sExternalUrl, sExternalUrlQueryString, iSiteId) {
	if(arrEvents[iRecieverId]==null) {
		arrEvents[iRecieverId] = new Array();
	}
	var eventObj = new Object();
	
	eventObj.Name = sName;
	eventObj.ActionId = iEventId;
	eventObj.ActionIndex = arrEvents[iRecieverId].length;
	eventObj.Action = sAction;
	eventObj.ChangeMode = iChangemode;
	eventObj.Placement = iPlacement;
	eventObj.WarningTitle = sWarningTitle;
	eventObj.WarningText = sWarningText;
	eventObj.MultiSelectWarningTitle = sMultiSelectWarningTitle;
	eventObj.MultiSelectWarningText = sMultiSelectWarningText;
	eventObj.ParentEventId = iParentEventId;
	eventObj.UseInMultiSelect = iUseInMultiSelect;
	// These could very well be null
	eventObj.ExternalUrl = sExternalUrl;
	eventObj.ExternalUrlQueryString = sExternalUrlQueryString;
	eventObj.SiteId = iSiteId;
	
	// seperator?
	if(iEventId==-100) {
		eventObj.IsSeperator = true;
	}
	else {
		eventObj.IsSeperator = false;
	}
	arrEvents[iRecieverId].push(eventObj);
}


function AddPermissionBitMask(iPermissionId, sBitMask) {
	var oPermissionInfo = new Object();
	oPermissionInfo.PermissionId = iPermissionId;
	oPermissionInfo.BitMask = sBitMask;
	Permissions.push(oPermissionInfo);	
//	debug("Added permissions: "+iPermissionId+" - "+sBitMask);
}

function GetPermissionBitMask(iPermissionId) {
	for(var i=0; i<Permissions.length; i++) {
		if(Permissions[i].PermissionId==iPermissionId) {
			return Permissions[i].BitMask;
		}
	}
	return null;
}

// Extended sitetree drag-and-drop
function TreeRowOnDragOver(row) {
	parent.TreeRowOnDragOver(row, event, iPaneId);
}
function TreeRowOnDrop(row) {
	parent.TreeRowOnDrop(row, event, iPaneId);
}
function TreeRowOnDragLeave(row) {
	parent.TreeRowOnDragLeave(row, event, iPaneId);
}

// param name: The general name of the treeviewlist
//             [name]_swHeaderTable will contain headers
//			   [name]_swContentTable will contain content
function TreeViewListInit(name) {
	var headerTable = document.getElementById(name+"_HeaderTable");
	
	var contentTable = document.getElementById(name+"_ContentTable");
}

TreeNodeDblClick = function(event) { // this bound to TreeNode
	PerformDefaultAction(this);
	return false;
}

TreeNodeContextMenu = function(event) {
	if ( this.IsTrashNode ) {
		ShowTrashActions(this);
	}
	else {
		ShowActions(this);
	}
	return false;
}

TreeNodeMouseDown = function(event) {
	SelectNode(this);
}

TreeNodeDragOver = function(event) {
	DragOver(this);
}

TreeNodeDragEnter = function(event) {
	DragEnter(this);
}

TreeNodeDragLeave = function(event) {
	DragLeave(this);
}

TreeNodeDrop = function(event) {
	Drop(this);
}

TreeNodeDragStart = function(event) {
	DragStart(this);
}

TreeNodeDragEnd = function(event) {
	DragEnd(this);
}

TreeNodeDrag = function(event) {
	Drag(this);
}

TreeRowDragOver = function(event) {
	TreeRowOnDragOver(this);
}

TreeRowDrop = function(event) {
	TreeRowOnDrop(this);
}

TreeRowDragLeave = function(event) {
	TreeRowOnDragLeave(this);
}

OpenCloseNodeMouseDown = function(event) {
	bFocusCausedByTreeNode = true;
}

// CORE-4125 CAS-38619
TreeGetSelector = function() {
	var selector = null;
	
	if ( GetSelectMode() == 1 ) {
		if ( GetSelectorName().indexOf("swRTE_") == 0 ) { // select-tree opened from RichTextEditor
			// create pseudo selector
			selector = {
				AllowMultiSelect : false,
				TypeIds: []
			};
			for ( var i = 0; i < parent.arrEmbeddableFiles.length; i++ ) {
				selector.TypeIds.push(parent.arrEmbeddableFiles[i]);
			}
		}
		else {
			selector = GetParentPane().GetSelector(GetSelectorName());
		}
	}
	
	return selector;
}

var scrollTimer = null;

TreeOnScroll = function(event) {
	if ( scrollTimer ) {
		clearTimeout(scrollTimer);
	}
	scrollTimer = setTimeout(TreeInitNodesInView, 150);
}

// CORE-4037
// JIT initialization of visible treenodes
TreeInitNodesInView = function() {
	scrollTimer = null;
	
	var oFormContainer = document.getElementById("formcontainer");
	
	var potentialNodesVisible = parseInt(oFormContainer.clientHeight/20) + 1;
	var nodeOffset = parseInt(oFormContainer.scrollTop/20) - 1;

	if ( nodeOffset < 0 ) { nodeOffset = 0; }
	
	var i = 0;
	
	var nodesInited = 0;

	var selector = TreeGetSelector();
	
	while ( i < potentialNodesVisible && (i+nodeOffset) < TreeNodes.length ) {
		var oTreeNode = TreeNodes[(i+nodeOffset)].Node;
		
		if ( !oTreeNode.init ) {
			oTreeNode.init = true;
			
			Event.observe(oTreeNode, 'contextmenu', TreeNodeContextMenu.bindAsEventListener(oTreeNode), false);
			Event.observe(oTreeNode, 'mousedown', TreeNodeMouseDown.bindAsEventListener(oTreeNode), false);
			
			if ( !oTreeNode.IsTrashNode ) {
				Event.observe(oTreeNode, 'dblclick', TreeNodeDblClick.bindAsEventListener(oTreeNode), false);
			}
			
			Event.observe(oTreeNode, 'dragover', TreeNodeDragOver.bindAsEventListener(oTreeNode), false);
			Event.observe(oTreeNode, 'dragenter', TreeNodeDragEnter.bindAsEventListener(oTreeNode), false);
			Event.observe(oTreeNode, 'dragleave', TreeNodeDragLeave.bindAsEventListener(oTreeNode), false);
			Event.observe(oTreeNode, 'drop', TreeNodeDrop.bindAsEventListener(oTreeNode), false);
			
			Event.observe(oTreeNode.previousSibling, 'mousedown', OpenCloseNodeMouseDown.bindAsEventListener(oTreeNode.previousSibling), false);
			
			if ( oTreeNode.TreeId>2 ) {
				// only enable dragging if the node is not the solution node or the trash node
				Event.observe(oTreeNode, 'dragstart', TreeNodeDragStart.bindAsEventListener(oTreeNode), false);
				Event.observe(oTreeNode, 'dragend', TreeNodeDragEnd.bindAsEventListener(oTreeNode), false);
				Event.observe(oTreeNode, 'drag', TreeNodeDrag.bindAsEventListener(oTreeNode), false);
			}
			
			if ( oTreeNode.TreeTypeId == TypeIdSiteTree ) {
				var oRow = oTreeNode.parentElement;
				Event.observe(oRow, 'dragover', TreeRowDragOver.bindAsEventListener(oRow), false);
				Event.observe(oRow, 'drop', TreeRowDrop.bindAsEventListener(oRow), false);
				Event.observe(oRow, 'dragleave', TreeRowDragLeave.bindAsEventListener(oRow), false);
			}

			// set width on drag-image
			// CORE-4389: Use getElementsByTagName to locate dragimg - childNodes[2] sometimes returns a #textnode instead of the expected IMG-node
			var dragimg = oTreeNode.getElementsByTagName("IMG");
			if (dragimg[0] != null) {
				dragimg[0].width = Element.getDimensions(oTreeNode).width;
			}
			
			if ( selector ) {
				var nodeLinks = oTreeNode.getElementsByTagName("A");
				for ( var j=0; j<nodeLinks.length; j++ ) {
					var typeID = nodeLinks[j].className.match(/t(\d+)/);
					if ( !InArray(selector.TypeIds,parseInt(typeID[1])) || nodeLinks[j].parentElement.className.indexOf("TreeTrashNode") != -1 ) { // CORE-3379 dim trashnodes
						nodeLinks[j].className = "Icon t"+typeID[1]+"_dis";
						oTreeNode.SelectableNode = false;
					}
					else {
						oTreeNode.SelectableNode = true;
					}
				}
			}
			
			// initialize asynchronous tooltip on node
			// new Via.AsyncTooltip(oTreeNode, 0.5, oTreeNode.TypeId, oTreeNode.NodeId);
			
			nodesInited++;
		}
		i++;
	}
}

function TreeInit() {
	TreeNodes = new Array();
	
	var oFormContainer = document.getElementById("formcontainer");
	// oFormContainer.style.height = "100%";
	oFormContainer.unselectable = "on";
	
	// get tree container
	var oTreeContainer = document.getElementById("treecontainer");
	if(oTreeContainer.firstChild==null) {
		oTreeContainer = document.getElementById("TreeViewListTableContent");
		oTreeContainer.unselectable="on";
	}
	
	oTreeContainer.unselectable="on";
	
	// traverse tree container to find the tree nodes
	var selectedTreeNodeId = GetPane().SelectedTreeNodeId;
	
	for(var i=0; i<oTreeContainer.childNodes.length; i++) {
		var oRow = oTreeContainer.childNodes[i];
		
		oRow.unselectable = "on";
		
		var oOpenCloseNode = oRow.childNodes[1];
		var oTreeNode = oRow.childNodes[2];
	
        if(oTreeNode.TreeTypeId==TypeIdFileTree) {
			oTreeNode.NonGenericTypeId = oTreeNode.TypeId;
       		oTreeNode.TypeId = TranslateTypeIdForFileTree(oTreeNode.TypeId);
        }
	
		oTreeNode.init = false;
		oTreeNode = oTreeNode;
		oTreeNode.unselectable = "on";
		oTreeNode.IsTreeViewNode = true;
		
		oTreeNode.IsTrashNode = (oTreeNode.className != "TreeNode");
		
		TreeNodes[TreeNodes.length] = { Node: oTreeNode };
		
		if ( selectedTreeNodeId != null ) {
			if ( oTreeNode.TreeId == selectedTreeNodeId ) {
				SelectNode(oTreeNode);
				selectedTreeNodeId = null;
			}
		}
	}
	
	UpdateStatusBar();
}

function AddPathNode(iTreeId, iNodeId, iTypeId, iParentTreeId, iParentTypeId, iTreeTypeId, sTitle) {
//	debug("AddPathNode("+iTreeId+", "+iNodeId+", "+iTypeId+", "+iParentTreeId+", "+iParentTypeId+", "+iTreeTypeId+", "+sTitle+")");
	var oLocationBar = document.getElementById("LocationBar");
	var oLink = document.createElement("A");
	var oText = document.createTextNode(sTitle);
	oLink.href = "javascript:ZoomTo("+iTreeId+", "+iNodeId+", "+iTypeId+");";
	oLink.appendChild(oText);
	oLocationBar.appendChild(oLink);
	oText = document.createTextNode("/");
	oLocationBar.appendChild(oText);
	
	var PathNode = new Object();
	PathNode.TreeId = iTreeId;
	PathNode.TypeId = iTypeId;
	PathNode.NodeId = iNodeId;
	PathNode.ParentTreeId = iParentTreeId;
	PathNode.ParentTypeId = iParentTypeId;
	PathNode.TreeTypeId = iTreeTypeId;
	PathNodes.push(PathNode);
}

// determine wether oNode is a subnode to oParentNode
function IsSubNode(oParentNode, oNode) {
	// is it "direct" child?
	if(oNode.ParentTreeId==oParentNode.TreeId) {
		return true;
	}
	else {
		// get parent node
		oNode = GetNode(oNode.ParentTreeId);
		// while the parent node is not null and is not the "solution" node or the trash can
		while(oNode!=null && oNode.TreeId!=1 && (oNode.NodeId!=0 && oNode.TypeId!=0)) {
			// is the node a subnode?
			if(oNode.TreeId==oParentNode.TreeId) {
				return true;
			}
			// get next parent
			oNode = GetNode(oNode.ParentTreeId);
		}
	}
	// oNode is not in the subtree of oParentNode
	return false;
} 
// get the node with TreeId = iTreeId
function GetNode(iTreeId) {
	for(var i=0; i<TreeNodes.length; i++) {
		if(TreeNodes[i].Node.TreeId==iTreeId) {
			return TreeNodes[i].Node;
		}
	}
	for(var i=0; i<PathNodes.length; i++) {
		if(PathNodes[i].TreeId==iTreeId) {
			return PathNodes[i];
		}
	}
	return null;
}

function ShowSearchTool() {
	GetPane().ShowSearchTool();
}

function HidePopupTool() {
	GetPane().HidePopupTool();
}

function Reload() {
	GetPane().Reload();
}

function New() {
	GetPane().Alert("Not yet implemented", "...awaiting specifications!");
}

function SubmitSearch(sOrderByString) {
	var arrSearchToolValidations = new Array();
	var oSearchTool = document.getElementById("SearchTool");
	var elementsToValidate = oSearchTool.getElementsByTagName("INPUT");
	
	// traverse the input elements of the HTML element "SearchTool" to find the elements that requires validation
	for(var i=0; i<elementsToValidate.length; i++) {
		var fieldidx = fieldValidationIndexes.indexOf(elementsToValidate[i].name);
		if(fieldidx != -1) {
			arrSearchToolValidations.push(arrValidations[fieldidx]);
		}
	}

	if(Validate(arrSearchToolValidations, true)) {
		HidePopupTool();
		SubmitAction(0, 0, 0, 0);
		SubmittedBySearchTool();
		DoSubmit();
	}
}

function NewNode(iTreeId, iNodeId, iTypeId, iEventId) {
	var oAction = new Object();
	oAction.TreeId = iTreeId;
	oAction.NodeId = iNodeId;
	oAction.TypeId = iTypeId;
	oAction.ActionId = iEventId;
//	GetPane().ShowNewNodeTool(iTreeId, iNodeId, iTypeId, iEventId);
	GetPane().ShowNewNodeTool(oAction);
}

function SubmitNewNode(oAction) {
	var iTreeId = oAction.TreeId;
	var iNodeId = oAction.NodeId;
	var iTypeId = oAction.TypeId;
	var iEventId = oAction.ActionId;

	// TODO: remove
	if(iTreeId==null) {
		// debug("I'M NOT SUPPOSED TO COME HERE ANYMORE");
		iEventId = oAction.ActionGroupId;
	}

	// validate and submit
	if(ValidateNewNode(iEventId)) {
		// TODO: remove
		if(iTreeId==null) {
			// not a tree action
			// debug("I'M NOT SUPPOSED TO COME HERE ANYMORE");
			GuibActionSubmitNewNode(oAction);
			return;
		}
		HidePopupTool();		
		SubmitAction(iTreeId, iNodeId, iTypeId, iEventId);
		SubmittedByNewNodeTool();
		DoSubmit();
	}
	else {
	//	NewNode(iTreeId, iNodeId, iTypeId, iEventId);
	}
}

function ValidateNewNode(iEventId) {
	var arrNewNodeValidations = new Array();
	var oNewNodeTool = document.getElementById("NewNodeTool"+iEventId);
	var inputElements = oNewNodeTool.getElementsByTagName("INPUT");
	
	// traverse the child nodes of the HTML element "NewNodeTool[iTypeId]" to find the elements that requires validation
	for(var i=0; i<inputElements.length; i++) {
		var fieldidx = fieldValidationIndexes.indexOf(inputElements[i].name);
		if(inputElements[i].name!=null && fieldidx != -1 /*arrValidations[inputElements[i].name]!=null*/) {
			arrNewNodeValidations.push(arrValidations[fieldidx]);
		}
	}
	if(Validate(arrNewNodeValidations, true)==true) {
//		debug("validated new node - action ID: "+iEventId);
		return true;
	}
//	debug("validation of new node FAILED - action ID: "+iEventId);
	return false;
}

function SubmitPopupWindow() {
	var arrPopupWindowValidations = new Array();
	var oPopupWindowContent = document.getElementById("PopupWindowContent");
	var inputElements = oPopupWindowContent.getElementsByTagName("INPUT");
	
	// traverse the input nodes of the HTML element to find the elements that requires validation
	for(var i=0; i<inputElements.length; i++) {
		var fieldidx = fieldValidationIndexes.indexOf(inputElements[i].name);
		if(inputElements[i].name!=null && fieldidx != -1 /*arrValidations[inputElements[i].name]!=null*/) {
			arrPopupWindowValidations.push(arrValidations[fieldidx]);
		}
	}
	
	// validate and submit
	if(Validate(arrPopupWindowValidations, true)==true) {
		HidePopupTool();
		SubmittedByPopupWindow();
		DoSubmit();
	}
//	else {
//		GetPane().ShowPopupWindow();
//	}
}

function SelectNode(oNode, e) {
	if ( e == null ) { e = event; }
	bFocusCausedByTreeNode = true;
	// SHIFT select?
	if(e!=null && e.shiftKey && e.ctrlKey==false && parent.Selection.Size()>0) {
		// get first node in selection
		var oStartNode = parent.Selection.Nodes[0];
		// get indexes of start and end nodes
		var iStartIndex=0, iEndIndex=0;
		for(var i=0; i<TreeNodes.length; i++) {
			if(TreeNodes[i].Node==oStartNode) {
				iStartIndex = i;
			}
			if(TreeNodes[i].Node==oNode) {
				iEndIndex = i;
			}
		}
		// swap indexes?
		if(iStartIndex>iEndIndex) {
			var iTemp = iEndIndex;
			iEndIndex = iStartIndex;
			iStartIndex = iTemp;
		}
		// clear current selection
		parent.Selection.Clear();
		// add nodes between the start and end nodes (incl)
		for(var i=iStartIndex; i<=iEndIndex; i++) {
			parent.Selection.Add(TreeNodes[i].Node, iPaneId); 
		}
	}
	else {
		parent.Selection.SelectNode(oNode, e, iPaneId);
	}
	if(Element.readAttribute(oNode, "Path") != null) { // CORE-4370
		UpdateLocationBar(oNode.Path);
//		debug("should show path '"+oNode.Path+"' now [1]");
	}
	UpdateStatusBar();
}

function UpdateSelectButton() {
	if ( GetSelectMode() == 1 ) {
		GetPane().submitBar.SetButtonEnabled("SelectTreeSelect",IsSelectOK());
	}
}

function PerformDefaultAction(oLink, e) {
	// don't perform any action if the node is disabled
	if(oLink.IsDisabled==true){
		// debug("cannot PerformDefaultAction on a disabled node");
		return;
	}
	// don't perform any action on double click if multiple nodes are selected (CORE-65)
	if(parent.Selection.Size()>1) {
		return;
	}
	var actions = GetAllowedActions(oLink.TypeId, oLink.PermissionId, oLink);
	
	// any actions?
	if(actions.length==0) {
		// debug("WARNING: No actions defined for " + oLink.TypeId);
		return;
	}
	var linkText = oLink.innerText;
	
	// select action?
//	if(GetSelectMode()==1 && GetParentPane()!=null) {
//		TreeSelect(oLink.TypeId, oLink.NodeId, linkText);
//		return;
//	}

	// find the "select" or "edit" action if it is allowed for the current user (action id = ActionIdEdit)
	var action = null;
	var selectMode = (GetSelectMode()==1);
	// for(var i in actions) {
	var i = 0;
	while ( action == null && i < actions.length ) {
		// CORE-385: "Select" is the only allowed default action in select trees
		if((actions[i].ActionId == ActionIdSelect) || (selectMode == false && actions[i].ActionId == ActionIdEdit)) {
			action = actions[i];
			//action.ActionIndex = i;
			break;
		}
		i++;
	}
	// edit action not allowed?
	if(action == null) {
//		debug("Edit is not allowed for the current user, or the action is not defined in the action list");
		return;
	}
	
	action.TreeId = oLink.TreeId;
	action.NodeId = oLink.NodeId;
	action.TypeId = oLink.TypeId;
	action.ParentTreeId = oLink.ParentTreeId;
	action.TreeTypeId = oLink.TreeTypeId;
	action.Forced = false;
	action.TrashAction = false;
	action.LinkText = linkText;

	// CORE-4316: only use first column value as prettyname for postback
	if(action.ActionId == ActionIdSelect && oLink.className.indexOf("ListRow") != -1) {
		action.LinkText = oLink.firstChild.innerText
	}

//	debug("perform default action: " + action.Action);
	PerformAction(action);
}

// CORE-1490
// returns a boolean indicating the node's selectability
function IsSelectable(oLink, bMultiSelect) {
	if ( GetSelectMode() != 1 ) {
		// cannot select when not in select-mode
		return false;
	}
	
	if ( oLink.IsDisabled ) {
		// cannot select disabled nodes
		return false;
	}

	if ( !oLink.SelectableNode ) {
		// CORE-3379 cannot select dimmed node
		return false;
	}
	
	var actions = GetAllowedActions(oLink.TypeId, oLink.PermissionId, oLink);
	if ( actions.length == 0 ) {
		// cannot select nodes with no actions defined
		return false;
	}

	for ( var i = 0; i < actions.length; i++ ) {	
		if ( actions[i].ActionId == ActionIdSelect ) {
			// found select-action
			if ( bMultiSelect ) {
				// if we are multi-selecting check for multi-selectability
				if ( actions[i].UseInMultiSelect != true ) {
					return false;
				}
			}
			return true;
		}
	}
	
	// fallthrough 
	return false;
}

// CORE-1490
// used for toggling select-button
function IsSelectOK() {
	if ( parent.Selection.Size() == 0 ) {
		return false;
	}
	
	var selectOK = true;
	for ( var i = 0; i < parent.Selection.Size(); i++ ) {
		selectOK = selectOK && IsSelectable(parent.Selection.Nodes[i],parent.Selection.Size()>1);
	}
	return selectOK;
}

function ShowActions(oLink, e) {
	if ( e == null ) { e = event; }
	if(parent.Selection.Size()<=0) {
		return;
	}
	if(GetPane().ActionList.style.visibility == "visible") {
		return;
	}
//	SelectNode(oLink);
	// don't show actions if the CTRL key or SHIFT key is down
	if(e.ctrlKey || e.shiftKey) {
		return;
	}
	// don't show actions if the node is disabled
	if(oLink.IsDisabled==true){
		// debug("cannot show actions on a disabled node");
		return;
	}
	
	var iTreeId = oLink.getAttribute("TreeId");
	var iNodeId = oLink.getAttribute("NodeId");
	var iTypeId = oLink.getAttribute("TypeId");
	var iParentTreeId = oLink.getAttribute("ParentTreeId");
	var iParentTypeId = oLink.getAttribute("ParentTypeId");
	var iTreeTypeId = oLink.getAttribute("TreeTypeId");
	var iPermissionId = oLink.getAttribute("PermissionId");
	
	
//	debug("showactions: "+iNodeId+", "+iTypeId);
	
	var sPermissionBitMask = GetPermissionBitMask(iPermissionId);
	if(sPermissionBitMask==null) {
		// debug("WARNING: No permission bit mask defined");
		return;
	}
	
	// filetree hack (again)
	var iActionsTypeId = iTypeId;
	var iNonGenericTypeId = iTypeId;
	if(iTreeTypeId==TypeIdFileTree) {
		iNonGenericTypeId = oLink.getAttribute("NonGenericTypeId");
	}
//	debug("ShowActions iActionsTypeId="+iActionsTypeId);

	// array of actions to show
	var arrActions = new Array();

	// array of parent action id's
	var arrParentActions = new Array();
	
	// get the allowed actions on the selected node
	var allowedActions = GetAllowedActions(iActionsTypeId, iPermissionId, oLink);
		
	// create array of actions
	// for(var i in allowedActions) {
	for ( var i = 0; i < allowedActions.length; i++ ) {
		// if the event has a parent event, don't show it
		if(allowedActions[i].ParentEventId!=0) {
			continue;
		}
		
		// check required changemode 
		// events with required changemode < 0 is always shown
		if(allowedActions[i].ChangeMode!=parent.ChangeMode && allowedActions[i].ChangeMode>=0) {
			continue;
		}
		
		// CORE-268: 
		// if the action is a parent action to other action(s), check if the user has access to
		// any of the child actions. if not, don't show the parent action.
		var isParentEvent = false;
		var childEventIsAllowed = false;
		// traverse all actions (regardless of permissions)
		// for(var j in arrEvents[iActionsTypeId]) {
		for ( var j = 0; j < arrEvents[iActionsTypeId].length; j++ ) {
			// is any action a child action of the current action?
			if(arrEvents[iActionsTypeId][j].ParentEventId != 0 && arrEvents[iActionsTypeId][j].ParentEventId == allowedActions[i].ActionId) {
				// the current action is definitively a parent action
				isParentEvent = true;
				// traverse all allowed actions to see if the user has access to one of the child actions
				// for(var k in allowedActions) {
				for ( var k = 0; k < allowedActions.length; k++ ) {
					if(allowedActions[k].IsSeperator) {
						// CORE-268 update: seperators don't count... don't want to display a submenu with only seperators!
						continue;
					}
					if(allowedActions[k].ActionId == arrEvents[iActionsTypeId][j].ActionId) {
						// the user has access to one of the child actions -> no need to continue, we know all we need to know
						childEventIsAllowed = true;
						break;
					}
				}
			}
		}
		// if the actions is a parent, and none of the child actions are allowed, don't show the parent action
		if(isParentEvent==true && childEventIsAllowed==false) {
			continue;
		}
		
		// multiselect - only show the actions all selected nodes have in common
		var bCanUseInMultiSelect = true;
		if(parent.Selection.Size()>1) {
			// can this action be used in multi select?
			if(allowedActions[i].UseInMultiSelect==0) {
				bCanUseInMultiSelect = false;
			}
			else {
				// check action ids; if different node types are selected, only show the actions with the same action id for all selected types
				for(var k=0; k<parent.Selection.Size(); k++) {
					// get next node in selection
					var oNode = parent.Selection.Nodes[k];
					var bCanUseAction = false;
					
					// get the allowed actions on the selected node
					var allowedNodeActions = GetAllowedActions(oNode.TypeId, oNode.PermissionId, oNode);
					
					// traverse the actions on the selected node
					// for(var j in allowedNodeActions) {
					for ( var j = 0; j < allowedNodeActions.length; j++ ) {
						// if a multiselect-action with the same action id exists on the selected node, it's okay to use the action
						if(allowedNodeActions[j].ActionId == allowedActions[i].ActionId && allowedNodeActions[j].UseInMultiSelect==1) {
							bCanUseAction = true;
							break;
						}
					}
					
					if(bCanUseAction==false) {
						bCanUseInMultiSelect = false;
						break;
					}
				}
			}
		}
		if(bCanUseInMultiSelect==false) {
			// cannot use this action in multi select
			continue;
		}
		// paste action; check that the copied nodes are allowed subnodes to the clicked node
		if(parent.ChangeMode==1 && allowedActions[i].ChangeMode==1) {
		
			var bCanPaste = true;		
			var iClipboardTypeId;
			var arrTypeIds;
			for(var j=0; j<parent.Clipboard.Size(); j++) {
				// cannot paste the node into the node itself (CORE-101)
				if(
					(parent.Clipboard.Nodes[j].TreeId == parent.Selection.Nodes[0].TreeId) && 
					(allowedActions[i].ActionId == ActionIdPasteInto)
					) {
					bCanPaste = false;
					break;
				}
	
				// make sure that the user does not try to paste a node into a child node of itself
				if(IsSubNode(parent.Clipboard.Nodes[j], oLink)) {
					bCanPaste = false;
					break;
				}

				iClipboardTypeId = parent.Clipboard.Nodes[j].TypeId;
				
				// filetree typeid hack
//				if(iTreeTypeId==TypeIdFileTree) {
//					iClipboardTypeId = TranslateTypeIdForFileTree(iClipboardTypeId);
//				}
				
				if(allowedActions[i].ActionId==ActionIdPasteInto) { // paste into
					arrAllowedTypeIds = arrAllowedSubnodes[iTreeTypeId][iActionsTypeId];
				}
				else { // paste above/below
					arrAllowedTypeIds = arrAllowedSubnodes[iTreeTypeId][iParentTypeId];
				}
				if(arrAllowedTypeIds==null) {
					// no types have been defined as allowed sub types
					bCanPaste = false;
				}
				else {
					var bCanContain = false;
					for(k=0; k<arrAllowedTypeIds.length; k++) {
						iAllowedTypeId = arrAllowedTypeIds[k];
						if(iClipboardTypeId == iAllowedTypeId) {
							bCanContain = true;
						}
					}
					bCanPaste = bCanContain;		
				}
				
				if(bCanPaste==false) {
					break;
				}
			}
			// filetree: cannot paste a node into same folder as the node currently resides in
			if(iTreeTypeId==TypeIdFileTree && bCanPaste==true) {
				for(j=0; j<parent.Clipboard.Size(); j++) {
					if(parent.Clipboard.Nodes[j].ParentTreeId==iTreeId) {
						bCanPaste = false;
					}
				}
			}

			if(bCanPaste==false) {
				continue;
			}
		}
		
		arrActions.push(allowedActions[i]);
	}
	if(arrActions.length<=0 && arrActions[-1]==null) {
		// no actions....
		return;
	}
	
	// find out if any of the actions have children
	// CORE-3504
	for ( var i = 0; i < arrActions.length; i++ ) {
		var j = 0;
		var foundChild = false;
		
		while ( j < allowedActions.length && !foundChild ) {
			foundChild = ( allowedActions[j].ParentEventId == arrActions[i].ActionId );
			j++;
		}
		
		arrParentActions[i] = ( foundChild ) ? 1 : 0;
	}
	
	var iXpos = event.clientX-2;
	var iYpos = event.clientY-2;

	if(event.keyCode==93) {
		var point = GetLinkCoordinates(oLink);
		// adjust with constants from constants.js
		iXpos = point.x + TreeActionAdjustHorizontal;
		iYpos = point.y + TreeActionAdjustVertical;
	}	
	oLink.LinkText = oLink.innerText;
	
	GetPane().ShowActions(oLink, arrActions, arrParentActions, iXpos, iYpos, false);
}

function FileTreeSelectFromRTE() {
//	debug("FileTreeSelectFromRTE: " + GetSelectorName().indexOf("swRTE_"));
	if(
		(parent.Selection.Nodes[0].TreeTypeId == TypeIdFileTree) &&
		(GetSelectorName().indexOf("swRTE_")==0)
		) {
		return true;
	}
	return false;
}

function GetAllowedActions(typeId, permissionId, node) {
	var allowedActions = new Array();
	// is the tree in select mode, and is the parent pane still open?
	if(GetSelectMode()==1 && GetParentPane()!=null) {
		// get the selector that spawned this tree
		var selector = null;
		if(FileTreeSelectFromRTE()) {
//			debug("filetree select from editor");
			selector = new Object();
			selector.TypeIds = new Array();
			// for(var i in parent.arrEmbeddableFiles) {
			for ( var i = 0; i < parent.arrEmbeddableFiles.length; i++ ) {
//				debug("adding " + parent.arrEmbeddableFiles[i] + " to selectable file types");
				selector.TypeIds.push(parent.arrEmbeddableFiles[i]);
			}
			selector.AllowMultiSelect = false;
		}
		else {
			selector = GetParentPane().GetSelector(GetSelectorName());
		}
		if(selector!=null && node!=null) {
			// wether or not to show the select action for the selected node(s)
			var showSelectAction = true;
			
			// multiselect disabled and more than one node selected?
//			debug("parent.Selection.Size() = " + parent.Selection.Size() + " selector.AllowMultiSelect = " + selector.AllowMultiSelect);
			if(parent.Selection.Size() > 1 && selector.AllowMultiSelect == false) {
				showSelectAction = false;
			}
			else {
				// traverse the current selection
				for(var j=0; j<parent.Selection.Size(); j++) {
					var canShow = false;
					// verify that the selected node(s) are valid for the selector
					for(var i=0; i<selector.TypeIds.length; i++) {
						if(
							(selector.TypeIds[i]==parent.Selection.Nodes[j].TypeId) ||
							// check NonGenericTypeId for file library
							(parent.Selection.Nodes[j].NonGenericTypeId!=null && selector.TypeIds[i]==parent.Selection.Nodes[j].NonGenericTypeId)
							) {
							canShow = true;
							break;
						}
					}
					// not a valid node type -> don't show the select action
					if(canShow==false) {
//						debug("cannot show select action");
						showSelectAction = false;
						break;
					}
				}
				// show the select action?
				if(showSelectAction==true) {
					// select action
					var selectAction = new Object;
					// localized action text from main page
					selectAction.Name = parent.TextSelectAction;
					selectAction.Action = "Select(iNodeId, iTypeId, '"+node.innerText+"');";
					selectAction.ActionIndex = -2;
					selectAction.ActionId = ActionIdSelect;
					selectAction.ParentEventId = 0;
					selectAction.ChangeMode = -1;
					selectAction.UseInMultiSelect = true;
					allowedActions.push(selectAction);

					var seperatorAction = new Object;
					seperatorAction.Name = '##seperator';
					seperatorAction.Action = '';
					seperatorAction.ActionIndex = -1;
					seperatorAction.ActionId = -1;
					seperatorAction.IsSeperator = true;
					seperatorAction.ParentEventId = 0;
					seperatorAction.ChangeMode = -1;
					seperatorAction.UseInMultiSelect = true;
					allowedActions.push(seperatorAction);


				}
			}
		}
	}
	
	// the defined actions
	var actions = arrEvents[typeId]
	// the permission bitmask
	var permissionBitMask = GetPermissionBitMask(permissionId);
	
	if(permissionBitMask==null || actions==null) {
		// debug("WARNING: No actions or permission bitmask defined");
		// return empty array
		return allowedActions;
	}
	
	// create array of allowed actions
	// CORE-467: keep track of seperators
	var lastActionWasSeperator = false;
	var firstActionAdded = false;
	
	// for(var i in actions) {
	for ( var i = 0; i < actions.length; i++ ) {
		// Check expand/collapse CORE-1719
		if ( node != null ) {
			if ( (actions[i].ActionId == ActionIdExpand || actions[i].ActionId == ActionIdCollapse) && node.OpenClosed == "None" ) {
				// dont show expand/collapse for nodes with no subtree
				continue;
			}
		}
		
		if(actions[i].IsSeperator!=true) {
			// Check permission bitmask		
			if(permissionBitMask.substr(actions[i].Placement,1) == 0) {
	//			debug("No permissions for action ID "+actionId);
				continue;
			}
			lastActionWasSeperator = false;
			firstActionAdded = true;
		}
		else {
			if(firstActionAdded==false) {
				// CORE-467: don't allow the first action to be a seperator
				continue;
			}
			if(lastActionWasSeperator==true) {
				// don't show two seperators in a row
				continue;
			}
			lastActionWasSeperator = true;
		}
//		debug("adding action id " + actionId + " ('" + actions[actionId].Name + "')");
		allowedActions.push(actions[i]);
//		allowedActions[i] = actions[i];
	}
	if(lastActionWasSeperator==true) {
//		debug("dont show seperator last");
		// CORE-467: don't allow the last action to be a seperator
		allowedActions.pop();
	}
//	debug("allowedActions.length = " + allowedActions.length);
	return allowedActions;
}

function GetChildActions(iTypeId, iParentActionTypeId, iPermissionId) {
	var arrActions = new Array();
	var allowedActions = GetAllowedActions(iTypeId, iPermissionId);
		
	// create array of actions
	// for(var i in allowedActions) {
	for ( var i = 0; i < allowedActions.length; i++ ) {
		// if the parent action id is equal to iParentActionTypeId, return the action
		if(allowedActions[i].ParentEventId==iParentActionTypeId) {
			arrActions.push(allowedActions[i]);
		}	
	}
	return arrActions;
}


// actions for all nodes in the trash can (using localized texts from main page)
var arrTrashActions = new Array();
// restore action
arrTrashActions[0] = new Array();
arrTrashActions[0].Name = parent.TextTrashActionMove;
arrTrashActions[0].Action = "Copy(iNodeId, iTypeId, "+ActionIdMove+");";
arrTrashActions[0].ActionId = ActionIdMove;
arrTrashActions[0].ActionIndex = 0;
arrTrashActions[0].ChangeMode = -1;
arrTrashActions[0].WarningTitle = "";
arrTrashActions[0].WarningText = "";
arrTrashActions[0].UseInMultiSelect = 1;
arrTrashActions[0].MultiSelectWarningTitle = "";
arrTrashActions[0].MultiSelectWarningText = "";
// delete action
arrTrashActions[1] = new Array();
arrTrashActions[1].Name = parent.TextTrashActionDelete;
arrTrashActions[1].Action = "SubmitAction(iTreeId, iNodeId, iTypeId, "+ActionIdDeleteFromTrash+");DoSubmit();";
arrTrashActions[1].ActionId = ActionIdDeleteFromTrash;
arrTrashActions[1].ActionIndex = 1;
arrTrashActions[1].ChangeMode = -1;
arrTrashActions[1].WarningTitle = parent.TextTrashActionDeleteWarningTitle; 
arrTrashActions[1].WarningText = parent.TextTrashActionDeleteWarningText;
arrTrashActions[1].UseInMultiSelect = 1;
arrTrashActions[1].MultiSelectWarningTitle = parent.TextTrashActionDeleteWarningTitleMulti;
arrTrashActions[1].MultiSelectWarningText = parent.TextTrashActionDeleteWarningTextMulti;

// actions for the trash can (using localized texts from main page)
var arrTrashCanActions = new Array();
// empty trash action
arrTrashCanActions[0] = new Array();
arrTrashCanActions[0].Name = parent.TextTrashActionEmpty;
arrTrashCanActions[0].Action = "SubmitAction(iTreeId, iNodeId, iTypeId, "+ActionIdDeleteFromTrash+");DoSubmit();";
arrTrashCanActions[0].ActionId = ActionIdDeleteFromTrash;
arrTrashCanActions[0].ActionIndex = 0;
arrTrashCanActions[0].ChangeMode = -1;
arrTrashCanActions[0].WarningTitle = parent.TextTrashActionEmptyWarningTitle;
arrTrashCanActions[0].WarningText = parent.TextTrashActionEmptyWarningText;
arrTrashCanActions[0].UseInMultiSelect = 0;
arrTrashCanActions[0].MultiSelectWarningTitle = "";
arrTrashCanActions[0].MultiSelectWarningText = "";

function ShowTrashActions(oLink) {
	if(event.ctrlKey || event.shiftKey) {
		return;
	}

	var iTreeId = oLink.getAttribute("TreeId");
	var iNodeId = oLink.getAttribute("NodeId");
	var iTypeId = oLink.getAttribute("TypeId");

	var iXpos = event.clientX-2;
	var iYpos = event.clientY-2;

	if(event.keyCode==93) {
		var point = GetLinkCoordinates(oLink);
		// adjust with constants from constants.js
		iXpos = point.x + TreeActionAdjustHorizontal;
		iYpos = point.y + TreeActionAdjustVertical;
	}	

	if(iNodeId==0 && iTypeId==0) {
		// show trash can actions
		GetPane().ShowActions(oLink, arrTrashCanActions, null, iXpos, iYpos, true);
		//Pane.prototype.ShowActions = function(oLink, arrActions, arrParentActions, iXpos, iYpos, bFromTrash) {
	}
	else {
		// show actions on a node in the trash
		GetPane().ShowActions(oLink, arrTrashActions, null, iXpos, iYpos, true);
	}
}

//function PerformAction(iTreeId, iNodeId, iTypeId, iActionId, bFromTrash, sLinkText, iParentTreeId, bForced) {
function PerformAction(oAction) {
	// TODO: remove:
	if(oAction.ActionIndex==null) {
		alert("oAction.ActionIndex==null, cannot perform action (tree.js)");
		return;
	}
//	debug("Tree.PerformAction "+oAction.TreeId);
	
	var iTreeId = oAction.TreeId;
	var iNodeId = oAction.NodeId;
	var iTypeId = oAction.TypeId;
	var iActionId = oAction.ActionId;
	var iActionIndex = oAction.ActionIndex;
	var bFromTrash = oAction.TrashAction;
	var sLinkText = oAction.LinkText;
	var iParentTreeId = oAction.ParentTreeId;
	var iTreeTypeId = oAction.TreeTypeId;
	var bForced = oAction.Forced;
	
	// CORE-3531
	var iSiteId = oAction.SiteId;
	var sExternalUrl = oAction.ExternalUrl;
	var sExternalUrlQueryString = oAction.ExternalUrlQueryString;

/*
	// filetree hack (again)
	var iActionsTypeId = iTypeId;
	if(oAction.TreeTypeId==TypeIdFileTree) {
//		iActionDeleteFromTrash = '1';
//		if (iActionId != iActionDeleteFromTrash) { 
		if(oAction.TrashAction==false) {
			iActionsTypeId = TranslateTypeIdForFileTree(iTypeId);
			iTypeId = iActionsTypeId;
		}
	}
*/
//	debug("PerformAction("+iTreeId+", "+iNodeId+", "+iTypeId+", "+iActionId+", "+bFromTrash+", "+sLinkText+", "+iParentTreeId+", "+bForced+");");
	GetPane().HideActions();
	// select action?
	if (sExternalUrl) {
		// Pseudo guesswork prerequisites	
		// ExternalUrl = "#{PreviewUrl}"
		// ExternalUrlQueryString = "preview=true&pageid=#{PageId}"
		// iNodeId = 12
				
		if(sExternalUrlQueryString) {
			sExternalUrl += "?" + sExternalUrlQueryString.interpolate({"PreviewUrl": Utility.GetSitePreviewUrlForQueryString(iSiteId, iNodeId)});
		}
		
		sExternalUrl = sExternalUrl.interpolate({
			"PreviewUrl": Utility.GetSitePreviewUrl(iSiteId)
		}).interpolate({ "PageId": iNodeId });
		
		// sExternalUrl #=> http://localhost:1337/via12.html?preview=true&pageid=12
		
		window.open(sExternalUrl);
		
		return;
	}
	
	if (iActionId==ActionIdSelect && GetSelectMode()==1 && GetParentPane()!=null) {
		TreeSelect(iTypeId, iNodeId, sLinkText);
	} else {
		var oAction;
		if (bFromTrash==false) {
			// action performed on "regular" node in the tree
			oAction = arrEvents[iTypeId][iActionIndex];
		} else {
			if(iNodeId==0 && iTypeId==0) {
				// action performed on the trash can node
				oAction = arrTrashCanActions[iActionIndex];
			}
			else {
				// action performed on a node in the trash
				oAction = arrTrashActions[iActionIndex];
			}
		}
		if(bForced==false) {
			// prompt the user?
//			if(ShowActionWarning(oAction, iTreeId, iNodeId, iTypeId, iActionId, bFromTrash, sLinkText, iParentTreeId)) {
			oAction.TreeId = iTreeId;
			oAction.NodeId = iNodeId;
			oAction.TypeId = iTypeId;
			oAction.ActionId = iActionId;
			oAction.ActionIndex = iActionIndex;
			oAction.LinkText = sLinkText;
			oAction.ParentTreeId = iParentTreeId;
			oAction.TreeTypeId = iTreeTypeId;
			oAction.TrashAction = bFromTrash;
			if(ShowActionWarning(oAction)) {
				return;
			}
		}
		parent.ChangeMode = oAction.ChangeMode;
		eval(oAction.Action);
	}
}

function TreeSelect(iTypeId, iNodeId, sLinkText) {
	// image select from RTE
	if(FileTreeSelectFromRTE()) {
		// debug("post back [" + iTypeId + ", " + iNodeId + " to RTE");
		// selector name = ["swRTE_" + frame name]
		var frameName = GetSelectorName().substr(6, GetSelectorName().length-6);
		// debug("- frameName = " + frameName);
		var editorFrame = GetParentPane().ChildPane.document.getElementById(frameName);
		editorFrame.contentWindow.insertImageCallBack(iNodeId);
		GetPane().Close();
		return;
	}

	var sSelectorName = GetSelectorName();
	var oSelector = GetParentPane().GetSelector(sSelectorName);
	if(oSelector!=null) {
		if ( GetPane().ClientsideSubmit ) {
			var multiSelect = parent.Selection.Size() > 1;
			var messageData = { selection: [] };
			
			for ( var i = 0; i < parent.Selection.Size(); i++ ) {
				if ( IsSelectable(parent.Selection.Nodes[i], multiSelect) ) {
					messageData.selection.push(parent.Selection.Nodes[i]);
				}
			}
			
			GetPane().PublishMessage("Pane.Select", messageData);
			GetPane().Close();
			return;
		}
	
		// post back to parent pane
		
		// refactored - CORE-1490
		var bMultiSelect = parent.Selection.Size() > 1;
		var submitCount = 0;
		var i = 0;
		
		while ( i < parent.Selection.Size() ) {
			var oLink = parent.Selection.Nodes[i];
			if ( IsSelectable(oLink,bMultiSelect) ) {
				var typeIdFormName = "sw" + sSelectorName + "_typeid" + submitCount;
				var nodeIdFormName = "sw" + sSelectorName + "_nodeid" + submitCount;
				GetParentPane().PostBack(typeIdFormName, parent.Selection.Nodes[i].TypeId);
				GetParentPane().PostBack(nodeIdFormName, parent.Selection.Nodes[i].NodeId);
				submitCount++;
			}
			i++;
		}
		
		if ( submitCount == 0 ) {
			// nothing submitted
			return;
		}
		
		var selectionSizeFormName = "sw" + sSelectorName + "_count";
		GetParentPane().PostBack(selectionSizeFormName, submitCount);
		
		if(submitCount==1) {
			GetParentPane().PostBack(sSelectorName + "_prettyname", sLinkText);
		}
		else {
			// localized text for "multiple nodes selected" (from main page)
			var prettyname = parent.TextSelectActionMultipleNodesSelected;
			GetParentPane().PostBack(sSelectorName + "_prettyname", prettyname);
		}
		GetParentPane().SetFocus();		
		
//		var selectionSizeFormName = "sw" + sSelectorName + "_count";
//		GetParentPane().PostBack(selectionSizeFormName, parent.Selection.Size());
//		for(var i=0; i<parent.Selection.Size(); i++) {
//			var typeIdFormName = "sw" + sSelectorName + "_typeid" + i;
//			var nodeIdFormName = "sw" + sSelectorName + "_nodeid" + i;
//			GetParentPane().PostBack(typeIdFormName, parent.Selection.Nodes[i].TypeId);
//			GetParentPane().PostBack(nodeIdFormName, parent.Selection.Nodes[i].NodeId);
//		}
//		GetParentPane().SetFocus();
//		if(parent.Selection.Size()==1) {
//			GetParentPane().PostBack(sSelectorName + "_prettyname", sLinkText);
//		}
//		else {
			// localized text for "multiple nodes selected" (from main page)
//			var prettyname = parent.TextSelectActionMultipleNodesSelected;
//			GetParentPane().PostBack(sSelectorName + "_prettyname", prettyname);
//		}

		if(oSelector.SubmitPane==true) {
			// Submit parent pane
			var oAction = new Object();
			oAction.ActionId = ActionIdSelect;
			oAction.TypeId = -1;
			oAction.NodeId = -1;
			oAction.CustomId = -1;
			oAction.GuibElementName = sSelectorName;				
			GetParentPane().ChildPane.GuibActionSubmit(oAction);
		}
		var parentPane = GetParentPane();

		GetPane().Close();
		
		// CORE-789 : Tekstfelter i vinduer kan "tabe" fokus #3 (problem med Selector-elementer)
		parentPane.Enable();		
	}
}

//function ShowActionWarning(oAction, iTreeId, iNodeId, iTypeId, iActionId, bFromTrash, sLinkText, iParentTreeId) {
function ShowActionWarning(oAction) {
	var sWarningText="", sWarningTitle="";
	if(parent.Selection.Size()>1 && oAction.UseInMultiSelect==1
		&& oAction.MultiSelectWarningTitle!="" && oAction.MultiSelectWarningText!=""
		) {
		sWarningTitle = oAction.MultiSelectWarningTitle;
		sWarningText = oAction.MultiSelectWarningText;
	}
	else if(oAction.WarningTitle!="" && oAction.WarningText!="") {
		sWarningTitle = oAction.WarningTitle;
		sWarningText = oAction.WarningText;
	}
	if(sWarningTitle!="" && sWarningTitle!="") {
//		debug("GetPane().ShowWarning("+sWarningTitle+", "+sWarningText+", "+iTreeId+", "+iNodeId+", "+iTypeId+", "+iActionId+", "+bFromTrash+", "+sLinkText+", "+iParentTreeId+");");

//		GetPane().ShowWarning(sWarningTitle, sWarningText, iTreeId, iNodeId, iTypeId, iActionId, bFromTrash, sLinkText, iParentTreeId);
		GetPane().ShowWarning(oAction, sWarningTitle, sWarningText);
		return true;
	}
	return false;
}

function SubmitAction(iTreeId, iNodeId, iTypeId, iEventId) {
//	debug("SubmitAction("+iTreeId+", "+iNodeId+", "+iTypeId+", "+iEventId+")");
	
	if(iEventId!=ActionIdPasteInto && iEventId!=ActionIdPasteAbove && iEventId!=ActionIdPasteBelow) {
		// add selection to form if not paste event (paste appends the clipboard)
		// TODO: add all nodes or only top nodes??
		AddNodesToForm(parent.Selection.Nodes, false);
		ClearClipboard();
	}
	else {
		// clear the clipboard without enabling any disabled nodes
		ClearClipboard(false);
	}
	
	AddFieldToForm("TreeId", iTreeId);
	AddFieldToForm("NodeId", iNodeId);
	AddFieldToForm("TypeId", iTypeId);
	AddFieldToForm("EventId", iEventId);
	SubmittedBySubmitButton();
	parent.Selection.Clear();
//	parent.Clipboard.Clear();
//	DoSubmit();
}

function OpenNode(iId) {
	AddFieldToForm("OpenCloseId", iId);
	AddFieldToForm("OpenCloseType", 1);

	SubmitAction(iId, 0, 0, 0);
	DoSubmit();
}

function CloseNode(iId) {
	AddFieldToForm("OpenCloseId", iId);
	AddFieldToForm("OpenCloseType", 2);

	SubmitAction(iId, 0, 0, 0);
	DoSubmit();
}

function ZoomTo(iTreeId, iNodeId, iTypeId) {
	AddFieldToForm("ZoomToNodeId", iNodeId);
	AddFieldToForm("ZoomToTypeId", iTypeId);
	AddFieldToForm("ZoomToTreeId", iTreeId);

	SubmitAction(iTreeId, iNodeId, iTypeId, 0);
	DoSubmit();
}
// clears the clipboard and optionally enables all nodes that 
// was disabled because of a "cut/move" operation
function ClearClipboard(enableNodes) {
	if(useCopyShortcutKeys) {
		if(enableNodes==null) {
			enableNodes = true;
		}
		if(enableNodes==true && parent.Clipboard.LastAction == ActionIdMove) {
			for(var i=0; i<parent.Clipboard.Size(); i++) {
				EnableNodeAndSubNodes(parent.Clipboard.Nodes[i]);
			}
		}
	}
	parent.Clipboard.Clear();
//	parent.ChangeMode = 0;
}
// disable a node in the tree
function DisableNode(node) {
	if(useCopyShortcutKeys==false) {
		return;
	}

//	debug("DisableNode " + node.NodeId);
	node.className = "TreeNode TreeNodeDimmed";
	node.IsDisabled = true;

	if(node.TreeId>2) {
		node.ondragstart = null;
		node.ondragend = null;
		node.ondrag = null;
		node.ondrop = null;
	}
}
// disable a node and all sub nodes in the tree
function DisableNodeAndSubNodes(node) {
	if(useCopyShortcutKeys==false) {
		return;
	}
	DisableNode(node);
	// for(var j in TreeNodes) {
	for ( var j = 0; j < TreeNodes.length; j++ ) {
		if(IsSubNode(node, TreeNodes[j].Node)) {
			DisableNode(TreeNodes[j].Node);
		}
	}
}
// enable a node in the tree
function EnableNode(node) {
	if(useCopyShortcutKeys==false) {
		return;
	}
	node.className = "TreeNode";
	node.IsDisabled = false;	

	if(node.TreeId>2) {
		node.ondragstart = new Function("DragStart(this)");
		node.ondragend = new Function("DragEnd(this)");
		node.ondrag = new Function("Drag(this)");
		node.ondrop = new Function("Drop(this)");
	}
}
// enable a node and all sub nodes in the tree
function EnableNodeAndSubNodes(node) {
	if(useCopyShortcutKeys==false) {
		return;
	}
	EnableNode(node);
	// for(var j in TreeNodes) {
	for ( var j = 0; j < TreeNodes.length; j++ ) {
		if(IsSubNode(node, TreeNodes[j].Node)) {
			EnableNode(TreeNodes[j].Node);
		}
	}
}
function Copy(iNodeId, iTypeId, iEventId, iParentTreeId) {
	if(parent.Selection.Size()>0) {
		// clear the clipboard
		ClearClipboard();
		for(var i=0; i<parent.Selection.Size(); i++) {
			parent.Clipboard.Add(parent.Selection.Nodes[i]);
			// disable the nodes on move action (only if not dragging nodes)
//			debug("Copy - parent.dragObj = " + parent.dragObj);
			if(iEventId == ActionIdMove && parent.dragObj==null) {
				DisableNodeAndSubNodes(parent.Selection.Nodes[i]);
			}
		}
		parent.Clipboard.LastAction = iEventId;
//		debug(parent.Clipboard.ToString());
		parent.ChangeMode = 1;
		if(useCopyShortcutKeys) {
			// clear selection on move action (the selected nodes have been disabled)
			if(iEventId == ActionIdMove) {
				parent.Selection.Clear();
			}
		}
	}
	/*
	else {
		parent.Clipboard.Copy(iNodeId, iTypeId, iEventId, iParentTreeId);
	}
	*/
}

function Paste(iTreeId, iNodeId, iTypeId, iEventId) {
	
	if(parent.Clipboard.Size()<=0) {
		return;
	}

	AddNodesToForm(parent.Clipboard.Nodes, false);
	AddFieldToForm("LastEventId", parent.Clipboard.LastAction);
	
//	parent.Clipboard.Clear();
//	parent.Selection.Clear();
	parent.ChangeMode = 0;

	SubmitAction(iTreeId, iNodeId, iTypeId, iEventId);
	DoSubmit();
}

function AddNodesToForm(arrNodes, bAddChildNodes) {
//	debug("adding nodes to form...");
	if(arrNodes.length<=0) {
		return 0;
	}

//	var output = "BEFORE: ";
//	for(var i=0; i<arrNodes.length; i++) {
//		output += i + ". (" + arrNodes[i].TypeId+","+arrNodes[i].NodeId+") ";
//	}
//	debug(output);

	// sort nodes by placement in the tree (top to bottom)
	arrNodes = arrNodes.sort(SortNodesByPlacement);

//	output = "AFTER: ";
//	for(var i=0; i<arrNodes.length; i++) {
//		output += i + ". (" + arrNodes[i].TypeId+","+arrNodes[i].NodeId+") ";
//	}
//	debug(output);	

	var counter = 0;
	for(var i=0; i<arrNodes.length; i++) {
		var bAddNode = true;
		if(bAddChildNodes==false) {
			for(var j=0; j<arrNodes.length; j++) {
				if(IsSubNode(arrNodes[j], arrNodes[i])) {
					bAddNode = false;
				}
			}
		}
		if(bAddNode) {
//			debug("adding node: "+arrNodes[i].NodeId+", "+arrNodes[i].TypeId + " - offsetTop:" + arrNodes[i].offsetTop);			
			AddFieldToForm("PasteNodeId"+counter, arrNodes[i].NodeId);
			AddFieldToForm("PasteTypeId"+counter, arrNodes[i].TypeId);
			counter++;
		}
	}
	
	AddFieldToForm("PasteCounter", counter);
	return counter;
}

// #################### DRAGGING ######################### //
function DragStart(oNode) {
	// cannot drag a disabled node
	if(oNode.IsDisabled==true) {
		return;
	}
	if(parent.Selection.Contains(oNode)==false) {
		SelectNode(oNode);
	}
	parent.DragStart(oNode, event);
}
function DragEnd(oNode) {
	// cannot drag a disabled node
	if(oNode.IsDisabled==true) {
		return;
	}
	parent.DragEnd(oNode, event);
}
function Drag(oNode) {
	// cannot drag a disabled node
	if(oNode.IsDisabled==true) {
		return;
	}
	parent.Drag(event)
}
function Drop(oNode){
	// cannot drag a disabled node
	if(oNode.IsDisabled==true) {
		return;
	}
	parent.Drop(oNode, event, iPaneId)
}
function DragOver(oNode){
	// cannot drag a disabled node
	if(oNode.IsDisabled==true) {
		return;
	}
	parent.DragOver(oNode, event, iPaneId);
}
function DragEnter(oNode){
	// cannot drag a disabled node
	if(oNode.IsDisabled==true) {
		return;
	}
	parent.DragEnter(oNode, event);
}
function DragLeave(oNode){
	// cannot drag a disabled node
	if(oNode.IsDisabled==true) {
		return;
	}
	parent.DragLeave(oNode, event);
}

// ################## MULTISELECT ###################### //
var MultiSelectRectangle = document.createElement("DIV");
MultiSelectRectangle.style.cssText = "position:absolute;";
MultiSelectRectangle.className = "MultiSelectRectangle";
MultiSelectRectangle.style.visibility = "hidden";
//MultiSelectRectangle.style.zIndex = 10000;

// start drawing the multiselect dotted rectangle
function StartMultiSelect() {
	// has the user clicked inside the form container?
	var oTreeContainer = document.getElementById("formcontainer");
	if(event.clientY<oTreeContainer.offsetTop) {
		// user clicked in a menu bar
		return;
	}
	if(event.clientX>oTreeContainer.offsetLeft+oTreeContainer.offsetWidth-18) {
		// user clicked the scroll bar
		return;
	}
	// CORE-112, CORE-4346: horizontal scrollbar visible?
	if (Utility.HorizontalScrollbarVisible(oTreeContainer)) {
		// user clicked the (horizontal) scroll bar?
		if(event.clientY > (oTreeContainer.offsetTop+oTreeContainer.offsetHeight - 18)) {
			return;
		}
	}
	//	if(document.getElementById("FormContainer").offsetWidth>=GetPane().FrameContainer.offsetWidth) {
//		debug("Pane content is too wide, vertical scrollbar is visible");
//	}
	// append the multiselect reckangle to the document
	document.body.appendChild(MultiSelectRectangle);
	// save start position
	MultiSelectRectangle.StartLeft = event.clientX;
	MultiSelectRectangle.StartTop = event.clientY;
	// set start position and size
	MultiSelectRectangle.style.left = event.clientX+"px";
	MultiSelectRectangle.style.top = event.clientY+"px";
	MultiSelectRectangle.style.width = "1px";
	MultiSelectRectangle.style.height = "1px";
	// show rectangle
	MultiSelectRectangle.style.visibility = "visible";	
//	event.cancelBubble = true;
}
// hide the multiselect rectangle
function StopMultiSelect() {
	if(MultiSelectRectangle.style.visibility == "visible") {
		MultiSelectRectangle.style.visibility = "hidden";
		// remove rectangle from document body
		document.body.removeChild(MultiSelectRectangle);
	}
}
// (re)draw multiselect rectangle
function DrawMultiSelect() {
	if(MultiSelectRectangle.style.visibility == "hidden") {
		// multiselect rectangle is not shown - return
		return;
	}
	// out of formcontainer bounds?
	if(event.clientY<=document.getElementById("formcontainer").offsetTop) {
		return;
	}
	// CORE-112: vertical scrollbar visible?
	if(document.getElementById("treecontainer").offsetWidth >= GetPane().Pane.offsetWidth) {
		if(event.clientY > (document.getElementById("formcontainer").offsetTop+document.getElementById("formcontainer").offsetHeight - 18)) {
			return;
		}
	}
	if((MultiSelectRectangle.offsetLeft+event.clientX-MultiSelectRectangle.StartLeft)>=(document.getElementById("formcontainer").offsetWidth-parent.iScrollBarWidth)) {
		return;
	}
	// set multiselect rectangle x position
	if(MultiSelectRectangle.StartLeft < event.clientX) {
		MultiSelectRectangle.style.left = MultiSelectRectangle.StartLeft+"px";
	}
	else {
		MultiSelectRectangle.style.left = event.clientX+"px";
	}
	// set multiselect rectangle y position
	if(MultiSelectRectangle.StartTop < event.clientY) {
		MultiSelectRectangle.style.top = MultiSelectRectangle.StartTop+"px";
	}
	else {
		MultiSelectRectangle.style.top = event.clientY+"px";
	}
	// set multiselect rectangle width and height
	MultiSelectRectangle.style.width = Math.abs(event.clientX-MultiSelectRectangle.StartLeft)+"px";
	MultiSelectRectangle.style.height = Math.abs(event.clientY-MultiSelectRectangle.StartTop)+"px";

	// get multiselect rectangle boundaries
	var iLeftX = MultiSelectRectangle.offsetLeft + document.getElementById("formcontainer").scrollLeft;
	var iRightX = iLeftX + MultiSelectRectangle.offsetWidth;
	var iTopY = MultiSelectRectangle.offsetTop-document.getElementById("formcontainer").offsetTop+document.getElementById("formcontainer").scrollTop;
	var iBottomY = iTopY + MultiSelectRectangle.offsetHeight;

	// select all nodes that are touched by the multiselect rectangle
	var oNode;
	parent.Selection.Clear();
	for(var i=0; i<TreeNodes.length; i++) {
		oNode = TreeNodes[i].Node;
		var iNodeLeftX, iNodeRightX, iNodeTopY, iNodeBottomY;
		if(oNode.IsTreeViewNode) {
			iNodeLeftX = oNode.offsetLeft;
			iNodeRightX = oNode.offsetLeft+oNode.offsetWidth;
			iNodeTopY = oNode.offsetTop;
			iNodeBottomY = oNode.offsetTop+oNode.offsetHeight;
		}
		else {
			// listview nodes are wrapped in a couple of extra DIV elements
			// TODO: new list view -> change something here? It seems to work as it is....
			iNodeLeftX = oNode.parentNode.parentNode.offsetLeft;
			iNodeRightX = oNode.parentNode.parentNode.offsetLeft+oNode.offsetWidth;
			iNodeTopY = oNode.parentNode.parentNode.offsetTop;
			iNodeBottomY = oNode.parentNode.parentNode.offsetTop+oNode.offsetHeight;
		}
		// if the node is touched by the rectangle, add it to selection				
		if(
			(iNodeLeftX>iLeftX &&iNodeLeftX<iRightX) || 
			(iNodeRightX>iLeftX && iNodeRightX<iRightX) ||
			(iNodeLeftX<iLeftX && iNodeRightX>iRightX)
			) {
			if(iNodeTopY>iTopY && iNodeTopY<iBottomY || iNodeBottomY>iTopY && iNodeBottomY<iBottomY) {
			/*
				if( 
					// don't include the trash in multiselect
					(oNode.TypeId==0 && oNode.NodeId==0) ||
					// don't combine trash nodes and "regular" nodes in multiselection
					(oNode.IsTrashNode==true && parent.Selection.Size()>0 && parent.Selection.Nodes[0].IsTrashNode==false) ||
					(oNode.IsTrashNode==false && parent.Selection.Size()>0 && parent.Selection.Nodes[0].IsTrashNode==true)
					) {
					continue;
				}
				else {
				*/
					parent.Selection.Add(oNode, iPaneId);
//				}
			}
		}
	}
	if(IsTreeView==false) {
		UpdateLocationBar("");
	}
	UpdateStatusBar();
}

// misc. status variables for handling keyboard input in trees
var sShiftSelectDirection = "";
var oActionInFocus = null;
var oChildActionInFocus = null;
var bUseCircularMovementInTrees = false;
// handle keyboard input in trees

// TODO: HANDLE THIS THROUGH SHORTCUTMANAGER!! (Interference with ctrl-alt-s shortcut)...
function TreeOnKeyDown(e) {
	e = ( e ) ? e : event;
	
	switch(e.keyCode) {
		// key UP (38) or key DOWN (40): (de)select node
		case 38:
		case 40:
			// move focus within 2nd level context menu?
			if(GetPane().ChildActionList.style.visibility == "visible") {
				// get UL object of 2nd level context menu
				var oActionList = GetPane().ChildActionList.firstChild;
				if(e.keyCode==38) {
					// move focus to previous 2nd level action
					oChildActionInFocus = SetFocusOnAction("UP", oChildActionInFocus, oActionList);
				}
				else {
					// move focus to next 2nd level action
					oChildActionInFocus = SetFocusOnAction("DOWN", oChildActionInFocus, oActionList);
				}
				return true;
			}
		
			// move focus within 1st level context menu?
			if(GetPane().ActionList.style.visibility == "visible") {
				// get UL object of 1st level context menu
				var oActionList = GetPane().ActionList.firstChild;
				if(e.keyCode==38) {
					// move focus to previous 1st level action
					oActionInFocus = SetFocusOnAction("UP", oActionInFocus, oActionList);
				}
				else {
					// move focus to next 1st level action
					oActionInFocus = SetFocusOnAction("DOWN", oActionInFocus, oActionList);
				}
				return true;
			}

			// no nodes selected?
			var oNode = null;
			if(parent.Selection.Size()<=0) {
				if(e.keyCode==38) {
					if(bUseCircularMovementInTrees==false) {
						return false; 
					}
					// select bottom node in tree
					oNode = TreeNodes[TreeNodes.length-1].Node;
				}
				else {
					// select top node in tree
					oNode = TreeNodes[0].Node;
				}
				SelectNode(oNode);
			}
			else {
				// get last selected node
				oNode = parent.Selection.Nodes[parent.Selection.Size()-1];
				// clear selection if SHIFT is not pressed
				if(e.shiftKey==false) {
					parent.Selection.Clear();
				}
				// get the position of the last selected node in the TreeNodes array
				var iNodePos = 0;
				for(var i=0; i<TreeNodes.length; i++) {
					if(TreeNodes[i].Node==oNode) {
						iNodePos = i;
						break;
					}
				}
				// are we actually in the process of deselecting previously selected nodes (multiselected by using SHIFT+UP/DOWN)
				if(parent.Selection.Size()>1 && parent.Selection.Contains(TreeNodes[iNodePos].Node) && 
					((e.keyCode==38 && sShiftSelectDirection=="DOWN") || (e.keyCode==40 && sShiftSelectDirection=="UP"))
					) {
					// remove node from selection
					parent.Selection.Remove(TreeNodes[iNodePos].Node);
				}
				else {
					if(e.keyCode==38) {
						// key UP pressed; select the node above the last selected node
						var foundNode = false;
						iNextNodePos = iNodePos;
						while(foundNode==false) {
							iNextNodePos = iNextNodePos-1;
							if(iNextNodePos<0) {
								if(bUseCircularMovementInTrees==false) {
									iNextNodePos = 0; 
								}
								else {
									// circular movement in tree
									iNextNodePos = TreeNodes.length-1;
								}
							}
							if(TreeNodes[iNextNodePos]!=null && TreeNodes[iNextNodePos].Node.IsDisabled!=true) {
								foundNode = true;
							}
						}
						sShiftSelectDirection = "UP";
					}
					else {
						// key DOWN pressed; select the node belov the last selected node
						var foundNode = false;
						iNextNodePos = iNodePos;
						while(foundNode==false) {
							iNextNodePos = iNextNodePos+1;
							if(iNextNodePos>=TreeNodes.length) {
								if(bUseCircularMovementInTrees==false) {									
									iNextNodePos = TreeNodes.length-1;
									// if the last node in the tree is disabled, the next selected node should 
									// be the same as the previously selected node
									if(TreeNodes[iNextNodePos].Node.IsDisabled==true) {
										iNextNodePos = iNodePos;
									}
								}
								else {
									// circular movement in tree
									iNextNodePos = 0;
								}
							}
							if(TreeNodes[iNextNodePos]!=null && TreeNodes[iNextNodePos].Node.IsDisabled!=true) {
								foundNode = true;
							}
						}
						sShiftSelectDirection = "DOWN";
					}
					// clear selection if the last selected node was the trash can or the solution (cannot include those in multiselection)
					if(TreeNodes[iNodePos].Node.TreeId<=2) {
						parent.Selection.Clear();
					}
					// clear selection if the selected node is the trash can or the solution (cannot include those in multiselection)
					if(TreeNodes[iNextNodePos].Node.TreeId<=2) {
						parent.Selection.Clear();
					}
					parent.Selection.Add(TreeNodes[iNextNodePos].Node, iPaneId);
					
					oNode = TreeNodes[iNextNodePos].Node;
				}
			}
			if(oNode!=null) {
				ScrollTo(oNode);				
				if(oNode.IsTreeViewNode==false) {
					UpdateLocationBar(oNode.Path);
//					debug("should show path '"+TreeNodes[iNextNodePos].Node.Path+"' ("+TreeNodes[iNextNodePos].Node.innerText+") now");
				}
				UpdateStatusBar();
			}
			break;
		case 36:
		case 35:
			if(GetPane().ActionList.style.visibility != "visible") {
				// HOME / END: select first/last node (only if the context menu is not visible)
				if(e.keyCode==35) {
					// select bottom node in tree
					oNode = TreeNodes[TreeNodes.length-1].Node;
				}
				else {
					// select top node in tree
					oNode = TreeNodes[0].Node;
				}
				SelectNode(oNode);
				ScrollTo(oNode);
				if(oNode.IsTreeViewNode==false) {
					UpdateLocationBar(oNode.Path);
				}
				UpdateStatusBar();
			}
			break;
		case 27:
			// esc: hide actions
			if(GetPane().ChildActionList.style.visibility == "visible") {
				GetPane().HideChildActions();
				oChildActionInFocus = null;
			}
			else if(GetPane().ActionList.style.visibility == "visible") {
				GetPane().HideActions();
				oActionInFocus = null;
				oChildActionInFocus = null;
			}
			else {
				ClearClipboard();
				return false;
			
				if(useCopyShortcutKeys) {
					// clear clipboard and enable all nodes that was "cut"
					ClearClipboard();
				}
			}
			break;
		case 37:
			// key LEFT: hide child actions or close the currently selected node (only in treeview)
			if(GetPane().ChildActionList.style.visibility == "visible") {
				GetPane().HideChildActions();
				oChildActionInFocus = null;
			}
			// close node? only if the context menu is not visible and the selection contains only one node
			else if(GetPane().ActionList.style.visibility != "visible" && parent.Selection.Size()==1) {
				// get the selected node
				var currentNode = parent.Selection.Nodes[0];
				// is the node in a tree view?
				if(currentNode.IsTreeViewNode) {
					// is the node open? check the classname of the "+/-" link
					if(currentNode.previousSibling.firstChild.className=="OpenNode") {
						// save the node id (to select the node again after submit)
						GetPane().SelectedTreeNodeId = currentNode.TreeId;
						// close the node
						CloseNode(currentNode.TreeId);
					}
				}
			}
			break;
		case 39:
			// key RIGHT: show child actions or open the currently selected node (only in treeview)
			if(oActionInFocus!=null && oActionInFocus.firstChild.IsParentAction==true) {
				GetPane().ShowChildActions(oActionInFocus.firstChild);
				// set focus on first child action
				var oActionList = GetPane().ChildActionList.firstChild;
				oChildActionInFocus = SetFocusOnAction("DOWN", oChildActionInFocus, oActionList);
			}
			// open node? only if the context menu is not visible and the selection contains only one node
			else if(GetPane().ActionList.style.visibility != "visible" && parent.Selection.Size()==1) {
				// get the selected node
				var currentNode = parent.Selection.Nodes[0];
				// is the node in a tree view?
				if(currentNode.IsTreeViewNode) {
					// is the node closed? check the classname of the "+/-" link
					if(currentNode.previousSibling.firstChild.className=="ClosedNode") {
						// save the node id (to select the node again after submit)
						GetPane().SelectedTreeNodeId = currentNode.TreeId;
						// open the node
						OpenNode(currentNode.TreeId);
					}
				}
			}
			break;
		case 13:
			// enter: perform action in focus
			if(oChildActionInFocus!=null) {
				// perform 2nd level action
				GetPane().PerformAction(oChildActionInFocus.firstChild);
				// prevent this action from being performed on next "enter"
				oChildActionInFocus = null;
			}
			else if(oActionInFocus!=null) {
				if(oActionInFocus.firstChild.IsParentAction==true) {
					GetPane().ShowChildActions(oActionInFocus.firstChild);
					// set focus on first child action
					var oActionList = GetPane().ChildActionList.firstChild;
					oChildActionInFocus = SetFocusOnAction("DOWN", oChildActionInFocus, oActionList);
				}
				else {
					// perform 1st level action
					GetPane().PerformAction(oActionInFocus.firstChild);
					// prevent this action from being performed on next "enter"
					oActionInFocus = null;
					// ...and just to be sure:
					oChildActionInFocus = null;
				}
			}
			break;
		case 93:
			// context menu key: show context menu if it is not already visible
			if(parent.Selection.Size()>0 && GetPane().ActionList.style.visibility == "hidden") {
				if(parent.Selection.Nodes[0].IsTrashNode) {
					ShowTrashActions(parent.Selection.Nodes[parent.Selection.Size()-1]);
				}
				else {
					ShowActions(parent.Selection.Nodes[parent.Selection.Size()-1]);
				}
			}
			break;
		case 83:
			// "s" - display search tool on CTRL+S 
			// TODO: "f": event.ctrlKey = 70 (spawns IE search tool)
			if(e.ctrlKey && !e.altKey && !e.shiftKey) { // CORE-3891 specific check for altkey
				debug("CTRL-S");
				if(GetPane().PopupTool.style.visibility != "visible") {
					GetPane().ShowSearchTool();
				}
			}
			else {
				return false;
			}
			break;
		case 46:
			// "DEL"
			// debug("delete " + parent.Selection.Size() + " nodes");
			if(parent.Selection.Size()>0) {
				var deleteAction = GetActionForSelectedNodes(ActionIdDelete);
				if(deleteAction!=null) {
					deleteAction = AddActionProperties(deleteAction, parent.Selection.Nodes[0], false);	
					PerformAction(deleteAction);
				}
			}
			break;
		case 67:
			if(useCopyShortcutKeys==false) {
				return false;
				break;
			}
			// "c" - copy selection on CTRL+C
			if(e.ctrlKey==true && parent.Selection.Size()>0) {
				// debug("copy " + parent.Selection.Size() + " nodes");
				var copyAction = GetActionForSelectedNodes(ActionIdCopy);
				if(copyAction!=null) {
					copyAction = AddActionProperties(copyAction, parent.Selection.Nodes[0], false);
					PerformAction(copyAction);
				}
			}
			break;
		case 88:
			if(useCopyShortcutKeys==false) {
				return false;
				break;
			}
			// "x" - cut/move selection on CTRL+X
			if(e.ctrlKey==true && parent.Selection.Size()>0) {
				// debug("cut " + parent.Selection.Size() + " nodes");
				var moveAction = GetActionForSelectedNodes(ActionIdMove);
				if(moveAction!=null) {
					moveAction = AddActionProperties(moveAction, parent.Selection.Nodes[0], false);
					PerformAction(moveAction);
				}
			}
			break;
		case 86:
			if(useCopyShortcutKeys==false) {
				return false;
				break;
			}
			// "v" - paste selection on CTRL+V
			if(e.ctrlKey==true && parent.Clipboard.Size()>0 && parent.Selection.Size()==1) {
				// debug("paste " + parent.Clipboard.Size() + " nodes");
				var pasteAction = GetActionForSelectedNodes(ActionIdPasteInto);
				if(pasteAction!=null) {
					// only paste if the nodes in the clipboard are allowed below the selected node
					if(CanPasteNodes(parent.Selection.Nodes[0], parent.Clipboard.Nodes, ActionIdPasteInto)) {
						pasteAction = AddActionProperties(pasteAction, parent.Selection.Nodes[0], false);
						PerformAction(pasteAction);
					}
				}
			}
			break;		
		default:
//			debug(e.keyCode);
			return false;
			break;
	}
	
	// this prevents the scrollbar from reacting on the keydown:
//	e.cancelBubble = true;
	e.returnValue = false;
	
	return true;
}
// wether or not a collection of nodes can be pasted on a given node
function CanPasteNodes(parentNode, nodeCollection, actionId) {
	if(nodeCollection.length<=0) {
		// debug("WARNING: CanPasteNodes nodeCollection.length<=0");
		return false;
	}
	if(actionId!=ActionIdPasteInto && actionId!=ActionIdPasteAbove && actionId!=ActionIdPasteBelow) {
		// debug("WARNING: CanPasteNodes invalid action");
		return false;
	}
	
	var canPaste = true;
	var arrAllowedTypeIds;
	var treeTypeId = parentNode.TreeTypeId;
	var nodeTypeId = parentNode.TypeId;
	var parentNodeTypeId = parentNode.ParentTypeId;
	for(var j=0; j<nodeCollection.length; j++) {

		// make sure that the user does not try to paste a node into a child node of itself
		if(IsSubNode(nodeCollection[j], parentNode)) {
			return false;
		}

		// get the sub node type
		var subNodeTypeId = nodeCollection[j].TypeId;
		
		// get allowed sub nodes
		if(arrAllowedSubnodes[treeTypeId]==null) {
			// no types have been defined for this tree type
			// debug("WARNING: No tree sub types defined");
			return false;
		}
		var arrAllowedTypeIds;
		if(actionId==ActionIdPasteInto) { // paste into
			arrAllowedTypeIds = arrAllowedSubnodes[treeTypeId][nodeTypeId];
		}
		else { // paste above/below
			arrAllowedTypeIds = arrAllowedSubnodes[treeTypeId][parentNodeTypeId];
		}
		if(arrAllowedTypeIds==null) {
			// no types have been defined as allowed sub types
			// debug("WARNING: No sub types defined for parent node");
			return false;
		}
		else {
			var canContain = false;
			for(k=0; k<arrAllowedTypeIds.length; k++) {
				var allowedTypeId = arrAllowedTypeIds[k];
//				debug(subNodeTypeId +"=="+ allowedTypeId);
				if(subNodeTypeId == allowedTypeId) {
					canContain = true;

					break;
				}
			}
			canPaste = canContain;		
		}
		
		if(canPaste==false) {
//			debug("Cannot paste type " + subNodeTypeId);
			break;
		}
	}
	return canPaste;
}
// get a specific action for the nodes in the selection 
// (returns null if the action is not defined for all nodes in the selection)
function GetActionForSelectedNodes(actionId) {
	return GetActionForNodes(actionId, parent.Selection);
}
// get a specific action for the nodes in the clipboard
// (returns null if the action is not defined for all nodes in the clipboard)
function GetActionForClipboardNodes(actionId) {
	return GetActionForNodes(actionId, parent.Clipboard);
}
// get a specific action for the nodes in a given the selection or clipboard object
function GetActionForNodes(actionId, collection) {
	var action = null;
	for(var i=0; i<collection.Size(); i++) {
		var actions = GetAllowedActions(collection.Nodes[i].TypeId, collection.Nodes[i].PermissionId);
		// for(var j in actions) {
		for ( var j = 0; j < actions.length; j++ ) {
			if(actions[j].ActionId==actionId) {
				if(action==null) {
					action = actions[j];
				}
				break;
			}
		}				
		if(action==null) {
			break;
		}
	}
	return action;
}
// adds the properties to an action object required to invoke the action
function AddActionProperties(action, treenode, forced) {
	action.TreeId = treenode.TreeId;
	action.NodeId = treenode.NodeId;
	action.TypeId = treenode.TypeId;
	action.ParentTreeId = treenode.ParentTreeId;
	action.TreeTypeId = treenode.TreeTypeId;
	action.Forced = forced;
	// TODO: trash action?
	action.TrashAction = false;
	// TODO: link text?
	action.LinkText = "";
	
	return action;
}

// set focus on a action
function SetFocusOnAction(sDirection, oActionInFocus, oActionList) {
	var oAction = null;
	for(var i=0; i<oActionList.childNodes.length; i++) {
		if(oActionList.childNodes[i]==oActionInFocus) {
			// remove "Active" class on LI and A
			oActionList.childNodes[i].className = "";
			if(sDirection=="UP") {
				while(oAction==null) {
					i--;
					if(i<0) {
						i = oActionList.childNodes.length-1;
					}
					if(oActionList.childNodes[i].className.indexOf("Seperator")<0) {
						oAction = oActionList.childNodes[i];
					}
					/*
					if(i>=0) {
						// select previous action
						oAction = oActionList.childNodes[i];
					}
					else {
						// select bottom action
						oAction = oActionList.childNodes[oActionList.childNodes.length-1];
					}
					*/
				}
			}
			else {
				while(oAction==null) {
					i++;
					if(i>=oActionList.childNodes.length) {
						i = 0;
					}
					if(oActionList.childNodes[i].className.indexOf("Seperator")<0) {
						oAction = oActionList.childNodes[i];
					}
					/*
					if(i<oActionList.childNodes.length) {
						// select next action
						oAction = oActionList.childNodes[i];
					}
					else {
						// select top action
						i = 0;
						oAction = oActionList.childNodes[i];
					}
					*/
				}
			}
			break;
		}
	}
	if(oAction==null) {
		// no action is in focus - select the first action
		oAction = oActionList.firstChild;
	}
	// set "Active" class on LI and A
	oAction.className = "Active";
	return oAction;
}

function UpdateLocationBar(sNodePath) {
	if(parent.Selection.Size()>1) {
//		SetLocationBarText(parent.Selection.Size()+" objects selected");
		SetLocationBarText("");
	}
	else {
		SetLocationBarText(sNodePath);
	}
}

function SetLocationBarText(sText) {
	var locationBar = document.getElementById("LocationBar");
	if ( locationBar ) {
		locationBar.innerText = sText;
	}
}

function UpdateStatusBar() {
/*
var TextSingleNodeSelected = "1 object selected";
var TextMultipleNodesSelected = "%d objects selected";
var TextSingleNode = "1 object";
var TextMultipleNodes = "%d objects";</script>
*/
	if(parent.Selection.Size()==1) {
//		GetPane().SetStatusBarText("1 object selected");
		GetPane().SetStatusBarText(parent.TextSingleNodeSelected);
	}
	else if(parent.Selection.Size()>1) {
		GetPane().SetStatusBarText(parent.TextMultipleNodesSelected.replace("%d", parent.Selection.Size()));
//		GetPane().SetStatusBarText(parent.Selection.Size()+" objects selected");
	}
	else {
		if(TreeNodes.length==1) {
			GetPane().SetStatusBarText(parent.TextSingleNode);
		}
		else {
			GetPane().SetStatusBarText(parent.TextMultipleNodes.replace("%d", TreeNodes.length));
		}
//		GetPane().SetStatusBarText(TreeNodes.length+" objects");
	}
	UpdateSelectButton();
}

function ScrollTo(oNode) {
	var FormContainer = document.getElementById("FormContainer");
	var TreeNodeTop = oNode.offsetTop - oNode.offsetHeight;;
	var TreeNodeBottom = oNode.offsetTop + 2*oNode.offsetHeight;
	var ScrollTop = FormContainer.scrollTop;
	var ScrollBottom = FormContainer.scrollTop + FormContainer.offsetHeight;
	//					debug(TreeNodeBottom+">"+ScrollBottom+"?");
	if(TreeNodeBottom>ScrollBottom) {
		FormContainer.scrollTop = oNode.offsetTop - FormContainer.offsetHeight + 2*oNode.offsetHeight;
	}
	else if(TreeNodeTop<ScrollTop) {
		FormContainer.scrollTop = oNode.offsetTop - oNode.offsetHeight;
	}
}
