//stop();
var ydrExclaimListClient = new function()
{
	/*
		first name is the only caller
		
		ydrClientScripts>>valueList() used by nodeWalker to add a list of sub rows to the nodeWalk
		 and to the html page to contain the row data to be sent to the server - this data is in XML not language specific format
		close() used by a button when clicked (not being the popupform's buttons
		radio(element) when a radio button is clicked
		
		init(element) tells me to find out where the data is - call when the innerHTML is set
		exit() tells me all done - important to stop nodeWalk picking up junk into another form
		
		this is predominantly coded for the situation where there is a grid list and a supplementary edit form to edit the individual rows
		it is then desensitised for when there is no grid list so that it works on plain fields in a main form
		
		this enables fragments for a list to be editted - eg a single value rather than many value all using the same code
	*/
	
	var m_activeElements;  // generated normally by popup - is ORIGINAL nodwalk of the data area
	var m_popup; // the object handling the form on which my fields are active
	var m_gridPane;   // the xhtml element list of all rows being editted
		var m_defaultBackground; // default cell background in m_gridPane
		var m_gridTable;  // the body of the grid part of its parent 	
	var m_editorPane; // the element containing the current row being editted
		var m_editorAdvisory; // the error messages gleaned from the underlying form and which may be hidden by this one
		var m_editorRows; // the body bart of the editor element
	var m_gridRowTemplate; //
	var m_minOccurs=0;
	var m_objectname;
	
//	stop();
	var m_referenceList=new Array();
	var m_insertMode=false;
	var m_xpath;
	var m_hasTranscribed;  // transcribing is when you take a row from a list and make it a table for interactive editing
		// for pages where there is no rowlist then this gets set on the first call to validate
	
	var m_this = this;
	
	var m_focusElement;  // the element of m_editorPane currently being editted

	// extends nodeWalk to have a set of sub elements one for each data row in the grid and using the top level element as the template
	this.valueList = function(nodeWalk)
	{
		var i;
		var j;
		var rowCells;
		
		if (m_activeElements != null)
		{						
			// iterate over tbody rows
			for (i=1; i<m_gridTable.rows.length; i++) 
			{
				rowCells = m_gridTable.rows[i].childNodes;
				
				// iterate over TD cells skipping radio button
				with (nodeWalk.appendChild(nodeWalk.cloneNode(false))) 
				{
					for (j=1; j<rowCells.length; j++)
					{	
						attributes[j-1].nodeValue = rowCells[j].innerHTML;
					};	
				};
			};					
			
			nodeWalk.setAttribute("_content","grid");
			if (m_objectname)
				nodeWalk.setAttribute("_objectname", m_objectname);
		};
	}

	this.close = function(dummy,element)
	{
		var i;
		
		switch (ydrBrowser.html.elementValue(element))
		{
			// buttons on the row list table
			case "Change" :
				m_insertMode = false;
				if (showEditor())
				{
					transcribeToEditable();
				};
				
				break;
				
			case "Delete" :
				m_insertMode = false;
				if (showEditor())
				{
					showList(false);
					if (window.confirm("Are you sure you want to delete the selected entry?"))
					{
						m_focusElement.parentNode.removeChild(m_focusElement);
						m_focusElement = null;
					}
				};

				break;
				
			case "Insert New" :
				m_insertMode = true;
				if (showEditor("new"))
				{
					focusElementSet (m_gridRowTemplate.cloneNode(true)); 
					transcribeToEditable();
				};

				break;	
				
			case "Insert Copy" :	
				m_insertMode = true;
				if (showEditor())
				{
					// we have to make sure the old element gets de-selected
					focusElementSet(m_focusElement.cloneNode(true));
					m_focusElement.firstChild.firstChild.checked=false;
					transcribeToEditable();
				};
				
				break; 
				
			// buttons on the editor table
			case "Accept" :				
				// and editor revised record to the grid		
				
				m_popup.validate();
				
				if (m_gridPane)
				{
					if (m_popup.advisory().length)
					{
						// this alert drops the timeout - so do again
						if (m_editorAdvisory)
							m_editorAdvisory.innerHTML = m_popup.advisory();
						
						window.setTimeout(
							'window.alert ("Please fix the validation errors that are listed at the bottom of the page")'
							,1);
						
					}
					else if (m_gridPane.style.visbility == 'hidden')
					{
						window.setTimeout(
							'window.alert ("Please cancel or apply the edit dialog first")'
							,1);
					}
					else
					{			
						if (m_insertMode)
							m_gridTable.lastChild.appendChild(m_focusElement)
												
						for (i=1; i<m_focusElement.childNodes.length; i++)
							ydrBrowser.html.text(m_focusElement.childNodes[i], ydrBrowser.html.elementValue(editorElement(i-1)));
					
						showList(true);
					};
				};
				break;
				
			case "Cancel" :
				if (m_insertMode)
					focusElementSet(null);
					
				showList(false);
				break;
				
			default :
				window.alert ("Unrecognised button click in Exclaim List");
		}
	}
	
	// for when there is no list - keep existing values
	function transcribeToEditableListless()
	{
		var i;
		
		for (i=0; i<m_activeElements.length; i++)
		{
			var elementI = m_activeElements[i];
			targetI = document.getElementById(elementI.getAttribute("id"));
			
			editorElementSet(targetI, null);			
		};
		
		m_hasTranscribed=true;
	}
	
	// for when there is a list move the focusrow item values accross
	function transcribeToEditable()
	{
		var i;
		
		for (i=1; i<m_focusElement.childNodes.length; i++)
		{
			editorElementSet(i-1, ydrBrowser.html.text(m_focusElement.childNodes[i]));
		};
		
		m_hasTranscribed=true;
	}
	
	// returns the input/select/ etc cell of the right hand column for the given index
	function editorElement(index)
	{
		if (typeof index == "number")
			if (m_editorRows == null)
				return document.getElementById(m_popup.activeElements[i].nodeName)
			else		
				return m_editorRows[index].firstChild.nextSibling.firstChild
		else
			return index;
	};
	
	// this is used where an ydrExclaimfield is being used outside of the formal environment but nonetheless wants
	// the full initialisation with the required characteristics - ie it acts as an orphan
	this.orphanSet = function(element, customDef)
	{
		editorElementSet (element, ydrBrowser.html.elementValue(element, customDef));
	}

	// customDef is a string version of XML with overrides for the default schema
	// note this only causes a client side change and cannot impact validation against
	// any data going to a database
	// eg , "<YDRexclaimfielddef><YDRreferences table='CONTACT' display='CONTACTID' /> </YDRexclaimfielddef>"
	function editorElementSet(index, newvalue, customDef)
	{
	
		with (editorElement(index))
		{
			var ydrfmt = getAttribute("ydr:fmt"); 
			
			switch (tagName.toLowerCase())
			{
				case "input" :
				case "textarea" :
					if (newvalue != null)
						value=newvalue;
					break;
				
				case "select" :						
					if (ydrfmt == "associative")
					{
						if (getAttribute("ydr:constrainMapSource") != null)
						{
							value="--"; // otherwise an initial mapping generates an error message							
							{
								var xml =ydrBrowser.html.xhtml(editorElement(index), null, "simpleXML");
								
								mapperBuild(xml.firstChild
									, editorElement(index)
									, getAttribute("ydr:constrainMapSource"));
							}
							value=newvalue;
						}
						else if (newvalue!= null)
							value=newvalue;
					}
					
					else if (ydrfmt == "reference")
					{
						if (customDef == null)
							customDef=getAttribute("ydr:customDef");
					
						// TODO the following optimisation causes a closed and reopened form not to show its
						// list entries correctly - can we live without as the 
						// httprequest optimises already on repeat calls
						if (false && m_referenceList[getAttribute("id")] != null)
						{
							if (newvalue != null)
								value=newvalue;
								
							m_referenceList[getAttribute("id")].lastTriggerValue = null;
							
							selectOptionsMap(m_referenceList[getAttribute("id")]);
							
							// now we already set value above for the benefit of the initial load of selectoptionsmap -
							// however on the first ever load this setting did not stick because the value set was not present
							// so we try again - this time preserving the new newvalue for if the newvalue is not part of the select option set
							
							var newnewvalue = value;
							value = newvalue;
							if (value == null )
								value = newnewvalue;
						}
						else
						{
							// ensure that at least the current value is in the list initially
							if (newvalue != null)
							{
								var optionElement;
								with (optionElement = appendChild(ownerDocument.createElement("option")))
								{
									setAttribute("value", newvalue);
									ydrBrowser.html.text(optionElement, newvalue);
									selected = true;	
								};
							};
							
							// look up the present list of possible values from the server
							// not very good at the moment for large RDBMS - needs some batching process for long lists
							
							with (ydrClientScripts.XMLHttpRequest())
							{
								onreadystatechange=	function ()
								{
									if (this.readyState == 4)
									{
										try
										{
											var response= this.responseXML;
											
											if (ydrBrowser.xml.parseError( response ))
											{
												window.alert("ydrExclaimListClient.editorElementSet[select/1]: Cannot retrieve list of values from server\n\nXML error: " 
													+ ydrBrowser.xml.parseError( response ) )
											}
											else if (response.documentElement.nodeName != "referenceList")
											{	
												window.alert("ydrExclaimListClient.editorElementSet[select/2]: Server side error\n\n"
													+ ydrBrowser.xml.xml(response)			 )							
											}
											else
											{
												mapperBuild(response.documentElement
													, document.getElementById(response.documentElement.getAttribute("source"))
													, response.documentElement.getAttribute("constrainMap"))
											};
										}
										catch (e)
										{
											window.alert("ydrExclaimListClient.editorElementSet[select/10]: Cannot retrieve list of values from server\n\nScript error: " + e.message)
										};
									};													
								};
								
								var extra="";
								if (customDef != null)
									extra = "&customDef="+ escape(customDef);
								
								//TODO need to make to work for more than just contact on userDetails
								// eg "http://ydr-d5000/inetpub/site%20belmont/htdocs/ydr/data/ydrExclaimList.asp?action=referenceList&source
									//    =XREFCONTACT-Manager&origin=userDetails&table=CONTACT"

								open("GET",rootDir+"/ydr/data/ydrExclaimList.asp?action=referenceList"
									+ "&source="+id
									+ "&origin=userDetails"
									+ "&table=CONTACT"
									+ extra);	// id is from editorElement(index)
								send(null);		
							};
						};
						
					}
					else
						value=newvalue;
						
					break;
					
				default :
					window.alert("Unrecognised update element");
				
			};
		};
	};

	function mapperBuild(response, sourceElement, constrainMap)
	{
		//stop();
		try
		{
			if (sourceElement == null)	
				throw new Error("Invalid response: "+ ydrBrowser.xml.xml(response))
			
			var mapper = new Object();
			
			if (response.nodeName != "referenceList" && response.nodeName != "select"  )
			{
				window.alert("ydrExclaimListClient.mapperBuild[developer]: Error retrieving map details\n\n"
					+ydrBrowser.xml.xml(response));
				return;
			};
			
			mapper.response = response;
			mapper.sourceElement = sourceElement
			mapper.lastTriggerValue = null;
			
			
			var script = sourceElement.getAttribute("ydr:validation");
			//alert("ss")
			if (script != null)
			{
				try 
				{					
					eval("mapper.script = "
						+ (ydrBrowser.xml.selectSingleNode(
							ydrBrowser.xml.document("<v>"+ script +"</v>").documentElement
							, "validation[@name='constrainMap']/@value").nodeValue)
						);
				}
				catch (e)
				{
					if (ydrBrowser.xml.selectSingleNode(
						ydrBrowser.xml.document("<v>"+ script +"</v>").documentElement
						,"validation[@name='constrainMap']/@value") != null)
					window.alert("ydrExclaimListClient.editorElementSet[select]: cannot parse script for value mapping\n\n" 
						+ e.message);
				};	
			};
			
			try 
			{
				mapper.notNull = script.search(/action=['"]notNull['"]/) > 0 ;												
			}
			catch (e)
			{
				mapper.notNull =false;
			};
			
			var existingIdsOnMap;
			
			mapper.constrainMap = constrainMap;
			if (mapper.constrainMap != null)
			{
				// data fields can be generic (ie many input elements use the same mapper field
						// in which case the referenced source id has a hyphen
				// however the mapping field which is used by the custom script to choose which members are shown
				// can either be generic (no hyphen) or specific - in which case the part after the hyphen must
				// match in both input field id and constrainmap field - for if they dont then generic is presumed
				// and if generic is not found then mapping still occurs but the script is given null as its constrain map value
				var sourceFlex = mapper.response.getAttribute("source");
				if (sourceFlex == null)
					sourceFlex = sourceElement.id;
				
				if (sourceFlex.indexOf("-")>0)
				{
					mapper.constrainMap = window.document.getElementById(mapper.constrainMap+"-"
						+ sourceFlex.split(/-/)[1]);
					
					if (mapper.constrainMap ==null)
						mapper.constrainMap = window.document.getElementById(constrainMap);
				}
				else
					mapper.constrainMap = window.document.getElementById(mapper.constrainMap);
				
				// look in the field that can cause us to change our mind as to what entries we can have in our list
				// when that far field changes - then add ourselves to ydr:ConstrainMap to act as a trigger when that field
				// changes
				if (mapper.constrainMap  != null)
				{
					existingIdsOnMap = mapper.constrainMap.getAttribute("ydr:ConstrainMap")
					
					if (existingIdsOnMap == null)
						existingIdsOnMap = ""
					else
						existingIdsOnMap += " ";
				
					// ydr:ConstrainMap attribute of triggering input/select/textarea cell becomes a space separate token list or unique entries!	
					if (existingIdsOnMap.indexOf(sourceElement.getAttribute("id")+" ")<0)
						mapper.constrainMap.setAttribute("ydr:ConstrainMap", existingIdsOnMap + sourceElement.getAttribute("id"));
				};
			}
															
			m_referenceList[sourceElement.getAttribute("id")] = mapper;
			selectOptionsMap(mapper);
		}
		catch (e)
		{
			window.alert("ydrExclaimListClient.editorElementSet[select]: Cannot use list of values from server\n\nXML error: " 
				+  e.message)
		};
	}
	
	
	function selectOptionsMap(referenceList)
	{
		// re-creates the select/option list with a new set of choices, trying to retain the present selected value											
		var optionsList;
		var optionValue;
		var optionElement;
		var i;
		var source = referenceList.sourceElement;
		var script= referenceList.script;
		var selected = true;
		var constrainMapValue;
		try {constrainMapValue = ydrBrowser.html.elementValue(referenceList.constrainMap) } // the document element containing the value of the mapper
		catch (e) {};
	// dont use stop(); here -gets trapped higher;
		try
		{
			with (source)
			{
				var triggerValue = "void";
				try { triggerValue = ydrBrowser.html.elementValue(referenceList.constrainMap)}
					catch (e) {};
				
				if (referenceList.lastTriggerValue != triggerValue)
				{
					referenceList.lastTriggerValue = triggerValue;
				
					currentValue=value;
					
					optionsList =  referenceList.response.childNodes;

					// remove any preexisting
					while (childNodes.length>0)
						removeChild(lastChild);				
	
					// if null is permitted then leave empty value at the start of the list
					if (! referenceList.notNull)
					{
						with (optionElement = appendChild(document.createElement("option")))
						{
							setAttribute ("value","");
							ydrBrowser.html.text(optionElement,"--");
						};
					};
		//alert("kk")hhh
					for (i=0; i<optionsList.length; i++)
					{
						optionValue= ydrBrowser.html.elementValue(optionsList[i]);
						
						if (script)
						{
							selected = script(optionValue, constrainMapValue, optionsList[i]);
						};
						
						if (selected)
						{
							
							with (optionElement = appendChild(document.createElement("option")))
							{
								setAttribute ("value",optionValue);
								ydrBrowser.html.text(optionElement, optionValue);
							};
						};
					};
					
					if (childNodes.length ==0 )
					{
						if ((m_editorPane && m_editorPane.style.visibility != "hidden") && constrainMapValue != "")
							window.alert("The list of potential values for '" + source.id + "' is empty.\n\nYou cannot enter data here")
					}
					else
					{
						value=currentValue;
						
						if (selectedIndex==-1 && currentValue != "" && currentValue != "--" && currentValue != "null")
						{
							// on the first map of this data we dont want to give this error message - for it is as a result of the values that the system 
						
							window.alert ("ydrExclaimListClient.selectOptionsMap: The current value for " +source.id +" is not valid."
								+ ((source.options.length <0 || (source.options.length ==1 && ydrBrowser.html.text(source.options[0]) == "--"))
									? "\n\nThere are no entries in "+source.id +" for you to select from.  Therefore you cannot input any data into this field" : ""));
						};
						
						// we need this commented out - setting a default value can lead to undesired error messages if the chosen one is used elsewhere 
						// when other data does not support it
						/*if (currentValue == "")	
							try { source.options.item(0).setAttribute("selected", "selected");
							} catch (e) {}; */
					};
				};
			};
		}
		catch (e) { window.alert("ydrExclaimListClient.selectOptionsMap: I cannot load the list of values for "+ source.id +"\n\n"+e.message); }
	};
	
	function editorElementGet(index)
	{
		with (editorElement(index))
		{
			switch (tagName.toLowerCase())
			{
				case "input" :
				case "textarea" :
					return value;
					break;
										
				default :
					window.alert("Unrecognised update element");
				
			};
		};
	};
	
	this.radio = function(element) { focusElementSet(element.parentNode.parentNode); };

	this.exit = function(atStartup)
	{
		if (atStartup != "atStartup")
		{
			m_activeElements = null; // we may leave this in place to allow some rudimentary functions 
												// when a non exclimlistclient form  is piggy backing some of our features
			m_editorRows = null;
			m_popup = null;
			m_hasTranscribed = false;
		};
		
		m_gridPane = null;
		m_editorPane = null;
		m_editorAdvisory =  null;
		m_focusElement = null;
		
	}
	
	this.init = function(popup)
	{
		// this will throw an error when the object shown by the popup is not a YDRexclaimlist item
		// this is normal behaviour for all other types of form
		try 
		{			
			// the following are preserved even if the full list edit is not operational
			m_hasTranscribed = false;
			m_popup = popup;
			m_activeElements = popup.activeElements();//  20081230 removed this .item(0).parentNode;
			try {
				m_editorRows = document.getElementById("exclaimListPane").firstChild.rows;
			} catch (e) {}; // is missing if this is a plain form rather than a list
			
			// the following are NOT preserved if the full list edit is not operational
			try {
				m_gridPane = document.getElementById("exclaimListGridPane");
			} catch (e) {};
			
			if (m_gridPane)
			{
				m_gridTable = document.getElementById("exclaimListGridTable");
				
				m_gridRowTemplate = m_gridTable.rows[1];
				m_gridRowTemplate.parentNode.removeChild(m_gridRowTemplate);
				
				m_minOccurs = m_gridRowTemplate.getAttribute("ydr:minoccurs");
				
				m_gridRowTemplate.removeAttribute("ydr:minoccurs");   
				
				m_objectname=m_gridRowTemplate.getAttribute("ydr:objectname");			
				m_gridRowTemplate.removeAttribute("objectname");   
			}
			else
			{
				m_objectname=document.getElementById("exclaimListPane").firstChild.firstChild.getAttribute("ydr:objectname");
				if (m_objectname.indexOf("_")<0)
					m_objectname = "@"+m_objectname;
			};
				
			if (m_minOccurs == null)
				m_minOccurs = 0;
					
			m_defaultBackground = ""; // not the way I expected but reverts IE to orginal style via class			
					
			try {
				m_editorPane = document.getElementById("exclaimListEditorPane")
			} catch (e) {};
				
			try {
				m_editorAdvisory = document.getElementById("exclaimListAdvisory"); 
			} catch (e) {};

			m_focusElement = null;
			
			showList(true);
		}
		catch (e) 
		{
			m_this.exit("atStartup");
			throw new Error("Not a ydrExclaimList popup")
		};			
	};
	
	function showList(sort)
	{
		var i;
		var lastValue;
		var thisValue;

		if (m_gridPane && m_editorPane)
		{
			m_popup.deeperModal("pop"); 
			m_gridPane.style.visibility="inherit";
			m_editorPane.style.visibility="hidden";
			
			//sort by related contact		
			if (sort && m_gridTable.rows>0)
			{
				focusElementSet(null);
				ydrClientScripts.tableSort(m_gridTable.rows[1].lastChild,"noAnchor", "allPriorFields");				

				// make sure there is no duplicate row - now since it is sorted we only have tolook at the previous row
				var items;
				with (items = m_gridTable.lastChild.rows)  // over the tbody rows 
				{	
					for (i= length-1; i>=0; i--)
					{
						thisValue=items[i].outerHTML;
						if (thisValue == lastValue)
						{
							window.alert ("This data duplicates a previous value - it has been discarded")
							m_gridTable.lastChild.removeChild(items[i]);
							break;
						};
						lastValue = thisValue;
					};
				};
			};
		};
	};
		
	function focusElementSet(newTR)
	{
		if (m_focusElement != null)
		{
			m_focusElement.firstChild.firstChild.checked = false;
			m_focusElement.style.cssText += "; background-color:"+ m_defaultBackground;
		}
		
		m_focusElement = newTR;		
		
		if (newTR != null)
		{
			m_focusElement.style.cssText += "; background-color:#dcdcdc";/* gainsboro */ 
			if (! m_focusElement.firstChild.firstChild.checked)
				m_focusElement.firstChild.firstChild.checked = true;		
		};
	}
	
	function showEditor(newRecord)
	{
		if (m_focusElement == null && newRecord != "new")
			window.alert("Please select a row using the radio buttons to the left")
		else 
		{
			if (m_gridPane && m_editorPane)
			{
				m_popup.deeperModal("push"); 
				m_gridPane.style.visibility="hidden";
				m_editorPane.style.visibility="inherit";
			};
			return true;
		};
	};
	
	// extends ydrpopup.validate automatically if this object is referenced as a script in the HTML
	// also see equivalent function is ydrExclaimList that responds server side
	this.validate = function (element)
	{
		var i;
		var j;
		
		var validations;
		var targetI;
		var isHidden;
		var format;
		var myValue;
		
		var constrainMapAttributes;
		var result="";

		if (m_activeElements)
		{		
			if (! m_hasTranscribed)
				transcribeToEditableListless();

			if (element == null) // start and end validations
			{			
				// nothing here yet
			};
			
			// check for validations that only I execute
			// can structured just like popup.validate()
			for (i=0; i<m_activeElements.length ; i++)
			{
				elementI=m_activeElements[i];			

				if (elementI.getAttribute("ydr:disabled") != "disabled")			
				{			
					validations = ydrBrowser.xml.selectNodes(elementI, "validation");
					targetI = document.getElementById(elementI.getAttribute("id"));
										
					isHidden = ydrClientScripts.isHidden(targetI);
//					format = targetI.getAttribute("ydr:fmt");  -- not used here at the moment
					
					if (!isHidden && (validations.length) && (ydrBrowser.html.elementValue(targetI).length>0))
					{
						for (j=0; j<validations.length; j++)
						{
							with (validations[j])
							{
								switch (getAttribute("action"))
								{
									case "unique" :
										//TODO not tested
										value = ydrBrowser.html.elementValue(targetI);
										
										switch (getAttribute("within"))
										{
											case "%table%" : 
												if (getAttribute("testedValue") != ydrBrowser.html.elementValue(targetI))
												{
													setAttribute("testedValue") = ydrBrowser.html.elementValue(targetI);
													setAttribute("testedResponse", "Checking uniqueness on server");
													setAttribute("testedClass", "info");
													
													with (ydrClientScripts.XMLHttpRequest())
													{
														onreadystatechange = 
															function()
															{
																
																if (this.readyState ==4)
																{
																	with (this.responseXML.documentElement)
																	{
																		var elementI = ydrBrowser.xml.selectSingleNode(
																			m_activeElements[0].parentNode
																			, "execute[@id='"+getAttribute("id")+"']/validation[@action='unique']");
																			
																		switch (getAttribute("occurs"))
																		{
																			case "0":
																				elementI.removeAttribute("testedResponse");
																				break;
																			
																			case "-1":
																				elementI.setAttribute("testedResponse", getAttribute("error"));
																				elementI.setAttribute("testedClass", "error");
																				break;
																			
																			default :
																				elementI.setAttribute("testedResponse", "The value is expected to be unique");
																				elementI.setAttribute("testedClass", "error");
																		}
																	}
																}
															}
															
															//TODO make work for other tables
														open("GET", rootDir+"/ydr/data/ydrExclaimList.asp?action=occurs&xpath="
															+ "TABLES/CONTACTRows/CONTACT[@CONTACTID='" 
																+ ydrClientScripts.HTMLencode(ydrBrowser.html.elementValue(targetI),"attribute") +"']"
															+"&origin=userDetails"
															+"&id="+targetI.getAttribute("id")
															);
														send(null);
													}
												};
												
												if (getAttribute("testedResponse") != null)
													result += "<p class='" + getAttribute("testedClass") + "'><span class='object'>"
														+ ydrPopup.firstPeerTD(targetI).innerHTML 
														+"</span> "+ ydrClientScripts.HTMLencode(getAttribute("testedResponse"))
														+"</p>";
														
												break;											
												
											case "%row%" : //TODO
												if (! uniqueHere(i,myValue) )
													result += "<p class='error'><span class='object'>"
														+ ydrPopup.firstPeerTD(targetI).innerHTML 
														+"</span>The value <span class='emph'>"+ ydrClientScripts.HTMLencode(myvalue)
														+"</span> is expected to be unque within this list.</p>";
												
												if (getAttribute("within") == "%table%")
												{
													//TODO
												};
												break; 
												
											default:
												window.alert ("invalid 'within' clause in unique validation");
										};
										
										break;
									
									case "xpath" :
										if (getAttribute("testedValue") != ydrBrowser.html.elementValue(targetI))
										{
											// cannot allow quotes as it gets confusing with levels of quoted strings in xlst
											setAttribute("testedValue", ydrBrowser.html.elementValue(targetI).replace(/\"/g, "'"));
												ydrClientScripts.elementValueUpdate(targetI, getAttribute("testedValue"));
											setAttribute("testedResponse", "Checking xpath");
											setAttribute("testedClass", "info");
											
											with (ydrClientScripts.XMLHttpRequest())
											{
												onreadystatechange = 
													function()
													{
														
														if (this.readyState ==4)
														{
															with (this.responseXML.documentElement)
															{
																var elementI = ydrBrowser.xml.selectSingleNode(
																	m_activeElements[0].parentNode
																	, "execute[@id='"+getAttribute("id")+"']/validation[@action='xpath']");
																	
																switch (getAttribute("occurs"))
																{																
																	case "-1":
																		elementI.setAttribute("testedResponse", getAttribute("error"));
																		elementI.setAttribute("testedClass", "error");
																		document.getElementById(elementI.parentNode.getAttribute("id"))	
																			.setAttribute("title", "In error")
																		break;
																	
																	default :
																		elementI.removeAttribute("testedResponse");
																		document.getElementById(elementI.parentNode.getAttribute("id"))	
																			.setAttribute("title", "This query currently returns " + getAttribute("occurs") + " rows");
																		break;
																}
															}
														}
													}
													
													//TODO make work for other tables
												open("GET", rootDir+"/ydr/data/ydrExclaimList.asp?action=occurs&xpath="
													+ "TABLES/CONTACTRows/CONTACT[" + escape(ydrBrowser.html.elementValue(targetI))+ "]"
													+"&origin=userDetails"
													+"&id="+targetI.getAttribute("id")
													);
												send(null);
											};
										};
										
										if (getAttribute("testedResponse") != null)
											result += "<p class='" + getAttribute("testedClass") + "'><span class='object'>"
												+ ydrPopup.firstPeerTD(targetI).innerHTML 
												+"</span> "+ydrClientScripts.HTMLencode(getAttribute("testedResponse"))
												+"</p>";
										break;
									
								};
							};
						};
					};	
				};
			};

			// ensure we have an acceptable number of rows
			try {if (m_gridTable.lastChild.rows.length<m_minOccurs && (m_gridPane && m_gridPane.style.visibility == "inherit"))
				result += "<p><span class='object'>&lt;rowset&gt;</span>You must have at least " + m_minOccurs +" entries in this data set.</p>"
			} catch (e) {};

			// if i change and my value affects another element then change the list of members of the other element
			//TODO needs changing along with the referenclist create 
			try 
			{
				constrainMapAttributes= element.getAttribute("ydr:ConstrainMap").split(" ");
				
				for (i=0; i<constrainMapAttributes.length; i++)
				{
					selectOptionsMap(m_referenceList[constrainMapAttributes[i]]);
				};
			} catch (e) {};

			setTimeout("ydrExclaimListClient.showAdvisory()", 1);		
		};
		
		return result;
	}

	// like in ydrClientTextEdit
	// do on a timeout as the value has not been set when validate was called	
	// but remember that this page might have disappeared
	this.showAdvisory = function() 
	{
		if (m_editorAdvisory)
		{
			try 
			{	
				 
				var advisory = m_popup.advisory()
				if (advisory.length>0)
					advisory="<div style=background-color:#ffe4e1>"+advisory+"</div>"; //mistyrose
				m_editorAdvisory.innerHTML = advisory;
				
				// stop the real one showing around the edges
				m_popup.advisoryHide();
				//if (advisory.length)
				//	m_popup.advisory("<div style='visibility:hidden'>"+advisory+"</div>");
					
			}catch (e) {};  
		};	
	};
	
	// this is to be given by the caller of the ydrPopup.show() as the ONEXIT handler to be obeyed when the data is client side committed
	this.commit = function(index, popupElement, popup)
	{	
		// when the user clicks the change button in the form given via contacts.change()
		// we issue the resultant data - if the data is saved by the server we show an OK message and then 
		// ask for the details page to be reuilt with the changed data from the server
		// if it were an error then we reshow the page with the error message - this also applies for pagenotfound etc

		if (index >=0)
		{			
			var url = popup.intermediateURL();
			
			if (url == null)
			{
				popup.advisory("<p class='error'>Cannot use ydrExclaimListClient.commit() from a popup form that is not configured with the required data in the <span class='object'>form</span> element to enable this.  Data posting URL missing</p>");
				popup.reshow();
				return false;
			}
			else
			{		
				if (index >=0)
				{
					var httpRequest = ydrClientScripts.XMLHttpRequest();
					httpRequest.open("POST",url);	
					httpRequest.onreadystatechange=function()
						{
							if (this.readyState == 4)	
							{	
								var response = this.responseText;
								var responseIndicator = response.indexOf("class='changeOK");
								if (responseIndicator <0 || responseIndicator>50 )
								{
									m_popup.advisory(this.responseText);
									m_popup.reshow();
								}
								else
								{							
									var nextURL = m_popup.nextURL();
									if (typeof nextURL == "string")
									{
										if (nextURL.indexOf("http") == 0)  // ie does start http:
										{
											window.location.href = nextURL
										}
										else if (nextURL == "")
											window.location.href = window.location.href 
										else
										{
											eval(nextURL)
											m_popup.hide()
										}
									}
									else
									{
										m_popup.hide("cascade"); //20100102
										//ydrClientPane.jiggle(); // cos we get lost screens
										//ydrBrowser.html.elementClick(nextURL);
									};
								};
							};
						};
					
					httpRequest.send(ydrBrowser.xml.xml(ydrPopup.nodeWalk(true, "withGrid")));	
					
					return true;	// forces 'please wait' to show temporarily rather than auto reshow of parent page
				};
			};
		};
	};	
	
	// components for when dialogZones are present with a tabber menu 
	this.tabberPreEvent; // can be set externally to a function event for when tabber is called
		// if present then must return true otherwise focus stays on the original tab
	this.tabber = function (tabElement)
	{
		// expects a tab from a tab list of the form generated by ydrMenu in horizontal mode
		// expects a series of div following at the same level as the outermost menu div
		// makes the div that matches the tab shown
		
		// first we make sure there are no unsaved changes on the current tab
		// then try the tabberPreEvent
		// then switch the visibility
		
		//TODO validate that if we object then we dont switch the selected tab marker in the menu
stop();
		var i;
		var iCanMove=true;
		
		// find the target data content div - these are ordinally arranged as the next siblings of the menu outer
		var menuContainer = tabElement.parentNode;
		while (menuContainer.nodeName.toLowerCase() != "div")
			menuContainer = menuContainer.parentNode;
		
		menuContainer = menuContainer.parentNode.parentNode; // up yet one more level
	
		var targetDiv;
		targetDiv = menuContainer.nextSibling.firstChild;
		
		// we need to find the data content div that matches the tab that is being pressed
		// so iterate through the anchors - for each anchor that is not the right one we move on to the next content div
		// so that by the time we break from the loop the targetDiv is the one with the required content
		var items;
		with (items = menuContainer.getElementsByTagName("a"))
		{
			for (i=0; i<length; i++)
			{
				if (items[i] == tabElement) 
					break; 
					
				targetDiv = targetDiv.nextSibling;
			};
		};
		
		// check is active and permitted to go to it - and if not dont go to it
		if (this.tabberPreEvent)
			iCanMove = this.tabberPreEvent(targetDiv);
		
		if (iCanMove)
		{
			// iterate through the content divs making just the one visible
			var div= menuContainer.nextSibling.firstChild;
			
			while (div)
			{
				div.style.display = div == targetDiv ? "block" : "none";
				div=div.nextSibling;
			};
		};
		
		return false;
	}
};

