    // Module to bind HTML-controls to XMLnodes
    // This module can be used standalone meaning it doesn't use other javascrip modules. 
    // Everything should work OK with Sarissa and should work in mozilla/firefox as well as internet explorer
    // version 1.5.601
    // Status : Develop.
    //
    ////////////////////////////////////////////////////////////////////////////////
    /* 
    validations : 
        number
        date
        time
        positive
        min=XXX
        max=XXX
		notnull
		function=
    
    format : 
    	boolean=YesString:NoString (where YesString and NoString represent the values to display when the underlying value = true or false )
        currency=Chr  Chr is optional and will be used as prefix. Defaults to €
        decimals=XXX
        number
		hidezero = number 0 is displayed as &nbsp;
		memo
        prefix=Chr (character to display before a number. e.g. prefix=$,number will display like "$ 100")
        suffix=Chr (Will display after a number. To be used with displaying percentages.
        DMY (date Day-Month-Year)
        MDY (date Month-Day-Year)
        HH:MM[:SS] (time hours:minutes:seconds)
		hH:MM[:SS] (hours:minutes:seconds no leading zero)
		DMYHMS = date and time
		mtime (time minutes after midnight)
		function=MyFormatFunction[/MyDeformatFunction]   MyFormatFunction(stValue) is the name of a formatting function. 
					It will be called with the value to be formatted (strValue) 

	//////////////////////////////////////////////////////////////////////////////
        
		FAQ
		<RvG20070824>
		Vraag: De onchange op een hidden element wordt (in Firefox?) niet meegenomen in BindControls.		
		Antwoord: Een onchange zetten op een element met display:none KAN NIET (in Firefox?). 
			De onchange wordt daarna nooit getriggerd; ook niet als het element van display:none wordt afgehaald...
			Onblur gaat wel goed. Aangeraden wordt dus om in dat geval deze zo veel mogelijk te gebruiken.
		Vraag: Bestaande events met 'this' als parameter worden niet met de (verwachte) HTML-node aangeroepen.
		Antwoord: Gebruik geen 'this' in events van elementen die met BindControls gebonden gaan worden.
		</RvG20070824>

    */
    //////////////////////////////////////////////////////////////////////////////

	var arLog=new Array();
	var starttime, lasttime;
	function bLog(strLogtext) {
		var tmpTime = new Date().valueOf();
		arLog[arLog.length]=(tmpTime-starttime)+"ms ("+(tmpTime-lasttime)+"ms) : "+strLogtext;
		lasttime=tmpTime;
	}
	
    //////////////////////////////////////////////////////////////////////////////
	
    var Bind_ErrorColor = "red";
    var Bind_ErrorClass="error";
    var Bind_ThousandSeparator=".";
    var Bind_DecimalSeparator=",";
    var Bind_Skiplist = "";
    var gValidateOnBind = false;
    var gSurpressValidationErrors = false;
	var gDirtyElements = true;
	var gDirtyDocument = true;
    
    var Bind_Debug = false;
    var gTagsToBind = "|td|th|span|div|input|select|table|a|img|textarea|";
    var bind_XMLdoc;
    var bind_curXMLNode=false;
    var bind_TraceDebug = new Array();
	var bind_ErrorPattern = new Array();
		bind_ErrorPattern["number"] = new RegExp(".*\\"+Bind_DecimalSeparator+".*\\"+Bind_DecimalSeparator);
		bind_ErrorPattern["currency"] = bind_ErrorPattern["number"];
		bind_ErrorPattern["time"] = /(.*\:.*\:.*\:)|(.{9,})/
		bind_ErrorPattern["date"] = /(.*[\-\/].*[\-\/].*[\-\/])|(.{11,})/
 	var bind_AllowedChars = new Array();
		bind_AllowedChars["number"] = new RegExp("[\\d\\"+Bind_DecimalSeparator+"\\-]");
		bind_AllowedChars["currency"] = bind_AllowedChars["number"];
		bind_AllowedChars["time"] = /[\d\:\.]/;
		bind_AllowedChars["date"] = /[\d\-\/]/;

    var gErrTable = new Array();
		gErrTable["currency"] = "You need to enter a currency value here (use %1 as decimal separator).";
		gErrTable["time"] = "You need to enter a valid time here (e.g. 14:23:12).";
		gErrTable["date"] = "You need to enter a valid date here (e.g. 15/09/2005).";
		gErrTable["positive"] = "This value needs to be positive.";
		gErrTable["number"] = "Please enter a number using %1 as decimal separator.";
		gErrTable["minimum"] = "The minimum value for this field is %1.";
		gErrTable["maximum"] = "The maximum value for this field is %1.";
		gErrTable["notnull"] = "This field may not be empty.";
	
	var arPathCache = new Array(); //Caching for parent Paths
	var arExpressionCache = new Array(); //Cache bindexpressions as functions
	var arBoundNodes = new Array(); //All the bound nodes
	
function bSelectNodes(aExpr, aNode) {
	if (window.ActiveXObject) {
		return aNode.selectNodes(aExpr);
	} else {
		var xpe = new XPathEvaluator();
		var nsResolver = xpe.createNSResolver(aNode.ownerDocument ? aNode.ownerDocument.documentElement : aNode.documentElement);
		var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
		var found = [];
		while (res = result.iterateNext())	found.push(res);
		return found;
	}
}

function bSelectSingleNode(aExpr, aNode) {
	if (aNode) {
		if (window.ActiveXObject) {
			return aNode.selectSingleNode(aExpr);
		} else {
			var xpe = new XPathEvaluator();
			var nsResolver = xpe.createNSResolver(  aNode.ownerDocument ? aNode.ownerDocument.documentElement : aNode.documentElement );
			var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
			return result.iterateNext();
		}
	} else {
		return null;
	}
}    

function bSetNodeValue(ElementNode, strAttributeName, strValue) {
	if (ElementNode == null || strValue == null)
		return false;
	else if (strAttributeName && strAttributeName != "") {
		var tmpNewAttr = selectSingleNode("@"+strAttributeName,ElementNode);
		if (!tmpNewAttr) {
			ElementNode.attributes.setNamedItem(ElementNode.ownerDocument.createAttribute(strAttributeName));
			tmpNewAttr = selectSingleNode("@"+strAttributeName,ElementNode);
		}
		tmpNewAttr.nodeValue=strValue;
		return true;
	} else {
		if (ElementNode.nodeValue!=null) {
			ElementNode.nodeValue=strValue;
		} else if (ElementNode.text!=null) {
			ElementNode.text=strValue;
		} else {
			if (ElementNode.childNodes.length==0) {
				ElementNode.appendChild(bind_XMLdoc.createTextNode(strValue));
			}
			ElementNode.childNodes[0].data=strValue;
		}
	}
}
    
function GetEmptyDoc() {
	if (!bind_XMLdoc) {
		if (document.implementation && document.implementation.createDocument) {
			bind_XMLdoc = document.implementation.createDocument("", "", null);
		} else if (window.ActiveXObject) {
			bind_XMLdoc = new ActiveXObject("Microsoft.XMLDOM");
		}
	
		try {
			bind_XMLdoc.loadXML("<dummy />");
		} catch(e) {
			bind_XMLdoc = (new DOMParser()).parseFromString("<dummy />", "text/xml");
		}
	}
}


/*****************************************************
 *
 *  Bindcontrols
 *  Binds all the controls in a document. new_XMLdoc should be supplied. If not: the last used xmldoc will be used 
 *  If strNodeID is provided binding will start at the HTMLelement identified by strNodeID.
 *  If no element is found with an id=strNodeID, bindcontrols will asume a HTMLelement object has been provided.  
 * 
 ********************************************************/  
function BindControls(new_XMLdoc, strNodeID) {	//bind controls to XML-message
	var bForceRebind=false;
	arPathCache = new Array();
	arExpressionCache = new Array();
	if (Bind_Debug) {starttime = new Date().valueOf();lasttime = new Date().valueOf();}
	if (Bind_Debug) bLog("Starting Binding");
	if (new_XMLdoc) {bind_XMLdoc=new_XMLdoc; bind_curXMLNode=null; bForceRebind=true;}
 //alert(xmlPrint(bind_XMLdoc));
	if (!bind_XMLdoc) GetEmptyDoc();
	var HTMLnode=document.getElementById(strNodeID);
	if (!HTMLnode) HTMLnode=strNodeID;
    if (HTMLnode) {
        if (!HTMLnode.tagName) HTMLnode = document.getElementById(String(HTMLnode));
        if (HTMLnode) {
        	BindSubset(HTMLnode,false,bForceRebind);
        }
    } else {
    	BindSubset(document.body,false,bForceRebind);
    } //if HTMLnode
	if (Bind_Debug) bLog("Done Binding");
	if (Bind_Debug) alert(arLog.join("\n"));
} //function

function BindRefresh(strCtrlId) {
	if (Bind_Debug) {starttime = new Date().valueOf();lasttime = new Date().valueOf();	bLog("Start refreshing");	}
	if (strCtrlId) {
		var HTMLnode = document.getElementById(strCtrlId);
		if (!HTMLnode) HTMLnode = strCtrlId;
		if (HTMLnode.boundnode) HTMLnode.ReadXML();
	} else {
		var nodecount=arBoundNodes.length;
		var x=nodecount;
		while (x) {
			arBoundNodes[nodecount-x].ReadXML();
			x--;
		}
	}
	if (Bind_Debug) bLog("Done refreshing");
	if (Bind_Debug) alert(arLog.join("\n"));
}

/******************************
 *  BindSubset
 *  
 *  Same as BindControls, but this time we'll recursively process all nodes from the startNode
 *  
 ********************************/
function BindSubset(startNode, startXMLNode,bForceRebind) {
	if (!bind_XMLdoc) GetEmptyDoc();
 	if (startNode && startNode.tagName && !startNode.isTemplate) {
		var bind_RememberCurNode = bind_curXMLNode;
		var strParentPath=startNode.getAttribute("parentpath");
		var bindindex=startNode.getAttribute("bindindex");
		if (startXMLNode) {
			bind_curXMLNode=startXMLNode;
		}
		if (!bForceRebind && startNode.curpath && startNode.curpath!=null){
			bind_curXMLNode=startNode.curpath;
		} else if (!bind_curXMLNode) {
			bind_curXMLNode=bind_XMLdoc;
		}
		if (!startNode.curpath && strParentPath && bSelectNodes(strParentPath,bind_curXMLNode).length>0) {
			startNode.curpath = bSelectNodes(strParentPath,bind_curXMLNode)[bindindex];
			bind_curXMLNode=startNode.curpath;
		} 
		var strDisplayCondition=startNode.getAttribute("displaycondition");
		startNode.boundnode=false;
//		try {
			if (strDisplayCondition && bSelectNodes(strDisplayCondition,bind_curXMLNode).length==0) { 
				startNode.style.display="none";
			} else {
		        if (strDisplayCondition && bSelectNodes(strDisplayCondition,bind_curXMLNode).length>0 && startNode.style.display=="none") startNode.style.display="";
				var strTag = startNode.tagName.toLowerCase();
		        var strID = startNode.ID;
				if ((Bind_Skiplist=="" || String("|"+Bind_Skiplist+"|").indexOf("|"+strID+"|")<0) && gTagsToBind.indexOf("|"+strTag+"|") > -1) {
					switch (strTag) {
		            case "table" :
						BindTable(startNode);
		                break;
		            case "input" : 
		                BindInputControl(startNode);
		                break;
		            case "select" :
		                BindSelectControl(startNode);
		                break;
		            case "a" :
		                BindAnchor(startNode);
		                break;
		            case "img" :
		                BindImage(startNode);
		                break;
		            case "textarea" :
		                BindTextArea(startNode);
		                break;
		            default:
		                BindInnerHTML(startNode);
		            }
		        } //if bindable tag
				var children = startNode.childNodes;
				for (var c=0, child; child=children[c]; c++) {if (child.tagName) BindSubset(child,bind_curXMLNode,bForceRebind);}
		    }
//		} catch(e) {}
		bind_curXMLNode = bind_RememberCurNode;
   } //if (startNode)
} //function    

function BindTable(HTMLnode) {
		/* This function binds xml-elements to tables
		 * It is the successor of the Bindtemplates function, but it only works on tables
		 */		 
        var strTMP
        var strDataSection=""; //holds the middle part of the result and is processed for every node;
        var strPath="";			//Holds the XMLpath to the current node in the datsection
        var targetCTRL, nodes;
        var pagesize=HTMLnode.getAttribute("pagesize");
       	var curpage=HTMLnode.getAttribute("curpage");

		// var tmpDatasection=document.createDocumentFragment(  );         
        if (String(HTMLnode.tagName).toLowerCase()=="table") {
			strPath=HTMLnode.getAttribute("boundpath");
			if (strPath) {
				HTMLnode.curpath = bind_curXMLNode;
				if (HTMLnode.tBodies.length>1) {
					targetCTRL=HTMLnode.tBodies[1];
					HTMLnode.removeChild(targetCTRL);
				} 
				targetCTRL=document.createElement("tbody");
				HTMLnode.appendChild(targetCTRL);
				if (HTMLnode.tBodies.length>0) {
					HTMLnode.tBodies[0].style.display="none";
					HTMLnode.tBodies[0].isTemplate=true;
					strTMP=HTMLnode.tBodies[0].innerHTML; 
		            if (targetCTRL) {
						nodes = bSelectNodes(strPath,bind_curXMLNode);
						var nOffset=1; //XPATH uses base 1 indexes; like //doc/element[1] indicating the first element
						var strRow;
						var arRow=new Array();
						var sublevel, rTest;
						if (nodes.length>0 && bSelectNodes(strPath+"[0]",bind_XMLdoc).length>0) {
							nOffset=0; //On some PCs this doesn't work and base 0 is used (//doc/element[0] is the first element) ; Don't know why -> just fix;
						}
						var nRow=0;
						var n=0, nodecount=nodes.length;
						if (pagesize && curpage>1) {
							n=(curpage-1)*pagesize;
						}
						for (; n<nodecount && (nRow<pagesize || !pagesize); n++) {
							strRow=bindTableRowString(nodes[n], n, strTMP, strPath) ;
							arRow[arRow.length]=strRow;
							if (pagesize) nRow++;
						}
						strDataSection=arRow.join("");
						bindAppenddTableRowsHTML(strDataSection, targetCTRL);
		            }
			        var strOnBindFunction = HTMLnode.getAttribute("onbind");
					if (strOnBindFunction) try { eval(strOnBindFunction.replace(/this/g,"HTMLnode")); } catch(e) {alert("ERROR PARSING onbind WHILE BINDING TABLE:\n"+strOnBindFunction+"\n\n"+e.description); }
				} //if tBodies.length>0
			} //if boundpath
		//	tmpDatasection=null;
        }//if String(HTMLnode.tagName).toLowerCase()=="table"
}


function bindAppendTableRow(rowXMLNode, objTable) {
	var tmpDatasection=document.createDocumentFragment(  );   
	var tmpNode=document.createElement("div");
	tmpDatasection.appendChild(tmpNode);
	var objTBody=objTable.tBodies[1];
	var rows = objTBody.rows;
	var strTemplate = objTable.tBodies[0].innerHTML;
	var strPath = objTable.getAttribute("boundpath");
	var nIDX = bSelectNodes("../*",rowXMLNode).length-1;
	var strRows=bindTableRowString(rowXMLNode, nIDX, strTemplate, strPath);
	tmpNode.innerHTML="<table><tbody>"+strRows+"</tbody></table>";
	var sourceRows = tmpDatasection.childNodes[0].childNodes[0].tBodies[0].rows;
	while (sourceRows.length>0) {
      objTBody.appendChild(sourceRows[0]);
    }
	tmpNode=null;
	tmpDatasection=null;
	objTable.tBodies[1].rows[objTable.tBodies[1].rows.length-1].curpath	= rowXMLNode;
	BindControls(false,objTable.tBodies[1].rows[objTable.tBodies[1].rows.length-1]);
}

//Append rows to a table using innerHTML
function bindAppenddTableRowsHTML(strRows, objTBody, bReplaceTBody) {
	var tmpDatasection=document.createDocumentFragment(  );   
	var tmpNode=document.createElement("div");
	tmpDatasection.appendChild(tmpNode);
	tmpNode.innerHTML="<table><tbody>"+strRows+"</tbody></table>";
	if (isIE6() || !bReplaceTBody) {
		var sourceRows = tmpDatasection.childNodes[0].childNodes[0].tBodies[0].rows;
		while (sourceRows.length>0) {
	      objTBody.appendChild(sourceRows[0]);
	    }
	} else {
		var sourceCTRL=tmpDatasection.childNodes[0].childNodes[0].tBodies[0];
		objTBody.parentNode.appendChild(sourceCTRL);
		objTBody.parentNode.removeChild(objTBody);
	}
	tmpNode=null;
	tmpDatasection=null;
}
function bindTableRowString(node, n, strTemplate, strPath) {
	var strRow = strTemplate.replace(/IindexI/g,n).replace(/IoddevenI/g,(n%2==0?"odd":"even")).replace(/IselectedI/g,(bSelectSingleNode("@selected",node)?"selected":""));
	strRow=strRow.replace(/IdeletedI/g,(bSelectSingleNode("@deleted",node)?"deleted":""))
	//subtables
	strRow=strRow.replace(/\<tr/gi,"<tr parentpath=\""+String(strPath).replace(/"/g,"'")+"\" bindindex=\""+String(n)+"\"");
	strRow=strRow.replace(/\[S1\]/gi,"");
	var sublevel=2;
	var rTest = new RegExp("\\[S"+sublevel+"\\]","gi");
	while (strRow.search(rTest)>-1) { 
		strRow = strRow.replace(rTest,"[S"+(sublevel-1)+"]");
		sublevel++;
		rTest = new RegExp("\\[S"+sublevel+"\\]","gi");
	}
	strRow=strRow.replace(/skip\=[\'\"]1[\'\"]/g,"");
	return strRow;
}
/******************************
 *  GetBoundNode
 *  Find the XML-node that is bound to the provided HTMLnode.
 *  Use the strAttrName to indicate which HTML attribute to use to determine the XMLnode.
 *  By default the "boundvalue" attribute is used, but some functions need boundkey
 *  
 ********************************/
function GetBoundNode(HTMLnode, strAttrName) {
	var retval;
	if (!strAttrName) strAttrName = "boundvalue";
	if (HTMLnode) {
		var strBoundValue="";
		var strWhenNotBound=HTMLnode.getAttribute("whennotbound");
		strBoundValue=HTMLnode.getAttribute(strAttrName);
		if (strBoundValue) {
			var strBAggr=HTMLnode.getAttribute("bindaggregate");
			if (strAttrName == "boundvalue" && HTMLnode.boundnode) {
				retval=HTMLnode.boundnode;
			} else if (strAttrName == "boundkey" && HTMLnode.boundkeynode) {
					retval=HTMLnode.boundkeynode;
			} else if (strBoundValue) {
				var strBoundAttribute=strBoundValue.replace(/^.*@/,""); 
				var strBoundElement=strBoundValue.replace(/\/@\w*?$/,"");
				if (strBoundElement=="/") strBoundElement=strBoundElement+"/*";
				if (strBoundValue==strBoundElement && bind_curXMLNode && bind_curXMLNode.ownerDocument) strBoundElement="."
				if (strBoundValue.indexOf("@")>-1 && !strBAggr && bSelectSingleNode(strBoundValue,bind_curXMLNode) == null && 
					bSelectSingleNode(strBoundElement,bind_curXMLNode) != null  && 
					(strBoundValue.replace(/[\/\.\@\[\]]/g,"")).length>0 &&
					(strWhenNotBound!="hide" && strWhenNotBound!="disable") && 
					HTMLnode.tagName != "IMG") {
					var tmpDnode = bSelectSingleNode(strBoundElement,bind_curXMLNode);
					var attr = tmpDnode.ownerDocument.createAttribute(strBoundAttribute);
					attr.nodeValue="";
					tmpDnode.attributes.setNamedItem(attr);
				}
				retval=bSelectSingleNode(strBoundValue,bind_curXMLNode);
			}
		}
	}
	if (strAttrName == "boundvalue" && retval && !HTMLnode.boundnode) {
		arBoundNodes[arBoundNodes.length]=HTMLnode;
		HTMLnode.boundnode=retval; 
	} else if (strAttrName == "boundkey" && retval && !HTMLnode.boundkeynode) {
		HTMLnode.boundkeynode=retval; 
	}
	return retval;
}

function GetBoundValue(HTMLnode) {
	var bnode, strNodeValue="";
    var strBindAggregate = HTMLnode.getAttribute("bindaggregate");
	var strFormatText = bind_getFormatString(HTMLnode); 
	if (bnode=GetBoundNode(HTMLnode)) {
	    var strBindExpression = HTMLnode.getAttribute("bindexpression");
		var strBV=HTMLnode.getAttribute("boundvalue");
		if (strBindExpression) {
			if (!arExpressionCache[strBindExpression]) {
				eval ("arExpressionCache[strBindExpression]=function(bnode) { return "+bind_ReplaceArg(strBindExpression,"bnode")+";}")
			}
			strNodeValue = arExpressionCache[strBindExpression](bnode);
		} else if (strBindAggregate) {
			 strNodeValue = bind_Aggregate(strBV, strBindAggregate);
		} else {
			strNodeValue = bnode.nodeValue;
			if (strNodeValue==null && bnode.childNodes[0] && bnode.childNodes[0].data) strNodeValue = bnode.childNodes[0].data;
			if (strNodeValue==null) strNodeValue="";
		}
		if (strFormatText) strNodeValue=bind_stringFormat(strNodeValue,strFormatText,true);
	} else if (strBindAggregate) {
		strNodeValue = bind_stringFormat("0",strFormatText, true);
	}
	return strNodeValue;
}
function BindTextArea(HTMLnode) {
    var strBoundValue="";
    var strNodeValue="";
    strBoundValue=HTMLnode.getAttribute("boundvalue");
    var strOnBindFunction = HTMLnode.getAttribute("onbind");
    var strBindExpression = HTMLnode.getAttribute("bindexpression");
 	var bnode=GetBoundNode(HTMLnode);
    if (strBoundValue && bnode) {
		HTMLnode.ReadXML=function () {HTMLnode.value = GetBoundValue(HTMLnode);}
		HTMLnode.ReadXML();
        if (String(HTMLnode.onchange).indexOf("WriteBackControl")==-1) {
            var TOnChange = (HTMLnode.onchange) ? HTMLnode.onchange : function() {};
			HTMLnode.onchange=function() { if (WriteBackControl(this)){ TOnChange(); } else { return false;}}
			HTMLnode.bindchange=true;
    	} 
		if (strOnBindFunction) {
			try { eval(strOnBindFunction.replace(/this/g,"HTMLnode")); } catch(e) {alert("ERROR PARSING onbind WHILE BINDING TEXTAREA CONTROL:\n"+strOnBindFunction+"\n\n"+e.description); } 
		}
    }
}

function BindInputControl(HTMLnode) {
    var strBoundValue="";
	var strBoundKey="";
    var inpType="";
    var strNodeValue="";
    var strFormatText="";
    var strValidations="";
    strValidations=HTMLnode.getAttribute("validations");
    strFormatText=HTMLnode.getAttribute("format");
    strBoundValue=HTMLnode.getAttribute("boundvalue");
	strBoundKey=HTMLnode.getAttribute("boundkey");
    inpType=String(HTMLnode.getAttribute("type")).toLowerCase();
    var strOnBindFunction = HTMLnode.getAttribute("onbind");
    var strBindExpression = HTMLnode.getAttribute("bindexpression");
	var bnode;
 	if (!HTMLnode.boundnode) { //net yet bound
	 	if (bnode=GetBoundNode(HTMLnode)) { //but it has a boundnode
			//First time to bind
	        if (inpType=="checkbox") {
				if (!HTMLnode.bindclick) {
					var tmpEvent = HTMLnode.onclick;
					HTMLnode.onclick=null;
					addEvent(HTMLnode, "click", function(){WriteBackControl(this);});
					if (tmpEvent) addEvent(HTMLnode, "click", tmpEvent);
					HTMLnode.bindclick=true;
		    	} 
			} else {
				removeEvent(HTMLnode, "blur", HandleNonBoundFormat);
				removeEvent(HTMLnode, "change", HandleNonBoundValidation);
				if (String(HTMLnode.onchange).indexOf("WriteBackControl")==-1) {
		            var TOnChange = (HTMLnode.onchange) ? HTMLnode.onchange : function() {};
					HTMLnode.onchange=function() { if (WriteBackControl(this)){ TOnChange(); } else { return false;}}
					HTMLnode.bindchange=true;
		    	} 
			} 
			if (strBoundKey) {var dummy=GetBoundNode(HTMLnode,"boundkey"); /*set the boundkeyvalue*/}
		}
	} else { bnode=GetBoundNode(HTMLnode); }
    if (bnode) {
    	if (HTMLnode.className.substr(0,12)=="wizardselect") { //Special case for the kiss4kids website which uses custom selectboxes
    		setPulldownValue(HTMLnode.parentNode,GetBoundValue(HTMLnode));
    	} else {
			switch (inpType) {
			case "text":
			case "password":
				//Set a controlcheck because IE doesn't fire OnChange when you click on an autocomplete-box
				if (!HTMLnode.bindblur) {
					var tmpEvent = HTMLnode.onblur;
					HTMLnode.onblur=null;
					addEvent(HTMLnode, "blur", function(){bind_QuickCheckControl(this);});
					if (tmpEvent) addEvent(HTMLnode, "blur", tmpEvent);
					HTMLnode.bindblur=true;
				} 
				HTMLnode.ReadXML = function() {	HTMLnode.value = GetBoundValue(HTMLnode); HTMLnode.setAttribute("backupvalue",HTMLnode.value);}
	   			break;
			case "hidden":
				HTMLnode.ReadXML = function() {	HTMLnode.value = GetBoundValue(HTMLnode); }
	    		break;
			case "checkbox":
				HTMLnode.ReadXML = function() {	
					var arrNodeValue = String(GetBoundValue(HTMLnode)).split(",");
					HTMLnode.checked=false;
					for (var hn=0; hn<arrNodeValue.length; hn++) {
						if (arrNodeValue[hn]==HTMLnode.value) {
							HTMLnode.checked = true;
							break;
						}
					} 
				}
	    		break;
			case "radio":
	    		HTMLnode.ReadXML = function() {	HTMLnode.checked=(GetBoundValue(HTMLnode)==HTMLnode.value); }
	    		break;
			} 
			HTMLnode.ReadXML();
			if (gValidateOnBind) ValidateControl(HTMLnode, false, true);
		}
		if (strOnBindFunction) try { eval(strOnBindFunction.replace(/this/g,"HTMLnode")); } catch(e) {alert("ERROR PARSING onbind WHILE BINDING INPUT CONTROL:\n"+strOnBindFunction+"\n\n"+e.description); }
    } else if (strBoundValue && !bnode) {
		//Bound value specified, but no value found in the XML -> erase
    	if (HTMLnode.className.substr(0,12)=="wizardselect") { //Special case for the kiss4kids website which uses custom selectboxes
    		setPulldownValue(HTMLnode.parentNode,"",false);
    	} else if (inpType=="text") {
    		HTMLnode.value = bind_stringFormat("",strFormatText);
    	} else if (inpType=="hidden") {
    		HTMLnode.value = "";
    	} else if (inpType=="checkbox") {
    		HTMLnode.checked=false;
    	} else if (inpType=="radio") {
			//do nothing
    	} 
   		strWhenNotBound=HTMLnode.getAttribute("whennotbound");
    	if (strWhenNotBound=="hide") { HTMLnode.style.display="none"; }	else if (strWhenNotBound=="disable") { HTMLnode.setAttribute('disabled', true);  }
    }
	if (!bnode && inpType=="text" && strValidations) {
 		if (!HTMLnode.bindchange) {
			var tmpEvent = HTMLnode.onchange;
			HTMLnode.onchange=null;
			addEvent(HTMLnode, "change", HandleNonBoundValidation);
			if (tmpEvent) addEvent(HTMLnode, "change", tmpEvent);
			HTMLnode.bindchange=true;
			if (gValidateOnBind) ValidateControl(HTMLnode);
        }
    }
    if (!bnode && inpType=="text" && strFormatText) {
    	var strtmp = HTMLnode.id + HTMLnode.onblur + HTMLnode.className;
		if (!HTMLnode.bindblur) {
			var tmpEvent = HTMLnode.onblur;
			HTMLnode.onblur=null;
			addEvent(HTMLnode, "blur", HandleNonBoundFormat);
			if (tmpEvent) addEvent(HTMLnode, "blur", tmpEvent);
			HTMLnode.bindblur=true;
			FormatControl(HTMLnode);
        }
    }
	if (strValidations && String(HTMLnode.onkeypress).indexOf("ValidationKeyPressHandler")==-1){
		var strMainValidation="";
		var strVals=","+strValidations+","
		if (strValidations.indexOf(",currency,")>-1) strMainValidation="currency";
		if (strValidations.indexOf(",number,")>-1) strMainValidation="number";
		if (strValidations.indexOf(",date,")>-1) strMainValidation="date";
		if (strValidations.indexOf(",time,")>-1) strMainValidation="time";
		if (strMainValidation!="") {
		 	HTMLnode.onkeypress=function(event) {  return ValidationKeyPressHandler(event, this, bind_AllowedChars[strMainValidation], bind_ErrorPattern[strMainValidation]);}
		}
	}
}
function HandleNonBoundFormat(evt) {
	if (evt.originalTarget && evt.originalTarget.getAttribute("format")!="") {
		FormatControl(evt.originalTarget);
	} else if (event.srcElement) {
		FormatControl(evt.srcElement);
	}
}
function HandleNonBoundValidation(evt) {
	if (evt.originalTarget && evt.originalTarget.getAttribute("validations")!="") {
		return ValidateControl(evt.originalTarget);
	} else if (event.srcElement) {
		return ValidateControl(evt.srcElement);
	}
}
function BindSelectControl(HTMLnode) {
	var strBP=HTMLnode.getAttribute("boundpath");
	var strValidations = HTMLnode.getAttribute("validations");
	if (strBP) {
		var bFirstRow=(HTMLnode.getAttribute("bindfirstrow")!="0");
		var template = false;
		if (HTMLnode.childNodes[0].tagName) {
			var tIdx=0;
			template = HTMLnode.childNodes[0];
		} else if (HTMLnode.childNodes[1].tagName) {
			var tIdx=1;
			template = HTMLnode.childNodes[1];
		}		
		if (template) {
			for (var c=HTMLnode.childNodes.length-1;c>tIdx; c--) {
				HTMLnode.removeChild(HTMLnode.childNodes[c]);
			}
			var parNodes = bSelectNodes(strBP, bind_curXMLNode);
			var newChild, strBVal, strBKey, tmpVal, tmpNode,strFormatText;
			for (var p=0; p<parNodes.length; p++) {
				if (bFirstRow && p==0) {
					newChild = template;
				} else {
					newChild = HTMLnode.appendChild(template.cloneNode(true));
				}
				strFormatText=newChild.getAttribute("format");
				strBVal=newChild.getAttribute("boundvalue");
				strBKey=newChild.getAttribute("boundkey");
				if (strBVal) {
					tmpVal=false;
					tmpNode=bSelectSingleNode(strBVal,parNodes[p]);
					if (tmpNode.nodeValue && tmpNode.nodeValue!=null) tmpVal=tmpNode.nodeValue;
					else if (tmpNode.childNodes[0].data!=null) tmpVal=tmpNode.childNodes[0].data;
					if (tmpVal) newChild.text=bind_stringFormat(tmpVal, strFormatText,true);
				}
				if (strBKey) {
					tmpVal=false;
					tmpNode=bSelectSingleNode(strBKey,parNodes[p]);
					if (tmpNode.nodeValue && tmpNode.nodeValue!=null) tmpVal=tmpNode.nodeValue;
					else if (tmpNode.childNodes[0].data!=null) tmpVal=tmpNode.childNodes[0].data;
					if (tmpVal) newChild.value=tmpVal;
				}
			}
		}
	}
	var strBV=HTMLnode.getAttribute("boundvalue");
	var bnode=GetBoundNode(HTMLnode);
	var strNodeValue="";
	if (strBV && bnode) {
		HTMLnode.ReadXML=function() {HTMLnode.value = GetBoundValue(HTMLnode);}
		HTMLnode.ReadXML();
		
// <RvG20070821> KISS ONLY! BETA-versie... Eventuele fouten graag melden bij mij... 
		if (HTMLnode.getAttribute("customized")=='1') { $pulldown(HTMLnode.name).setValue(HTMLnode.value);}
// </RvG20070821>
		
        eval("HTMLnode.onchange=function(){ " +(HTMLnode.onchange!=null?String(HTMLnode.onchange).replace("function anonymous()","").replace("function onchange(event)",""):"")+"; WriteBackControl(this);};");
        var strOnBindFunction = HTMLnode.getAttribute("onbind");
		if (strOnBindFunction) try { eval(strOnBindFunction.replace(/this/g,"HTMLnode")); } catch(e) {alert("ERROR PARSING onbind WHILE BINDING SELECT CONTROL:\n"+strOnBindFunction+"\n\n"+e.description); }
		if (gValidateOnBind) ValidateControl(HTMLnode, false, true);		
	} else {
		if (HTMLnode.getAttribute("whennotbound")=="hide") HTMLnode.style.display="none";
    	if (HTMLnode.getAttribute("whennotbound")=="disable") HTMLnode.setAttribute('disabled', true);  
	}
	if (!strBV && strValidations) {
		if (gValidateOnBind) ValidateControl(HTMLnode, false, true);		
    	if (String(HTMLnode.onchange).indexOf("ValidateControl")==-1) {
            eval("HTMLnode.onchange=function(){ " +(HTMLnode.onchange!=null?String(HTMLnode.onchange).replace("function anonymous()","").replace("function onchange(event)",""):"")+"; return ValidateControl(this);};");
        }
    }	
}

function BindInnerHTML(HTMLnode) {
	var strBP=HTMLnode.getAttribute("boundpath");
	if (strBP) {
		var template = false;
		if (HTMLnode.childNodes[0].tagName) {
			var tIdx=0;
			template = HTMLnode.childNodes[0];
		} else if (HTMLnode.childNodes[1].tagName) {
			var tIdx=1;
			template = HTMLnode.childNodes[1];
		}		
		if (template) {
			HTMLnode.curpath = bind_curXMLNode;
			template.isTemplate=true;
			for (var c=HTMLnode.childNodes.length-1;c>tIdx; c--) {
				HTMLnode.removeChild(HTMLnode.childNodes[c]);
			}
			var parNodes = bSelectNodes(strBP, bind_curXMLNode);
			for (var p=0; p<parNodes.length; p++) {
				var newChild = HTMLnode.appendChild(template.cloneNode(true));
				newChild.isTemplate=false;
				// strRow=strRow.replace(/\<tr/gi,"<tr parentpath=\""+String(strPath).replace(/"/g,"'")+"\" bindindex=\""+String(n)+"\"");
				newChild.setAttribute("parentpath",strBP);
				newChild.setAttribute("bindindex",p);
				//newChild.curpath = parNodes[p];
				newChild.style.display="";
				var strOnBindFunction = newChild.getAttribute("onbind");
				if (strOnBindFunction) try { eval(strOnBindFunction.replace(/this/g,"newChild")); } catch(e) {alert("ERROR PARSING onbind WHILE BINDING INNERHTML:\n"+strOnBindFunction+"\n\n"+e.description); }
			}
			template.style.display="none";
		}
	}
    var strOnBindFunction = HTMLnode.getAttribute("onbind");
	var strBV = HTMLnode.getAttribute("boundvalue");
	var strFormatText = bind_getFormatString(HTMLnode); 
    var strBindAggregate = HTMLnode.getAttribute("bindaggregate");
	var startnode=false;
	var eurosign = "&euro;";
	var bnode=GetBoundNode(HTMLnode);
	if (strBV) {
		if (bnode && bnode!=null) {
			HTMLnode.ReadXML = function() { HTMLnode.innerHTML = GetBoundValue(HTMLnode); }
			HTMLnode.ReadXML();
			if (strOnBindFunction) try { eval(strOnBindFunction.replace(/this/g,"HTMLnode")); } catch(e) {alert("ERROR PARSING onbind WHILE BINDING INNERHTML:\n"+strOnBindFunction+"\n\n"+e.description); }
	    } else {
			if (strBindAggregate) {
				HTMLnode.innerHTML = bind_stringFormat("0",strFormatText, true);
			} else {
				HTMLnode.innerHTML = "";
			}
	    }
	}
}

function BindAnchor(HTMLnode) {
	var strBV=HTMLnode.getAttribute("boundvalue");
	var strRef=HTMLnode.getAttribute("bindhref");
	if (strRef && strBV) {
		var bnode=GetBoundNode(HTMLnode);
		if (bnode && bnode!=null) {
			HTMLnode.ReadXML = function() {
				var strValue=GetBoundValue(HTMLnode);
				var strRef=HTMLnode.getAttribute("bindhref");
				if (strValue && strValue!="") {
		            HTMLnode.href = strRef.replace(/IvalueI/g,strValue);
		        } else {
		        	HTMLnode.removeAttribute("href");
		        }
			}
			HTMLnode.ReadXML();
	    } else {
	    	HTMLnode.removeAttribute("href");
	    }
	} //if bindvalue
    var strOnBindFunction = HTMLnode.getAttribute("onbind");
	if (strOnBindFunction) try { eval(strOnBindFunction.replace(/this/g,"HTMLnode")); } catch(e) {alert("ERROR PARSING onbind WHILE BINDING ANCHOR:\n"+strOnBindFunction+"\n\n"+e.description); }
}

function BindImage(HTMLnode) {
	var strBV=HTMLnode.getAttribute("boundvalue");
	if (strBV) {
		if (!HTMLnode.getAttribute("orgsrc")) HTMLnode.setAttribute("orgsrc",HTMLnode.getAttribute("src"));
		var bnode=GetBoundNode(HTMLnode);
		if (bnode) {
			HTMLnode.ReadXML = function() {
				var strSrc=HTMLnode.getAttribute("bindsrc");
				var strNodeValue=GetBoundValue(HTMLnode);
				if (strNodeValue!="") {
					if (strSrc) HTMLnode.src = strSrc.replace(/IvalueI/g,strNodeValue);
		            HTMLnode.style.display="block";
				} else {
			    	if (!HTMLnode.getAttribute("orgsrc")) {
			        	HTMLnode.style.display="none";
					} else {
						HTMLnode.src = HTMLnode.getAttribute("orgsrc");
			        }
				}
			}
			HTMLnode.ReadXML();
	    } else {
			if (!HTMLnode.getAttribute("orgsrc")) {
	        	HTMLnode.style.display="none";
			} else {
				HTMLnode.src = HTMLnode.getAttribute("orgsrc");
	        }
	    }
	} //if bindvalue
    var strOnBindFunction = HTMLnode.getAttribute("onbind");
	if (strOnBindFunction) try { eval(strOnBindFunction.replace(/this/g,"HTMLnode")); } catch(e) {alert("ERROR PARSING onbind WHILE BINDING IMAGE:\n"+strOnBindFunction+"\n\n"+e.description); }
}

/***************************
 * Write the value of an input control back to the XML document
******************/
function WriteBackControl(ctrl, bHideError) {
   	var inpType=ctrl.getAttribute("type");
	if (!inpType && ctrl.tagName=="select") { inpType="select-one"; }
	var strBV=ctrl.getAttribute("boundvalue");
	var strBK=ctrl.getAttribute("boundkey");
	var strValidations=String(ctrl.getAttribute("validations"));
	var strFormat=bind_getFormatString(ctrl);
	var multiplier=1, decimals=2;
	//do we need to shift decimals??
    if (strFormat && strFormat.indexOf("decimals")>0) {
	    var arFormat=strFormat.split(",");
	    for (var f=0; f<arFormat.length; f++) {
	        if (arFormat[f] && arFormat[f].substr(0,9)=="decimals=") {
				decimals=arFormat[f].substr(9);
			}
		}
	    if (String(decimals).indexOf("/")>-1) {
			var arTemp = String(decimals).split("/");
			decimals=Number(arTemp[0]);
			multiplier=Math.pow(10,Number(arTemp[1]));
		}
	}
	var retval=true;
	if (ctrl.boundnode) {
		var strBeforeWriteback=ctrl.getAttribute("beforewriteback");
		if (strBeforeWriteback) eval(strBeforeWriteback.replace(/this/g,"ctrl"));
		var strValue = ctrl.value;
		if (strFormat.indexOf("currency")>-1 || strFormat.indexOf("number")>-1) { strValue = strValue.replace(new RegExp("\\"+Bind_ThousandSeparator,"g"),""); }
		strValue = bind_stringDeformat(strValue,strFormat);
		if (ctrl.className.substr(0,12)=="wizardselect") {
			strValue=ctrl.parentNode.childNodes[1].value;
		} else if (inpType=="checkbox") {
			if (ctrl.name) {
				var HTMLnodes = document.getElementsByName(ctrl.name);
				strValue = "";
				for (var hn=0; hn<HTMLnodes.length; hn++) {
					if (HTMLnodes[hn].checked) {strValue += (strValue?",":"")+HTMLnodes[hn].value;}
				}
			} else {
				strValue = ctrl.checked?ctrl.value:"";
			}
		} else if (inpType=="radio") {
			strValue = ctrl.checked?ctrl.value:"";
		} else if (inpType=="select-one") {
			strValue = ctrl.value;
		} 
		retval=ValidateControl(ctrl,false,bHideError);
		if (multiplier!=1 && retval) {
			var decmp=Math.pow(10,Number(decimals));
			strValue = Math.round(strValue*multiplier*decmp)/decmp;
		}
		if (retval) {
    		var bnode=ctrl.boundnode;
			if (bnode.nodeValue!=null) {
				bnode.nodeValue=strValue;
			} else if (bnode.text!=null) {
				bnode.text=strValue;
			} else {
				if (bnode.childNodes.length==0) {
					bnode.appendChild(bind_XMLdoc.createTextNode(strValue));
				}
				bnode.childNodes[0].data=strValue;
			}	

			if (gDirtyElements) {
				//mark the element or parent-element as dirty
				var attr = bnode.ownerDocument.createAttribute("dirty");
				attr.nodeValue="1";
				if (bnode.attributes && bnode.attributes!=null) {
					bnode.attributes.setNamedItem(attr);
				} else {
					bSelectSingleNode("..",bnode).attributes.setNamedItem(attr);
				}
			}
			if (gDirtyDocument) {
				//mark the document as dirty
				var tmpDnode = bSelectSingleNode("//*",bind_XMLdoc); //the first node found
				if (tmpDnode) {
					var attr = tmpDnode.ownerDocument.createAttribute("dirty");
					attr.nodeValue="1";
					tmpDnode.attributes.setNamedItem(attr);
				}
			}
			if (inpType=="text" && ctrl.className.substr(0,12)!="wizardselect") {
				ctrl.value = bind_stringFormat(strValue,strFormat, true);
			} 
			var strAfterWriteback=ctrl.getAttribute("afterwriteback");
			if (strAfterWriteback) try { eval(strAfterWriteback.replace(/this/g,"ctrl")); } catch(e) {}
		}
	}
	return retval;
}

function bind_ReplaceArg(strOnbindString, strFirstArg) {
	var nLeftBracketPos=strOnbindString.indexOf("(");
	var nRightBracketPos=strOnbindString.indexOf(",");
	if (nRightBracketPos<0) nRightBracketPos = strOnbindString.indexOf(")");
	return strOnbindString.substr(0,nLeftBracketPos+1) + strFirstArg + strOnbindString.substr(nRightBracketPos);
}

function bind_Aggregate(strBV, strfunction, startNode) {
	if (!startNode) startNode=bind_curXMLNode;
	var vallist=bSelectNodes(strBV, startNode);
	var nSum=0;
	for (var v=0; v<vallist.length; v++) {
		strNodeValue = vallist[v].nodeValue;
		if (strNodeValue==null && vallist[v].childNodes[0] && vallist[v].childNodes[0].data) strNodeValue = vallist[v].childNodes[0].data;
		if (strNodeValue==null) strNodeValue="";
		if (strNodeValue!="" && bind_IsNumeric(strNodeValue)) {
			nSum+=Number(strNodeValue);
		}
	}
	if (vallist.length==0) return "0";
	else if (strfunction.toUpperCase()=="SUM") return String(nSum);
	else if (strfunction.toUpperCase()=="COUNT") return String(v);
	else if (strfunction.toUpperCase()=="AVG") return String(nSum/v);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//      Validation functions
/////////////////////////////////////////////////////////////////////////////////////////////////////

function ValidateControl(ctrl, bKeepColor, bHideError) {
	var strValidations=ctrl.getAttribute('validations');
	var strFormats=ctrl.getAttribute('format');
	if (!strFormats && strValidations && (strValidations.indexOf("currency")>-1 || strValidations.indexOf("number")>-1 )) strFormats="number";
	if (strFormats && (strFormats.indexOf("currency")>-1 || strFormats.indexOf("number")>-1)) {
		retval = ValidateValue(bind_stringDeformat(ctrl.value,strFormats),strValidations, !bHideError);
	} else {
		retval = ValidateValue(ctrl.value,strValidations, !bHideError);
	}
	if (!bKeepColor && !retval ) {
	  if (Bind_ErrorClass && Bind_ErrorClass!="") {
		if(ctrl.className!=Bind_ErrorClass) ctrl.setAttribute("rememberClass",String(ctrl.className));
	  	ctrl.className=Bind_ErrorClass;
  	  }
	  if (Bind_ErrorColor && Bind_ErrorColor!=""){
		if(ctrl.style.color!=Bind_ErrorColor) ctrl.setAttribute("rememberColor",String(ctrl.style.color));
	  	ctrl.style.color=Bind_ErrorColor;
	  }
	} else if (!bKeepColor) {
	  if (ctrl.style.color==Bind_ErrorColor) ctrl.style.color=ctrl.getAttribute("rememberColor");
	  if (ctrl.className==Bind_ErrorClass) ctrl.className=ctrl.getAttribute("rememberClass");
	}
   return retval;
}
    
function ValidateValue(strNewValue, strValidations, bShowError) {
	//This function validates the desired value strNewValue and returns false or a (corrected) value;
	var retval=true;
	var strCustomError;
	var strError="";
	var strValue=strNewValue;
	if (strValidations) var arValidations=strValidations.split(","); else var arValidations=new Array();
       for (var v=0; v<arValidations.length; v++) {
		if (arValidations[v]=="currency") {
			if (isNaN(strValue)) {
				retval=false;
				strError += gErrTable["currency"].replace(/\%1/g,Bind_DecimalSeparator)+"\n";
			}
		} else if (arValidations[v]=="notnull" && strValue=="") {
			retval=false;
			strError += gErrTable["notnull"]+"\n";
		} else if (arValidations[v]=="time" && !bind_validateTime(strValue)) {
			retval=false;
			strError += gErrTable["time"]+"\n";
		} else if (arValidations[v]=="date" && !bind_validateDate(strValue)) {
			retval=false;
			strError += gErrTable["date"]+"\n";
		} else if (arValidations[v]=="positive" && Number(strValue) < 0) {
			retval=false;
			strError += gErrTable["positive"]+"\n";
		} else if (arValidations[v]=="number" && isNaN(strValue)) {
			retval=false;
			strError += gErrTable["number"].replace(/\%1/g,Bind_DecimalSeparator)+"\n";
		} else if (arValidations[v].substr(0,4)=="min=") {
			if (strValue<Number(arValidations[v].substr(4))) {
				retval=false;
				strError += gErrTable["minimum"].replace(/\%1/g,arValidations[v].substr(4))+"\n";
			}
		} else if (arValidations[v].substr(0,4)=="max=") {
			if (strValue>Number(arValidations[v].substr(4))) {
				retval=false;
				strError += gErrTable["maximum"].replace(/\%1/g,arValidations[v].substr(4))+"\n";
			}
		} else if (arValidations[v].substr(0,9)=="function=") {
			strCustomError=eval(arValidations[v].substr(9)+"('"+strValue+"')");
			if (strCustomError!="") {
				retval=false;
				strError += strCustomError;
			}
		};
	} //for (var v=0; v<arValidations.length; v++)
	if (strError != "" && bShowError && !gSurpressValidationErrors) alert(strError,"Error.");
	return retval;
}

function ValidationKeyPressHandler(evt, ctrl, allowedchars, errorpattern) {
  if(window.event) {
    evt=window.event;
    var keynum = evt.keyCode; 
  } else var keynum = evt.which;
	var keychar = String.fromCharCode(keynum);
	if (keynum==0 && evt.keyCode!=0) {keynum=evt.keyCode; keychar=""}
	return (keynum==8 || keynum==13 || keynum==9 || (keynum==37 && keychar=="") || (keynum==39 && keychar=="") || (allowedchars.test(keychar) && (!errorpattern || !errorpattern.test(String(ctrl.value)+keychar))) || (keynum==46 && keychar==""));
	return (keynum==8 || keynum==13 || keynum==9 || (keynum==37 && keychar=="") || (keynum==39 && keychar=="") || (allowedchars.test(keychar) && (!errorpattern || !errorpattern.test(String(ctrl.value)+keychar))) || (keynum==46 && keychar==""));
}

function bind_QuickCheckControl(ctrl) {
	// check to see whether or not the value should be rewritten to the XML
	var retval=true;
	if (ctrl.getAttribute("backupvalue") != ctrl.value) {
		if (!ctrl.getAttribute("boundkey") ) {
			//controls with a boundkey  show a lookup value -> no rewrite
			//this control shows a real XMLvalue -> we need to rewrite te value to XML
			WriteBackControl(ctrl, true);
		} else {
			retval=false;
		}
	}
	return retval;
}
	
/////////////////////////////////////////////////////////////////////////////////////////////////////
//      Formatting functions
/////////////////////////////////////////////////////////////////////////////////////////////////////
function FormatControl(ctrl) {
	var strValue = ctrl.value;
	var bSkipDecimalRecalc=false;
	var strFormat = bind_getFormatString(ctrl);
	if (strFormat.indexOf("currency")>-1 || strFormat.indexOf("number")>-1) { strValue = strValue.replace(new RegExp("\\"+Bind_ThousandSeparator,"g"),""); }
	if (!ctrl.boundnode && strFormat.search(/decimals=\d*\//)>-1) bSkipDecimalRecalc=true;
	ctrl.value=bind_stringFormat(strValue,strFormat,false,bSkipDecimalRecalc) ;
}

function bind_stringDeformat(strValue, strFormat) {
    //remove prefixes and suffixes and such from the value
	var retval=String(strValue);
	var strPrefix="";
	var strSuffix="";
	var decimals=2;
    if (strFormat) var arFormat=strFormat.split(","); else var arFormat=new Array();
    for (var f=0; f<arFormat.length; f++) {
        if (arFormat[f].substr(0,8)=="currency" && retval!="") {
			retval = retval.replace(Bind_DecimalSeparator,".");
			var arRetval = retval.split(".");
			if (arRetval.length>1) {
				retval="";
				for (var r=0; r<arRetval.length-1; r++) { retval += arRetval[r]; }
				retval += "."+arRetval[r++];
			}
            strPrefix = (arFormat[f].substr(9)?arFormat[f].substr(9):"€");
		} else if (arFormat[f].substr(0,5).toLowerCase()=="mtime") {
			if (retval.indexOf(":")>-1) {
				var arTime = retval.split(":");
				retval = String((Number(arTime[0]) * 60) + Number(arTime[1])); 
			}
		} else if ((arFormat[f].indexOf("DMY")>-1 || arFormat[f].indexOf("MDY")>-1 )) {
			var tmpdate=bind_validateDate(retval);
			if (tmpdate) retval=String(tmpdate);
		} else if (arFormat[f].substr(0,5).toLowerCase()=="hh.mm" || arFormat[f].substr(0,5).toLowerCase()=="hh:mm") {
			var tmptime = bind_validateTime(retval); 
			if (tmptime) retval=String(tmptime);
		} else if ((arFormat[f]=="number") && retval!="") {
			retval=retval.replace(Bind_DecimalSeparator,".");
			var arRetval = retval.split(".");
			if (arRetval.length>1) {
				retval="";
				for (var r=0; r<arRetval.length-1; r++) { retval += arRetval[r]; }
				retval += "."+arRetval[r++];
			}
        } else if (arFormat[f].substr(0,6)=="suffix" && retval!="") {
            strSuffix = arFormat[f].substr(7);
        } else if (arFormat[f].substr(0,6)=="prefix" && retval!="") {
            strPrefix = arFormat[f].substr(7);
        } else if (arFormat[f]=="name" && retval!="") {
			retval = retval.substr(0,1).toUpperCase() + retval.substr(1).toLowerCase();
        } else if (arFormat[f]=="capitalize" && retval!="") {
			retval = retval.substr(0,1).toUpperCase() + retval.substr(1);
        } else if (arFormat[f]=="uppercase" && retval!="") {
			retval = retval.toUpperCase();
        } else if (arFormat[f]=="lowercase" && retval!="") {
			retval = retval.toLowerCase();
        } else if (arFormat[f].substr(0,9)=="function=" && retval!="") {
			var arFuncs=arFormat[f].substr(9).split("/");
			if (arFuncs.length>1) retval = eval(arFuncs[1]+"('"+retval+"');");
        }
    } //for (var f=0; f<arFormat.length; f++)
	strPrefix=strPrefix.replace(String.fromCharCode(160),String.fromCharCode(32));
	retval=retval.replace(String.fromCharCode(160),String.fromCharCode(32));
    if (retval.substr(0,strPrefix.length)==strPrefix) retval=retval.substr(strPrefix.length);
    if (retval.substr(retval.length-strSuffix.length,retval.length)==strSuffix) retval=retval.substr(0,retval.length-strSuffix.length);
	retval = retval.replace(/\\/g,"");//.replace(/\;/g,"").replace(/\"/g,"");
	return retval;
}
function bind_getFormatString(ctrl) {
	var strFormat = ctrl.getAttribute("format");
	var strCond="";
	var bNode = GetBoundNode(ctrl);
    if (bNode) {
		if (strFormat) var arFormat=strFormat.split(","); else var arFormat=new Array();
	    strFormat="";
		for (var f=0; f<arFormat.length; f++) {
			strCond="";
			if (arFormat[f].indexOf("(") > -1) {
				//parse condition
				strCond=arFormat[f].substr(arFormat[f].indexOf("(")+1,String(arFormat[f]).length - arFormat[f].indexOf("(") - 2);
				if (strCond && bSelectNodes(strCond,bNode).length > 0) {
					strFormat += "," + arFormat[f].substr(0,arFormat[f].indexOf("("));
				}
			} else {
				strFormat += "," + arFormat[f];
			}
		}
		if (strFormat.substr(0,1)==",") strFormat = strFormat.substr(1);
	}
	return strFormat;
}

function bind_stringFormat(strValue,strFormat, bSkipDeformat,bSkipDecimalRecalc) {
    //Apply format(s) to the strValue
	var retval=strValue;
	var strPrefix="";
	var strSuffix="";
	var decimals=2;
    var devider=1;
	var arTmp;
    if (strFormat) {
		if (!bSkipDeformat) retval=bind_stringDeformat(strValue,strFormat);
		var arFormat=strFormat.split(","); 
	    for (var f=0; f<arFormat.length; f++) {
			tmpFormat=arFormat[f];
	        if (tmpFormat=="DMYHMS") {
	        	retval = bind_stringFormat(retval.substr(0,8),"DMY",true)+" "+bind_stringFormat(retval.substr(8,6),"hh:mm:ss",true);
	        } else if (tmpFormat=="DMY") {
	           if (bind_validateDate(retval)) retval=bind_validateDate(retval);
	            if (retval.length>7 && retval.indexOf("-")<0 && retval.indexOf("/")<0) {
	                retval = retval.substr(6,2)+"-"+retval.substr(4,2)+"-"+retval.substr(0,4);
	            }
	        } else if (tmpFormat=="MDY") {
	           if (bind_validateDate(retval)) retval=bind_validateDate(retval);
	            if (retval.length>7 && retval.indexOf("-")<0 && retval.indexOf("/")<0) {
	                retval = retval.substr(4,2)+"-"+retval.substr(6,2)+"-"+retval.substr(0,4);
	            }
			} else if (tmpFormat.toLowerCase()=="mtime") {
				retval = Number(retval);
				retval = String((Math.floor(retval / 60) < 10 ? "0" : "") + Math.floor(retval / 60)) + ":" + String((Number(retval)%60 < 10 ? "0": "") + Number(retval)%60);
	        } else if (tmpFormat.substr(0,5).toLowerCase()=="hh:mm") {
	        	if (bind_validateTime(retval)) retval=bind_validateTime(retval);
				if (retval.length > 3 && tmpFormat.substr(0,1)=="h" && retval.substr(0,1)=="0") 
					retval = retval.substr(1,1)+":"+retval.substr(2,2);
				else if (String(retval).length > 4 && tmpFormat.substr(6,2).toLowerCase()=="ss")
					retval = retval.substr(0,2)+":"+retval.substr(2,2) + ":"+retval.substr(4,2) ;
				else if (String(retval).length > 3)
					retval = retval.substr(0,2)+":"+retval.substr(2,2);
		    } else if (tmpFormat.substr(0,5).toLowerCase()=="hh.mm") {
		        	if (bind_validateTime(retval.replace(/\./g,":"))) retval=bind_validateTime(retval.replace(/\./g,":"));
					if (retval.length > 3 && tmpFormat.substr(0,1)=="h" && retval.substr(0,1)=="0") 
						retval = retval.substr(1,1)+"."+retval.substr(2,2);
					else if (String(retval).length > 4 && tmpFormat.substr(6,2).toLowerCase()=="ss")
						retval = retval.substr(0,2)+"."+retval.substr(2,2) + "."+retval.substr(4,2) ;
					else if (String(retval).length > 3)
						retval = retval.substr(0,2)+"."+retval.substr(2,2);
			} else if (tmpFormat.substr(0,9)=="decimals=" && retval!=="") {
	            decimals=tmpFormat.substr(9);
			    if (String(decimals).indexOf("/")>-1) {
					var arTemp = String(decimals).split("/");
					decimals=Number(arTemp[0]);
					devider=Math.pow(10,Number(arTemp[1]));
				}
				if (!bSkipDecimalRecalc) retval /= devider;
				retval = bind_FormatCurrency(retval,decimals,3);//Number(retval).toFixed(tmpFormat.substr(9));
	        } else if (tmpFormat.substr(0,8)=="currency" && retval!="") {
	            if (arFormat[f+1] && arFormat[f+1].substr(0,9)=="decimals=") {
					decimals=arFormat[f+1].substr(9);
					arFormat[f+1]="";
				}
			    if (String(decimals).indexOf("/")>-1) {
					var arTemp = String(decimals).split("/");
					decimals=Number(arTemp[0]);
					devider=Math.pow(10,Number(arTemp[1]));
				}
				if (!bSkipDecimalRecalc) retval /= devider;
				strPrefix = (tmpFormat.substr(9)?tmpFormat.substr(9):"€") + " ";
	            retval = bind_FormatCurrency(retval,decimals,3)
	        } else if (tmpFormat.substr(0,7)=="boolean") {
	        	if (String(tmpFormat.substr(8)).length>2) arTmp=String(tmpFormat.substr(8)).split(":"); else arTmp=new Array("Y","N");
	            retval = (retval!="0" && String(retval).toLowerCase()!="false" && String(retval)!="")?arTmp[0]:arTmp[1];
	        } else if (tmpFormat.substr(0,6)=="suffix" && retval!="") {
	            strSuffix = tmpFormat.substr(7);
	        } else if (tmpFormat.substr(0,6)=="prefix" && retval!="") {
	            strPrefix = tmpFormat.substr(7);
	        } else if (tmpFormat.substr(0,6)=="CRfix" && retval!="") {
				retval = retval.replace(/\n/g,"<br />");
	        } else if (tmpFormat=="name" && retval!="") {
				retval = retval.substr(0,1).toUpperCase() + retval.substr(1).toLowerCase();
	        } else if (tmpFormat=="capitalize" && retval!="") {
				retval = retval.substr(0,1).toUpperCase() + retval.substr(1);
	        } else if (tmpFormat=="uppercase" && retval!="") {
				retval = retval.toUpperCase();
	        } else if (tmpFormat=="lowercase" && retval!="") {
				retval = retval.toLowerCase();
	        } else if (tmpFormat=="hidezero" && retval!="" && Number(strValue)==0) {
				retval = "&nbsp;";
	        } else if (tmpFormat=="memo" && retval!="") {
				retval = retval.replace(/\n/g,"<br />");
	        } else if (tmpFormat.substr(0,9)=="function=" && retval!="") {
				var arFuncs=tmpFormat.substr(9).split("/");
				retval = eval(arFuncs[0]+"('"+retval+"');");
	        }
	    } //for (var f=0; f<arFormat.length; f++)
	    retval=strPrefix+retval+strSuffix;
	}
    return retval;
}
   
function bind_validateNumber(number) {
	number = String(number).replace(Bind_ThousandSeparator,Bind_DecimalSeparator);
	if (isNaN(number)) {return '0'}
	else if (number == 0) {return '0'}
	else {
		number = Math.ceil(number);
		return String(number).replace(".",Bind_DecimalSeparator);
	}
}

function bind_validateCurrency(number) {
	number = String(number).replace(Bind_ThousandSeparator,Bind_DecimalSeparator);
	if (isNaN(number)) {return false}
	else if (number == 0) {return '0'+Bind_DecimalSeparator+'00'}
	else {
		number = Math.round(number * 100);
		return String(number).substring(0,String(number).length-2)+Bind_DecimalSeparator+String(number).substring(String(number).length-2,String(number).length)
	}
}

function bind_validateDate(datum) {

	// Date validation-function based on validation-script of
	// Sandeep V. Tamhankar (stamhankar@hotmail.com) -->

	// Checks for the following valid date formats: (corrected)
	// DD/MM/YYYY DD-MM-YYYY
	var datePat = /^(\d{1,2})(\/|-|\s)(\d{1,2})\2(\d{4})$/; // requires 4 digit year
	var dateStr = new String(datum);
	if (dateStr.length<8) dateStr += " "+String(new Date().getFullYear());
	dateStr=dateStr.replace("  "," ");
	var matchArray = dateStr.match(datePat); // is the format ok?
	if (matchArray == null) {
		return false;
	}
	var day = matchArray[1]; // parse date into variables
	var month = matchArray[3];
	var year = matchArray[4];
	if (month < 1 || month > 12) { // check month range
		return false;
	}
	if (day < 1 || day > 31) {
		return false;
	}
	if ((month==4 || month==6 || month==9 || month==11) && day==31) {
		return false;
	}
	if (month == 2) { // check for february 29th
		var isleap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
		if (day>29 || (day==29 && !isleap)) {
			return false;
		}
	}
	day 	= String(day);
	month = String(month);
	year	= String(year);
	if (day.length < 2) {day = '0' + day}
	if (month.length < 2) {month = '0' + month}

	return year+month+day;
}

function bind_IsNumeric(sText) { 
   var ValidChars = "0123456789.-"; 
   var Char;
   for (i = 0; i < sText.length; i++) {
       Char = sText.charAt(i);
       if (ValidChars.indexOf(Char) == -1) {
          return false;
       }
    }
   return true;
}

function bind_validateTime(strTime) {
	/* time validation. allows the following formats
	 * hh:mm:ss
	 * hh:mm
	 * Returns false if a valid time cannot be found
	 * otherwise returns the time in 'xml'-format hhmmss	 	 	 
	*/
	var timePat = /^(\d{1,2})([:.])?(\d{1,2})([:.])?(\d{1,2})?$/; 
	var timeStr = new String(strTime);

	var matchArray = timeStr.match(timePat); // is the format ok?
	if (matchArray == null) {
		return false;
	}
	var hours = matchArray[1];
	var minutes = matchArray[3];
	if (matchArray[5]) var seconds = matchArray[5]; else var seconds=0;
	if (hours < 0 || hours > 23) return false;
	if (minutes < 0 || minutes > 59) return false;
	if (seconds < 0 || seconds > 59) return false;
	hours=String(hours);
	minutes=String(minutes);
	seconds=String(seconds);
	if (hours.length < 2) hours = '0' + hours;
	if (minutes.length < 2) minutes = '0' + minutes;
	if (seconds.length < 2) seconds = '0' + seconds;
	return hours+minutes+seconds;
}

function bind_FormatCurrency( value, decimals, group ) {
	if( decimals == null ) decimals = 2;
    if( group == null ) group = 0;	// geen duizentallen
	if (isNaN(value)) {
		return false;
    }
	var value =  String(Number(value).toFixed(decimals)).replace(/\./g,Bind_DecimalSeparator);
	if( group )
    {	
    	var negative=value.indexOf("-")>-1;
    	if (negative) value=value.replace("-","");
		var parts = value.split(Bind_DecimalSeparator);
        var number = '';
        var count = 0;
        for( var i=parts[0].length-1; i >= 0; i-- )
        {
            if( count == 3 )
            {
            	count = 0 ;
            	number = Bind_ThousandSeparator + number ;
            }
            number = parts[0].charAt(i) + number;
        	count++;
        }
        return (negative?"-":"")+number + (parts.length > 1?Bind_DecimalSeparator+parts[1]:"");
    }
	if (negative) value="-"+value;
	return value;
}
 
function bind_SelectNode(node, strMultiple) {
	/* set the passed node to both @selected and @current
	 * depending on the strMultiple parameter other nodes will be / stay selected
	*/
	var nodelist;
	var attr;
	var curnode_index, selnode_index;
	if (node && node.parentNode) {
		nodelist = bSelectNodes("../*",node);
		if (strMultiple=="range") { //select all nodes between current and selected node.
			for (var n=0; n<nodelist.length; n++) {
				if (bSelectSingleNode("@current",nodelist[n])!=null) curnode_index=n;
				if (nodelist[n]==node) selnode_index=n;
			}
			if(selnode_index>curnode_index) var step=-1; else step=1;
			for (var n=selnode_index; n!=curnode_index; n+=step) {
				if (bSelectSingleNode("@current",nodelist[n])!=null) nodelist[n].attributes.removeNamedItem("current");
				attr=node.ownerDocument.createAttribute("selected");
				attr.nodeValue='1';
				nodelist[n].attributes.setNamedItem(attr);
			}
		} else { //select a single new node
			for (var n=0; n<nodelist.length; n++) {
				if (bSelectSingleNode("@selected",nodelist[n])!=null && !strMultiple) nodelist[n].attributes.removeNamedItem("selected");
				if (bSelectSingleNode("@current",nodelist[n])!=null) nodelist[n].attributes.removeNamedItem("current");
			}
		}
	}
	attr=node.ownerDocument.createAttribute("current");
	attr.nodeValue='1';
	node.attributes.setNamedItem(attr);
	attr=node.ownerDocument.createAttribute("selected");
	attr.nodeValue='1';
	node.attributes.setNamedItem(attr);

}

function bind_DeselectNodes(nodelist) {
	if (nodelist && nodelist.length>0) {
		for (var n=0; n<nodelist.length; n++) {
			if (bSelectSingleNode("@current",nodelist[n])!=null) nodelist[n].attributes.removeNamedItem("current");
			if (bSelectSingleNode("@selected",nodelist[n])!=null) nodelist[n].attributes.removeNamedItem("selected");
		}
	}
}

function bind_TableKeyuphandler(e,XMLlistdoc,TableHTMLNode) {
	/* Handles Keyup events for a table that has been bound using boundpath
	 * the handler allows the user to select rows using arrow and shift/ctrl keys
	 */	 	 	 
	if (!e) var evt = event; else var evt=e;
	var strMultiple=false;
	if (!XMLlistdoc) XMLlistdoc=bind_XMLdoc;
	if (TableHTMLNode) {
		var boundTable=TableHTMLNode;
	} else {
		var boundTable=evt.srcElement;
	}
	if (boundTable.getAttribute("multiselect")=="1") {
		if (evt.ctrlKey || evt.shiftKey) strMultiple=true;
	}
	var currow=bSelectSingleNode(boundTable.getAttribute("boundpath")+"[@current]",XMLlistdoc);
	var selectedrow=false;
	switch (evt.keyCode) {
	case 38: //up
		if (currow.previousSibling) selectedrow=currow.previousSibling;
		break;
	case 40: //down
		if (currow.nextSibling) selectedrow=currow.nextSibling;
		break;
	}
	if (selectedrow)  {
		bind_SelectNode(selectedrow,strMultiple);
		BindControls(XMLlistdoc, boundTable);
	}
	return selectedrow;
}

function bind_SetLookupValue(strCtrl, strKey, strText) {
	/* Sets the Key and the value of a lookup control
	 * A lookup control is just a textbox, but the textbox only shows
	 * the name or description that belongs to a key
	 * Use the boundkey attribute to indicate which xml-attribute holds the key.	 
	 */	
	var Ctrl=document.getElementById(strCtrl);
	if (!Ctrl) Ctrl=strCtrl;
	if (bind_XMLdoc && Ctrl) {
		var textNode=GetBoundNode(Ctrl);
		var keyNode=GetBoundNode(Ctrl, "boundkey");
		if (textNode && keyNode) {
			bSetNodeValue(keyNode,false,strKey);
			bSetNodeValue(textNode,false,strText);
			if (gDirtyElements) {
				//mark the element or parent-element as dirty
				var attr = keyNode.ownerDocument.createAttribute("dirty");
				attr.nodeValue="1";
				if (keyNode.attributes && keyNode.attributes!=null) {
					keyNode.attributes.setNamedItem(attr);
				} else {
					bSelectSingleNode("..",keyNode).attributes.setNamedItem(attr);
				}
			}
			if (gDirtyDocument) {
				//mark the document as dirty
				var tmpDnode = bSelectSingleNode("//*",bind_XMLdoc); //the first node found
				if (tmpDnode) {
					var attr = tmpDnode.ownerDocument.createAttribute("dirty");
					attr.nodeValue="1";
					tmpDnode.attributes.setNamedItem(attr);
				}
			}
			BindRefresh();	
			ValidateControl(Ctrl);
			var strAfterWriteback=Ctrl.getAttribute("afterwriteback");
			if (strAfterWriteback) try { eval(strAfterWriteback.replace(/this/g,"Ctrl")); } catch(e) {}
			
		}
	}
}

function Bind_CheckDirty(strWarning) {
	//Search for a dirty attribute
	//if set to 1 then return true (unless warning is confirmed)
	if (bind_XMLdoc && bSelectNodes("//*[@dirty='1']",bind_XMLdoc).length>0 && (!strWarning || (strWarning && !confirm(strWarning)))) return true; else return false;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//																															//
//		De volgende functies zijn KISS-specifiek en zijn alleen omwille van overzichtelijkheid in BindControls geplaatst 	//
//																															//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



// RB: 2007-10-03
// Vertaalt een XPATH Conditie naar een (soort van ) parsetree
// Een conditie heeft het volgende formaat. LET OP, momenteel worden HAAKJES niet ondersteund!
// Het is niet echt een goede parser het voert simpele splits uit. Dit kan dus in sommige gevallen problemen opleveren ...
// Ook worden geneste Condities niet geparst!
// 
// conditions => <or_condition>
// or_condition => <and_condition> [ OR ] <or_condition>
// 				| <and_condition>
// and_condition => <item_condition> [ AND ] <and_condition>
//				| <item_condition>
// item_condition => <item> <operator> <item>
//					| <item>						# resulteert in een LEGE operator
// item => kan alles zijn
//
// operator => [=] | [!=] | [<] | [>] | [!=] | [<=] | [>=] 
//
// RETURNS AN ARRAY: (voorbeeld
//    [ 							# or condities
//			 [ 						# and condities
//				[					# expressies
//					{ operator: '=', lvalue: '@voorbeeld', rvalue: 'value'  }, ...
//				], ...
//			
//			], ...
//		]
//

function parseXPathExpression( condition ) {
	// de operatoren (LET OP, volgorde is belangrijk, zet de langste strings vooraan!!!)
	var OPERATORS = ['!=', '<=', '>=', '<', '>', '=' ]

	// het resultaat:
	var result = [];
	var trim = function(value) { value = value.replace(/^\s+/,''); value = value.replace(/\s+$/,''); return value; };

	// Eerst SPLITS op OR
	var or_items = condition.split( /\sOR\s/gi );
	for( var orIdx=0; orIdx < or_items.length; orIdx++ ) {	
		var or_item = or_items[orIdx];
		
		// Vervolgens splitsen op AND
		var result_and = [];
		var and_items = or_item.split( /\sAND\s/gi );		
		for( var andIdx=0; andIdx < and_items.length; andIdx++ ) {
			var and_item = and_items[andIdx];
			
			// zoek vervolgens een operator
			var found_operator_pos = -1;
			for( var i=0; i < OPERATORS.length && found_operator_pos < 0; i++ ) {
				found_operator_pos = and_item.indexOf( OPERATORS[i] );
			}
			var found_operator = ( found_operator_pos >= 0 ) ? OPERATORS[i-1] : '';	
			
			// pak het stuk VOOR de operatoren
			var lvalue = '', rvalue = '';
			if( found_operator_pos>=0 ) {
				lvalue = and_item.substr( 0, found_operator_pos );
				rvalue = and_item.substr( found_operator_pos + found_operator.length );
			} else {
				lvalue = and_item;	
			}
			
			// vervolgens item toevoegen (Eventuele quotes worden van de RVALUE afgehaald!)
			result_and.push( { operator: trim( found_operator ), lvalue: trim( lvalue ), rvalue: trim( rvalue ).replace(/^\s*[\"\']/,"").replace(/[\"|\']\s*$/,"") } );
		} // andIdx
		result.push( result_and );
	} // orIdx
	return result;
// TEST: Xpath parser: alert( print_r( parseXPathExpression( "@item1 = '1' and  @item2 < '2' or @item3 != '1' and not( @item3 ) " )  ) );
}

// RB: 2007-10-03
// Voegt een node met attributen toe 
// INPUT
// - xmlParent. De parent waaronder de node gehangen moet worden
// - nodeExpression. Een node expressie. DUS Alleen een nodenaam met optioneel attribuut condities: Node[@value='1' and @attr2='2' ]
// RETURN
// - de nieuwe xmlNode
// 
function createElementWithAttributes( xmldoc, xmlParent, nodeExpression ) {
		// vervang elke '][ door een AND (dit kan ik namelijk beter parsen ;-). Dus:   Node[@a='2'][@b!='2']  => Node[@a='2' and @b!='2']
		var strArrBP = nodeExpression;
		strArrBP = strArrBP.replace( /\]\s*\[/g, " and " );	
		var cIdx = strArrBP.indexOf( '[' );
		var eIdx = strArrBP.lastIndexOf( ']' );
		var nodeName = cIdx >= 0 ? strArrBP.substr( 0, cIdx ) : strArrBP;
		var conditions = cIdx >= 0 ? strArrBP.substring( cIdx + 1 ).replace(/\]\s*$/,"") : ''; 	// verwijder de laatste ']' (optioneel de rechtse whitespaces)
		
		// aanmaken node
		var xmlNode = xmlParent.appendChild( xmldoc.createElement( String(nodeName) ) );

		// parse de condities als ze er zijn en maak de attributen aan
		if( conditions ) {
			var parseTree = parseXPathExpression( conditions );
			// doorloop alle OR condities
			for( var orIdx=0; orIdx < parseTree.length; orIdx++ ) {
				var andItems = parseTree[orIdx];
				for( var andIdx=0; andIdx < andItems.length; andIdx++ ) {
					var item = andItems[andIdx];
					// voor elke '=' operator moet de node worden aangemaakt :-)
					// alleen bij een '=' operator en attribuut 
					if( item.operator == '=' && item.lvalue.charAt( 0 ) =='@' ) {
						xmlNode.setAttribute( item.lvalue.substr(1), item.rvalue );
					}
				} // for andIdx
			} // for orIdx
		} // if conditions
		return xmlNode
}


// Voeg een regel toe aan een door BindControls gecontroleerde table
//
// INPUTS:
// - ref = HTMLnode van de table met de boundpath.
// - XMLdoc = XML-node waaronder de boundpath een geldig geheel moet gaan vormen
// OPMERKING:
// De BoundValue mag geen Optionele attribuutvergelijkingen bevatten en de verplichte attributen dienen genoteerde te worden alsvolgt: [conditie][conditie][conditie]
// [RvG20070820]
// [RB20071003] Aangepast, maakt gebruik van nieuwe parser
function addRowInBoundTable(ref,XMLdoc) {
	if (XMLdoc.ownerDocument) {
		XMLdocument = XMLdoc.ownerDocument;
	} else {
		XMLdocument = XMLdoc;
	}
	// Haal de boundpath op uit de meegegeven table
	var strBP = String(ref.getAttribute("boundpath"));
	// Splits de boundpath op / en // om de verwijzing op te splitsen tot XMLnodes
	arrBP = strBP.replace("[not(@hide)]","").replace(/\/\//g,"/").split("/");

	xmlParent = XMLdocument.documentElement;				
	
	// ITR: Over alle XMLnodes die in de boundpath genoemd worden na de eerste (een boundpath begint altijd met een //)
	// Elke node moet OFWEL bestaan in het XML-bestand, OFWEL aangemaakt worden in het XML-bestand.
	for (var x=1; x<arrBP.length; x++) {
		strArrBP = String(arrBP[x]);
		// Oude Xml NODE
		xmlNode = selectSingleNode(".//"+strArrBP,xmlParent);

		// is dit de laatste node? Of bestaat de xmlnode nog niet ?
		if (!xmlNode || x==arrBP.length-1) {
			xmlNode  = createElementWithAttributes( XMLdocument, xmlParent, strArrBP );
		} // if( xmlNode || x==arrBP.length-1 )
		xmlParent = xmlNode;
	}

	// Nu zoeken we alle HTML-elementen met een Boundvalue
	var htmlChildNodes = ref.getElementsByTagName('*');
	for (var x=0; x<htmlChildNodes.length; x++) {
		if (htmlChildNodes[x].getAttribute("boundvalue")) {
			// Al deze nodes ook aanmaken...
			buildBoundNode(htmlChildNodes[x],xmlParent);
		}
	}
}

// Bouw een XML-boom op onder XMLdoc op basis van de boundvalue in HTMLnode ref.
//
// INPUTS:
// - ref : gebonden HTMLnode (dus MET Boundvalue)
// - XMLdoc: XML-node waaronder deze boundvalue dient te bestaan. 
// OPMERKING:
// De BoundValue mag geen Optionele attribuutvergelijkingen bevatten en de verplichte attributen dienen genoteerde te worden alsvolgt: [conditie][conditie][conditie]
// [RvG20070820]
// [RB20071003] Aangepast, maakt gebruik van nieuwe parser
function buildBoundNode(ref,XMLdoc) {
	if (XMLdoc.ownerDocument) {
		XMLdocument = XMLdoc.ownerDocument;
	} else {
		XMLdocument = XMLdoc;
	}
	if (selectSingleNode(ref.getAttribute("Boundvalue"),XMLdoc)) { return }
	if (/\//.test(String(ref.getAttribute("Boundvalue")))) {
		// Splits de Boundvalue of individuele nodes (/ of //) 
		var arrBV = String(ref.getAttribute("Boundvalue")).replace(/\/\//g,"/").replace(/^\/*/,"").split("/");
		// ITR: over alle nodes in de boundvalue BEHALVE de laatste (het attribuut)
		xmlSubparent = XMLdoc;
		for (var z=0; z<arrBV.length-1; z++) {		
			//Controleer of de node bestaat
			var xmlNode = selectSingleNode(".//"+arrBV[z],xmlSubparent);
			
			// Zo niet? Aanmaken! PLUS attribuuteisen
			if (!xmlNode) {
				strArrBP = String(arrBV[z]);
				xmlNode = createElementWithAttributes( XMLdocument, xmlSubparent, strArrBP );
			}
			// Node bestaat, verder VANAF deze
			xmlSubparent = xmlNode;
		}
		// Maak het gevraagde attribuut leeg aan
		xmlSubparent.setAttribute(String(arrBV[arrBV.length-1]).substr(1),"");
	} else {
		XMLdoc.setAttribute(String(ref.getAttribute("Boundvalue")).substr(1),"");
	}
}

// Orginele code, laat maar even staan voor de zekerheid. ---

//~ // Voeg een regel toe aan een door BindControls gecontroleerde table
//~ //
//~ // INPUTS:
//~ // - ref = HTMLnode van de table met de boundpath.
//~ // - XMLdoc = XML-node waaronder de boundpath een geldig geheel moet gaan vormen
//~ // OPMERKING:
//~ // De BoundValue mag geen Optionele attribuutvergelijkingen bevatten en de verplichte attributen dienen genoteerde te worden alsvolgt: [conditie][conditie][conditie]
//~ // [RvG20070820]
//~ function addRowInBoundTable(ref,XMLdoc) {
	//~ if (XMLdoc.ownerDocument) {
		//~ XMLdocument = XMLdoc.ownerDocument;
	//~ } else {
		//~ XMLdocument = XMLdoc;
	//~ }
	//~ // Haal de boundpath op uit de meegegeven table
	//~ var strBP = String(ref.getAttribute("boundpath"));
	//~ // Splits de boundpath op / en // om de verwijzing op te splitsen tot XMLnodes
	//~ arrBP = strBP.replace("[not(@hide)]","").replace(/\/\//g,"/").split("/");

	//~ xmlParent = XMLdocument.documentElement;				
	//~ // ITR: Over alle XMLnodes die in de boundpath genoemd worden na de eerste (een boundpath begint altijd met een //)
	//~ // Elke node moet OFWEL bestaan in het XML-bestand, OFWEL aangemaakt worden in het XML-bestand.
	//~ for (var x=1; x<arrBP.length; x++) {
		//~ strArrBP = String(arrBP[x]);
		//~ arrAttr = strArrBP.split("@");
		//~ // Splits de meegegeven node op nodenaam en attributen
		//~ xmlNode = selectSingleNode(".//"+strArrBP,xmlParent);
		//~ // Als de node niet bestaat (of het betreft de laatste node - we maken immers een nieuwe regel aan), maken we deze aan inclusief alle attributen
		//~ if (!xmlNode || x==arrBP.length-1) {
			//~ xmlNode = xmlParent.appendChild(XMLdocument.createElement(String(arrAttr[0]).replace(/[\[\]]/g,"")));
			//~ // ITR: Over alle attributen
			//~ // Elk attribuut waarop gecheckt wordt dient te bestaan in de node MET de juiste waarde! 
			//~ for (var y=1; y<arrAttr.length; y++) {
				//~ xmlNode.setAttribute(String(arrAttr[y]).replace(/[\[\]]/g,"").split("=")[0],(String(arrAttr[y]).replace(/[\[\]]/g,"").split("=")[1]?String(arrAttr[y]).replace(/[\[\]\"\']/g,"").split("=")[1]:""));
			//~ }
		//~ }
		//~ xmlParent = xmlNode;
	//~ }

	//~ // Nu zoeken we alle HTML-elementen met een Boundvalue
	//~ var htmlChildNodes = ref.getElementsByTagName('*');
	//~ for (var x=0; x<htmlChildNodes.length; x++) {
		//~ if (htmlChildNodes[x].getAttribute("boundvalue")) {
			//~ // Al deze nodes ook aanmaken...
			//~ buildBoundNode(htmlChildNodes[x],xmlParent);
		//~ }
	//~ }
//~ }

//~ // Bouw een XML-boom op onder XMLdoc op basis van de boundvalue in HTMLnode ref.
//~ //
//~ // INPUTS:
//~ // - ref : gebonden HTMLnode (dus MET Boundvalue)
//~ // - XMLdoc: XML-node waaronder deze boundvalue dient te bestaan. 
//~ // OPMERKING:
//~ // De BoundValue mag geen Optionele attribuutvergelijkingen bevatten en de verplichte attributen dienen genoteerde te worden alsvolgt: [conditie][conditie][conditie]
//~ // [RvG20070820]
//~ function buildBoundNode(ref,XMLdoc) {
	//~ if (XMLdoc.ownerDocument) {
		//~ XMLdocument = XMLdoc.ownerDocument;
	//~ } else {
		//~ XMLdocument = XMLdoc;
	//~ }
	//~ if (selectSingleNode(ref.getAttribute("Boundvalue"),XMLdoc)) { return }
	//~ if (/\//.test(String(ref.getAttribute("Boundvalue")))) {
		//~ // Splits de Boundvalue of individuele nodes (/ of //) 
		//~ var arrBV = String(ref.getAttribute("Boundvalue")).replace(/\/\//g,"/").replace(/^\/*/,"").split("/");
		//~ // ITR: over alle nodes in de boundvalue BEHALVE de laatste (het attribuut)
		//~ xmlSubparent = XMLdoc;
		//~ for (var z=0; z<arrBV.length-1; z++) {
			//~ //Controleer of de node bestaat
			//~ var xmlNode = selectSingleNode(".//"+arrBV[z],xmlSubparent);
			//~ // Zo niet? Aanmaken! PLUS attribuuteisen
			//~ if (!xmlNode) {
				//~ strArrBP = String(arrBV[z]);
				//~ arrAttr = strArrBP.split("@");
				//~ xmlNode = xmlSubparent.appendChild(XMLdocument.createElement(String(arrAttr[0]).replace(/[\[\]]/g,"")));
	
				//~ // Maak alle attributen aan MET vereiste waarde
				//~ for (var a=1; a<arrAttr.length; a++) {
					//~ xmlNode.setAttribute(String(arrAttr[a]).replace(/[\[\]]/g,"").split("=")[0],(String(arrAttr[a]).replace(/[\[\]]/g,"").split("=")[1]?String(arrAttr[a]).replace(/[\[\]\"\']/g,"").split("=")[1]:""));
				//~ }
			//~ }
			//~ // Node bestaat, verder VANAF deze
			//~ xmlSubparent = xmlNode;
		//~ }
		//~ // Maak het gevraagde attribuut leeg aan
		//~ xmlSubparent.setAttribute(String(arrBV[arrBV.length-1]).substr(1),"");
	//~ } else {
		//~ XMLdoc.setAttribute(String(ref.getAttribute("Boundvalue")).substr(1),"");
	//~ }
//~ }

// Event Handlers
// written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini

// http://dean.edwards.name/weblog/2005/10/add-event/

function addEvent(element, type, handler) {
	if (element.addEventListener) {
		element.addEventListener(type, handler, false);
	} else {
		// assign each event handler a unique ID
		if (!handler.$$guid) handler.$$guid = addEvent.guid++;
		// create a hash table of event types for the element
		if (!element.events) element.events = {};
		// create a hash table of event handlers for each element/event pair
		var handlers = element.events[type];
		if (!handlers) {
			handlers = element.events[type] = {};
			// store the existing event handler (if there is one)
			if (element["on" + type]) {
				handlers[0]=element["on" + type];
			}
		}
		// store the event handler in the hash table
		handlers[handler.$$guid] = handler;
		// assign a global event handler to do all the work
		element["on" + type] = handleEvent;
	}
};
// a counter used to create unique IDs
addEvent.guid = 1;

function removeEvent(element, type, handler) {
	if (element.removeEventListener) {
		element.removeEventListener(type, handler, false);
	} else {
		// delete the event handler from the hash table
		if (element.events && element.events[type]) {
			delete element.events[type][handler.$$guid];
		}
	}
};

function handleEvent(event) {
	var returnValue = true;
	// grab the event object (IE uses a global event object)
	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
	// get a reference to the hash table of event handlers
	var handlers = this.events[event.type];
	// execute each event handler
	for (var i in handlers) {
		this.$$handleEvent = handlers[i];
		if (this.$$handleEvent(event) === false) {
			returnValue = false;
		}
	}
	return returnValue;
};

function fixEvent(event) {
	// add W3C standard event methods
	event.preventDefault = fixEvent.preventDefault;
	event.stopPropagation = fixEvent.stopPropagation;
	return event;
};
fixEvent.preventDefault = function() {
	this.returnValue = false;
};
fixEvent.stopPropagation = function() {
	this.cancelBubble = true;
};

// To circumvent IE6 Bugs
function isIE6() {
	var agt = navigator.userAgent.toLowerCase();
	if ((agt.indexOf("msie 6.")!=-1)) {
		return true;
	};
	return false;
}

