var DynDataNodes =  {}; 


/**
 * Lädt alle Kinder des geg. Pfades
 * @param {Object} sParentNodePath   [Default: '/']
 * @param {boolean} bAutoRepaintTree [Default: true]
 */
DynDataNodes.load = function(sParentNodePath, bAutoRepaintTree) {

	if (typeof(sParentNodePath) != 'string' || sParentNodePath == '') {
		sParentNodePath = "/";
	}
	
	if (typeof(bAutoRepaintTree) != 'boolean') {
		bAutoRepaintTree = true;
	}
	
	if (DynDataNode.ROOTNODE.getNodeByPath(sParentNodePath) == null) {
		var sGParentNodePath = sParentNodePath.split("/");
		sGParentNodePath.pop();
		sGParentNodePath = sGParentNodePath.join("/");
		DynDataNodes.load(sGParentNodePath);
	}
	
	var xhr = bb.command.load("/db/apsisa.dll/getDDNodes", "POST", "parentNodePath=" + sParentNodePath, null, null, null, null, null, null, false, null);
	DynDataNodes.parseFromXML(xhr.responseXML, sParentNodePath, bAutoRepaintTree);
	
}

DynDataNodes.parseFromXML = function(xmlResponse, sParentNodePath, bAutoRepaintTree) {
	if (typeof(bAutoRepaintTree) != 'boolean') {
		bAutoRepaintTree = true;
	}
	
	var ddNodes = bb.evaluateSmart("[/nodes/node]", xmlResponse);
	if (ddNodes) {
		for (var i = 0; i < ddNodes.length; i++) {
			var node = ddNodes[i];
			var nodeId = parseInt(node.getAttribute("nodeId"));
			var nodeType = parseInt(node.getAttribute("type"));
			var sNodeName = node.getAttribute("name");
			var sNodeDesc = node.textContent;
			if (bb.browser.ie) {
				sNodeDesc = node.innerText;
			}
			var rights = new Array();
			if (parseInt(node.getAttribute("read")) === 1) {
				rights.push(DynDataNode.RIGHT_READ);
			}
			if (parseInt(node.getAttribute("write")) === 1) {
				rights.push(DynDataNode.RIGHT_WRITE);
			}
			if (parseInt(node.getAttribute("modify")) === 1) {
				rights.push(DynDataNode.RIGHT_MODIFY);
			}
			if (parseInt(node.getAttribute("delete")) === 1) {
				rights.push(DynDataNode.RIGHT_DELETE);
			}
			switch (parseInt(node.getAttribute("type"))) {
				case DynDataNode.NODETYPE_BRANCH:
				case DynDataNode.NODETYPE_LEAF:{
					var dnNode = DynDataNode.ROOTNODE.getNodeByID(nodeId);
					if (!dnNode) {
						dnNode = new DynDataNode(nodeId, nodeType, sNodeName, sNodeDesc, rights);
					}
					var nExpectedChildCount = parseInt(node.getAttribute('cCount'));
					if (nExpectedChildCount == NaN || nExpectedChildCount < 0) {
						nExpectedChildCount = 0;
					}
					dnNode.setExpectedChildCount(nExpectedChildCount);
					dnNode.setName(sNodeName);
					dnNode.setDescription(sNodeDesc);
					dnNode.setRights(rights);
					var parentNode = DynDataNode.ROOTNODE.getNodeByPath(sParentNodePath);
					if (parentNode.getChildNodeByID(nodeId) == null) {
						parentNode.addChildNode(dnNode);
					}
					break;
				}
				default:
					{
						//unknown node type
						break;
					}
			}
			
		}
	}
	if (bAutoRepaintTree) {
		bb.command.fireEvent(bb.document.getElementById('binderTreeContainer'), 'refreshTree', false, true);
	}
}

DynDataNodes.reset = function() {
	DynDataNode.ROOTNODE.deleteChildNodes(true);
}


/**
 * @param {Number} nID ID des Knotens 
 * @param {Number} nNodeType Der Type des Knotens (Eine der DynDataNode.NODETYPE_* Konstanten)
 * @param {String} sName Name des Knotens
 * @param {String} sDesc Beschreibung des Knotens
 * @param {Array} oRights Rechte
 * @constructor
 * @return {DynDataNode}
 */
DynDataNode = function(nID, nNodeType, sName, sDesc, rights) {
	this.m_sName = sName;
	this.m_sDesc = sDesc;
	this.m_ID = nID;
	this.m_childNodes = new Array();
	this.m_rights = rights;
	this.m_type = nNodeType;
	this.m_nExpectedCCount = 0;
	DynDataNode[nID] = this;
}

/**
 * Konstante: Knotentyp ROOT (nur intern verwendet)
 * @return {Number}
 */
DynDataNode.NODETYPE_ROOT = 0;

/**
 * Konstante: Knotentyp Zweig
 * @return {Number}
 */
DynDataNode.NODETYPE_BRANCH = 1;

/**
 * Konstante: Knotentyp Blatt
 * @return {Number}
 */
DynDataNode.NODETYPE_LEAF = 2;

/**
 * Konstante: Schreibrecht
 * @return {String}
 */
DynDataNode.RIGHT_WRITE = "write";

/**
 * Konstante: Leserecht
 * @return {String}
 */
DynDataNode.RIGHT_READ = "read";

/**
 * Konstante: Löschrecht
 * @return {String}
 */
DynDataNode.RIGHT_DELETE = "delete";

/**
 * Konstante: Modifizierungsrecht
 * @return {String}
 */
DynDataNode.RIGHT_MODIFY = "modify";


/**
 * Konstante: Root Knoten des Baums
 * @return {DynDataNode}
 */
DynDataNode.ROOTNODE = new DynDataNode(0, DynDataNode.NODETYPE_ROOT, "ROOT", new Array(DynDataNode.RIGHT_READ));

/**
 * Fügt diesem Knoten einen Kindknoten hinzu
 * @param {DynDataNode} oDynDataNode der anzufügende Kindknoten 
 */
DynDataNode.prototype.addChildNode = function(oDynDataNode){
	if (this.getType() !== DynDataNode.NODETYPE_LEAF) { 
		this.m_childNodes[this.m_childNodes.length] = DynDataNode[oDynDataNode.getID()];
	}
}

DynDataNode.prototype.toString = function() {
	return this.getName();
}

/**
 * Setzt die Anzahl der zu erwartenden Kinder für diesen Knoten
 * @param {Number} n (muss >=0 sein)
 */
DynDataNode.prototype.setExpectedChildCount = function(n) {
	if (typeof(n) == 'number' && this.getType() != DynDataNode.NODETYPE_LEAF) {
		if (n >= 0) {
			this.m_nExpectedCCount = n;
		}
	}
}

/**
 * Gibt die Anzahl der zu erwartenden Kinder zurück
 * @return {Number}
 */
DynDataNode.prototype.getExpectedChildCount = function() {
	return this.m_nExpectedCCount;
}

/**
 * @return {Boolean}
 */
DynDataNode.prototype.isExpectingChildNodes = function() {
	var bIsExpectingChildNodes =((this.getType() != DynDataNode.NODETYPE_LEAF) && (this.m_childNodes.length < this.getExpectedChildCount()));; 
	return bIsExpectingChildNodes;
}

/**
 * Löscht diesen Knoten aus dem Speicher. (<b>NICHT VOM SERVER</b>) 
 * Wenn diser Knoten Kinder hat wird er nur gelöscht, 
 * wenn der Parameter auf true gesetzt wurde. 
 * Auf diese Weise wird der gesamte Teilbaum gelöscht. 
 * Wenn dieser Knoten der ROOT Knoten ist wird er nicht gelöscht. 
 * Die Kinder und Kindeskinder werden nur gelöscht, wenn der Parameter auf true gesetzt wurde.
 * @param {Boolean} bRecursiveDelete, Default: false
 * @return {void} 
 */
DynDataNode.prototype.deleteChildNodes = function(bRecursiveDelete) {
	if (typeof(bRecursiveDelete)!='boolean') {
		bRecursiveDelete = false;
	}
	if (this.hasChildNodes() && bRecursiveDelete===true) {
		for (var i=0; i<this.m_childNodes.length; i++) {
			this.m_childNodes[i].deleteChildNodes(bRecursiveDelete);
		}
		this.m_childNodes = new Array();
	}
	if (this != DynDataNode.ROOTNODE) {
		delete DynDataNode[this.m_ID];
	}
}

/**
 * Gibt den Knoten mit der geg. ID zurück, oder null wenn dieser nicht existiert
 * @param {Number} nID
 * @param {DynDataNode} Den Knoten mit der geg. ID, oder null wenn dieser nicht existiert 
 */
DynDataNode.prototype.getNodeByID = function(nID) {
	var node = null;
	if (DynDataNode[nID] != undefined) {
		node = DynDataNode[nID]; 
	}
	return node;
}

/**
 * Löscht den Knoten mit der geg. ID aus dem Baum (wenn dieser keine Kinder hat).
 * <i>Diese Methode kann nur vom ROOT Knoten aus ausgeführt werden: DynDataNode.ROOTNODE.deleteNodeByID(4711)</i>
 * @param {Number} nID ID des zu löschenden Knotens
 * @return {void}
 */
DynDataNode.prototype.deleteNodeByID = function(nID){
	if (DynDataNode[nID]) { 
		if ((this == DynDataNode.ROOTNODE || arguments[1]) && DynDataNode[nID] != DynDataNode.ROOTNODE) {
			for (var i=0; i<this.m_childNodes.length; i++) {
				var curChildNode = this.m_childNodes[i];
				var curChildNodeID = curChildNode.getID();
				if (curChildNodeID == nID) {
					this.m_childNodes.splice(i, 1);
				}
				else {
					this.m_childNodes[i].deleteNodeByID(nID, true);
				}
			}	
		}
		if (this == DynDataNode.ROOTNODE) {
			//Am Ende der Rekursion
			delete DynDataNode[nID];
		}
	}
}

/**
 * Löscht den Knoten mit dem geg. Pfad aus dem Baum (wenn dieser keine Kinder hat).
 * <i>Diese Methode kann nur vom ROOT Knoten aus ausgeführt werden: DynDataNode.ROOTNODE.deleteNodeByPath('/a/b/c')</i>
 * @param {String} sPath Pfad des zu löschenden Knotens
 * @return {void}
 */
DynDataNode.prototype.deleteNodeByPath = function(sPath) {
	var ddNode = DynDataNode.ROOTNODE.getNodeByPath(sPath);
	if (ddNode != null) {
		var ddNodeID = ddNode.getID();
		DynDataNode.ROOTNODE.deleteNodeByID(ddNodeID);		
	}
} 

/**
 * Gibt einen Knoten anhand seines Pfades zurück. Wenn dieser mit "/" beginnt wird er als vollständiger Pfad interpretiert, ansonsten wird davon ausgegangen, dass der Pfad relativ zu diesem Knoten ist.
 * @param {String} sPath
 * @return {DynDataNode} den Knoten, oder null wenn dieser nicht existiert
 */
DynDataNode.prototype.getNodeByPath = function(sPath) {
	var node = null;
	var bIsFullPath = (sPath.charAt(0)=='/');
	var splittedPath = sPath.split('/');
	var node = DynDataNode.ROOTNODE;
	if (sPath != '/') {
		if (!bIsFullPath) {
			node = this;
		}
		for (var i = bIsFullPath ? 1 : 0; i < splittedPath.length && node != null; i++) {
			node = node.getChildNodeByName(splittedPath[i]);
		}
	}
	return node;
}

/**
 * Gibt den name dieses Knotens zurück
 * @return {String}
 */
DynDataNode.prototype.getName = function() {
	return this.m_sName;
}

/**
 * Gibt den Typ dieses Knotens zurück
 */
DynDataNode.prototype.getType = function() {
	return this.m_type;
}

/**
 * Gibt die Beschreibung des Knotens zurück
 * @return {String}
 */
DynDataNode.prototype.getDescription = function() {
	return this.m_sDesc;
}

DynDataNode.prototype.setDescription = function(sDesc) {
	this.m_sDesc = sDesc;
}

DynDataNode.prototype.setName = function(sName) {
	this.m_sName = sName;
}

DynDataNode.prototype.setRights = function(oRights) {
	this.m_rights = oRights;
}

/**
 * Gibt zurück, ob dem Benutzer das geg. Recht gewährt ist
 * @param {String} strRighConst Eine der DynDataNode.RIGHT_* Konstanten
 * @return {Boolean} true, wenn das geg. Recht gewährt wurde, ansonsten false
 */
DynDataNode.prototype.isRightGranted = function(sRightConst) {
	var nRightIndex = -1;
	if (typeof(Array.indexOf)=='function') {
		 nRightIndex = this.m_rights.indexOf(sRightConst)
	}
	else {
		for (var i=0; i<this.m_rights.length && nRightIndex == -1; i++) {
			if (this.m_rights[i] == sRightConst) {
				nRightIndex = i;
			} 
		}
	}
	return (nRightIndex >= 0 && nRightIndex <= (this.m_rights.length-1));
}
/**
 * Gibt zurück, ob der Benutzer diesen Knoten lesen darf.
 * @return {Boolean} true, wenn der Benutzer den Knoten lesen darf, ansonsten false 
 */
DynDataNode.prototype.canRead = function() {
	return this.isRightGranted(DynDataNode.RIGHT_READ);
}
/**
 * Gibt zurück, ob der Benutzer in diesen Knoten schreiben darf.
 * @return {Boolean} true, wenn der Benutzer in den Knoten schreiben darf, ansonsten false
 */
DynDataNode.prototype.canWrite = function() {
	return this.isRightGranted(DynDataNode.RIGHT_WRITE);
}
/**
 * Gibt zurück, ob der Benutzer diesen Knoten modifizieren darf.
 * @return {Boolean} true, wenn der Benutzer den Knoten modifizieren darf, ansonsten false
 */ 
DynDataNode.prototype.canModify = function() {
	return this.isRightGranted(DynDataNode.RIGHT_MODIFY);
}
/**
 * Gibt zurück, ob der Benutzer diesen Knoten löschen darf.
 * @return {Boolean} true, wenn der Benutzer den Knoten löschen darf, ansonsten false
 */
DynDataNode.prototype.canDelete = function() {
	return this.isRightGranted(DynDataNode.RIGHT_DELETE);
}

/**
 * Gibt zurück, ob dieser Knoten Kindknoten besitzt
 * @return {Boolean} true, wenn dieser Knoten Kindknoten besitzt, ansonsten false
 */
DynDataNode.prototype.hasChildNodes = function() {
	var bHasChildNodes = false;
	if (this.getType() != DynDataNode.NODETYPE_LEAF) {
		bHasChildNodes = (this.m_childNodes.length > 0);
	}
	return bHasChildNodes;
}

/**
 * Gibt zurück ob dieser Knoten noch Kindknoten erwartet, die noch nicht geladen wurden
 */
DynDataNode.prototype.expectsChildNodes = function() {
	var bExpectsChildNodes = false;
	if (this.getType() != DynDataNode.NODETYPE_LEAF) {
		bExpectsChildNodes = (this.m_nExpectedCCount == this.m_childNodes.length)
	}
	return bExpectsChildNodes;
}

/**
 * Gibt alle Kindknoten dieses Knotens zurück
 * @return {Array} Ein Array mit allen Kindknoten dieses Knotens (Das Array ist leer, wenn dieser Knoten keine Kinder besitzt)
 */
DynDataNode.prototype.getChildNodes = function() {
	return this.m_childNodes;
}

/**
 * Gibt die ID dieses Knotens zurück
 * @return {Number}
 */
DynDataNode.prototype.getID = function() {
	return this.m_ID;
}

/**
 * Gibt den Kindknoten mit dem geg. Namen zurück
 * @param {String} sName Der Name des gewünschten Kindknotens
 * @return {DynDataNode} Den gewünschnten Kindknoten oder null wenn der Knoten nicht existiert
 */
DynDataNode.prototype.getChildNodeByName = function(sName) {
	var node = null;
	for (var i = 0; i < this.m_childNodes.length && node == null; i++) {
		if (this.m_childNodes[i].getName().toLowerCase() == sName.toLowerCase()) {
			node = this.m_childNodes[i]; 
		}
	} 
	return node;
}

/**
 * Gibt den Kindknoten mit der geg. ID zurück
 * @param {Number} nID Die ID des gewünschten Kindknotens
 * @return {DynDataNode} Den gewünschnten Kindknoten oder null wenn der Knoten nicht existiert
 */
DynDataNode.prototype.getChildNodeByID = function(nID) {
	var node = null;
	for (var i=0; i<this.m_childNodes.length && node==null; i++) {
		if (this.m_childNodes[i].getID() == nID) {
			node = this.m_childNodes[i];
		}
	}
	return node;
}

/**
 * Erzeugt den Baum in Backbase Darstellung (BTL) ab diesem Knoten. 
 * Wenn der Startknoten (dieser Knoten) nicht der Root Knoten ist gibt diese Methode null zuück.
 * Daher muss diese Funktion vom Root Knoten aus aufgerufen werden: DynDataNode.ROOTNODE.toBTLString() 
 * @return {String} den Baum als BTL String oder null wenn dieser Knoten nicht der Root Knoten ist.
 */
DynDataNode.prototype.toBTLString = function(){
	var sBTL = null;
	if (this == DynDataNode.ROOTNODE || arguments[0] != undefined) {
		sBTL = "";
		var sParentPath = '/';
		if (arguments[0] != undefined) {
			var sParentPathPostfix = arguments[0] == '/' ? '' : '/';
			sParentPath = arguments[0] + sParentPathPostfix + this.getName();
		}
		switch (this.m_type) {
			case DynDataNode.NODETYPE_ROOT:{
				sBTL += '<b:tree class="tree" xmlns="http://www.w3.org/1999/xhtml" xmlns:e="http://www.backbase.com/2006/xel" xmlns:c="http://www.backbase.com/2006/command" xmlns:b="http://www.backbase.com/2006/btl" id="tree:' + sParentPath + '">';
				break;
			}
			case DynDataNode.NODETYPE_BRANCH:{
				sBTL += '<b:treeBranch title="' + this.getDescription() + '" open="' + this.hasChildNodes() + '" label="' + this.getName() + '" id="tree:' + sParentPath + '" b:dragReceive="dia-ddNodeLeaf dia-resultviewitem dia-binderentry" icon="/system/images/iconclasses/folder-closed.gif" iconEmpty="/system/images/iconclasses/folder-closed.gif" iconOpen="/system/images/iconclasses/folder-opened.gif">';
				sBTL += '<e:handler event="DOMAttrModified" propagate="stop">'
				sBTL += '<e:variable name="myPath" select="substring-after(@id, \'tree:\')"/>';
				sBTL += '<e:choose>';
				sBTL += '<e:when test="$event/property::attrName=\'open\' and $event/property::prevValue!=\'true\' and $event/property::newValue=\'true\'">';
				if (this.hasChildNodes() || this.isExpectingChildNodes()) {
					sBTL += '<e:call function="DynDataNodes.load" argument1="$myPath"/>';
				}
				sBTL += '</e:when>';
				sBTL += '<e:when test="$event/property::attrName = \'selected\' and $event/property::prevValue!=\'true\' and $event/property::newValue=\'true\'">';
				sBTL += '<e:call function="updateBinderTreeToolbar">';
				sBTL += '<e:with-argument name="sSelectedNodePath" select="$myPath"/>';
				sBTL += '</e:call>';
				sBTL += '</e:when>';
				sBTL += '</e:choose>';
				sBTL += '</e:handler>';
				
				sBTL += '<e:handler event="dragOver" propagate="stop">';
				sBTL += '<e:call function="onDragOverDDTreeBranch">';
				sBTL += '<e:with-argument name="oEvent" select="$event"/>';
				sBTL += '<e:with-argument name="oEventSrc" select="javascript:this"/>';
				sBTL += '</e:call>';
				sBTL += '</e:handler>';
				
				sBTL += '<e:handler event="dragDrop" type="application/xml" propagate="stop">';
				sBTL += '<e:call function="onDragDropDDTreeBranch">';
				sBTL += '<e:with-argument name="oEvent" select="$event"/>';
				sBTL += '<e:with-argument name="oEventSrc" select="javascript:this"/>';
				sBTL += '</e:call>';
				sBTL += '</e:handler>';
				
				break;
			}
			case DynDataNode.NODETYPE_LEAF:{
				sBTL += '<b:treeLeaf'
				sBTL += ' dragItem="dia-ddNodeLeaf"';
				sBTL += ' title="' + this.getDescription() + '" label="' + this.getName() + '" id="tree:' + sParentPath + '" e:behavior="b:drag" dragMode="symbol" dropMode="none" dragSymbol="." b:dragReceive="dia-resultviewitem dia-binderentry" icon="/system/images/iconclasses/binder.gif">';
				
				sBTL += '<e:handler event="dragStart" propagate="stop" type="application/javascript">';
				sBTL += '<![CDATA[';
				sBTL += 'this.setAttribute("selected", "true");';
				sBTL += 'bb.callFunction("rememberSelectedNode", ["copy"]);';
				sBTL += ']]>';
				sBTL += '</e:handler>';
				
				sBTL += '<e:handler event="dragEnd" propagate="stop" type="application/javascript">';
				sBTL += '<![CDATA[';
				sBTL += 'if (!$_GLOBAL["DDLEAF_WAS_DROPPED"]) {'
				sBTL += 'bb.callFunction("resetRememberedNode",[]);';
				sBTL += '}';
				sBTL += 'else {';
				sBTL += 'delete $_GLOBAL["DDLEAF_WAS_DROPPED"];';
				sBTL += '}';
				sBTL += ']]>';
				sBTL += '</e:handler>';
				
				sBTL += '<e:handler event="click" type="application/xml">';
				sBTL += '<e:if test="$event/property::button=0">';
				sBTL += '<e:variable name="myBinderName" select="substring-after(@id, \'tree:\')"/>';
				sBTL += '<e:variable name="titlePrefix" select="\'{$dictionary/terms/term[@key=\'binder-single\']}\'"/>';
				sBTL += '<e:call function="openBinder">';
				sBTL += '<e:with-argument name="binderName" select="$myBinderName"/>';
				sBTL += '<e:with-argument name="doShowBinder" select="javascript:true"/>';
				sBTL += '</e:call>';
				sBTL += '</e:if>';
				sBTL += '</e:handler>';
				
				sBTL += '<e:handler event="dragOver" propagate="stop">';
				sBTL += '<e:call function="onDragOverDDTreeLeaf">';
				sBTL += '<e:with-argument name="oEvent" select="$event"/>';
				sBTL += '<e:with-argument name="oEventSrc" select="javascript:this"/>';
				sBTL += '</e:call>';
				sBTL += '</e:handler>';
				
				sBTL += '<e:handler event="dragDrop" type="application/xml" propagate="stop">';
				sBTL += '<e:call function="onDragDropDDTreeLeaf">';
				sBTL += '<e:with-argument name="oEvent" select="$event"/>';
				sBTL += '<e:with-argument name="oEventSrc" select="javascript:this"/>';
				sBTL += '</e:call>';
				sBTL += '</e:handler>';
				
				sBTL += '<e:handler event="DOMAttrModified" propagate="stop">';
				sBTL += '<e:variable name="myPath" select="substring-after(@id, \'tree:\')"/>';
				sBTL += '<e:if test="$event/property::attrName = \'selected\' and $event/property::prevValue!=\'true\' and $event/property::newValue=\'true\'">';
				sBTL += '<e:call function="updateBinderTreeToolbar">'
				sBTL += '<e:with-argument name="sSelectedNodePath" select="$myPath"/>';
				sBTL += '</e:call>'
				sBTL += '</e:if>';
				sBTL += '</e:handler>';
				break;
				
				
			}
		}
		for (var i = 0; i < this.m_childNodes.length && this.m_type != DynDataNode.NODETYPE_LEAF; i++) {
			var curChildId = this.m_childNodes[i].getID();
			if (DynDataNode[curChildId]) {
				sBTL += this.m_childNodes[i].toBTLString(sParentPath);
			}
		}
		switch (this.m_type) {
			case DynDataNode.NODETYPE_ROOT:{
				sBTL += '</b:tree>';
				break;
			}
			case DynDataNode.NODETYPE_BRANCH:{
				sBTL += '</b:treeBranch>';
				break;
			}
			case DynDataNode.NODETYPE_LEAF:{
				sBTL += '</b:treeLeaf>';
				break;
			}
		}
	}
	return sBTL;
}

/**
 * Erzeugt den Baum in Backbase Darstellung (BTL) ab diesem Knoten. 
 * Wenn der Startknoten (dieser Knoten) nicht der Root Knoten ist gibt diese Methode null zuück.
 * Daher muss diese Funktion vom Root Knoten aus aufgerufen werden: DynDataNode.ROOTNODE.toBTLString() 
 * @return {Node} den Baum als BTL Node oder null wenn dieser Knoten nicht der Root Knoten ist.
 */
DynDataNode.prototype.toBTLNode = function() {
	return bb.xml.parse(this.toBTLString);
}

