// a generic popup mechanism for all apps
var ydrPopupCreator = function (instanceName)
{
/* members 
	adopt(element) ' adopts a preexisitng form to allow validations to be performed
		do not call any other methods - it looks after itself
		the element must be a div with two further elements the first is the given page and the second is an emtpy div that 
		will show the error messages
	show(message, title [, size ,file,  onExit, onValidate, buttons, mainElement, popupElement]) 
		- triggers a pop up from a client page
	showRemote(url)
	cookie()  a premade example popup that describes cookies
	onload() the executable for the loaded popup - private to this implementation
	reshow() reshows the last popup
	ydrClientEditInitiator(button, textarea, tag) tries to initiate formatted text editing as an overlay popup
	nodeWalk() extracts values so can be used by ydrData (uses ydrClientScripts.nodewalk on m_activeElement)
	// by default utilises ydrPopup.asp
	
	
	// links with ydrExclaimListClient - if exists then calls init(), exit(), valueList() and validate()
*/
//stop();
	var m_this=this;
	var m_title;
	var m_onExit;
	var m_onValidate;
	var m_mainElement;
	var m_popupElement;
	var m_popupErrors;
	var m_popupButtons;
	var m_popupEditorContainer
	var m_activeElement; // the element within popupelement that contains the controlled data (allowing app to manage other 
			//data directly - say in private popup - eg see the text editor
	var m_actionElement; // the action buttons list in the m_popupElement 
	var m_activeElements; // this will hold those input fields that we have active actions eg ydr:fmt or ydr:validation
	var m_lastMenuSelectedItem;
	var buttonsHTML="";
	var m_zoneToLeft;
	
	this.parentPopup; // if we are in a nest of popups then refers to the parent popup
		// it is up to the caller of me to declare if it a parent
		
	this.instanceName=instanceName;
	this.popupElement = function() { return m_popupElement };
	var onInternalClick; // string code to wrap events
	this.onInternalClick = function (script) { onInternalClick = script };
		
	this.restoreIndex=-98989898;
	var restorePoint;
	var m_eventCache = new Object(); // contains events we have substituted for our own - keyeyed by element.id
	function adopt(newInnerHTML)
	{
		// we now look for any field that has formatting or validation and cache these so we can browse the list in validate()
		// at the same time we remove any existing onblur event and replace it with our own
		// if innerHTML is null, then we presume that  m_popupElement has already been preset to the required string 
		// generally the developer does this my capturing the m_popupElement.innerHTML as it is on an ordinary call prior to 
		// as it is 
			// m_popupElement.innerHTML is set below
		// the gymnastics aorund having null innerHTML is because ie doesnt properly service iframe in dynamic HTML and you 
		// can get blank forms with real content that is editable blind - crazy. so if this happens you have to create the form
		// capture a copy of it (including the trailing <div>) and then put the lot into a cache file (eg via menu.boilerplate)
		// then force the content onto the page at server rather than client - set ydrClientTextEdit as example
		
		var attr;
		var target;

		m_popupElement.style.marginLeft =0;

		if (newInnerHTML != null)
		{
			m_popupElement.innerHTML = newInnerHTML + "<div></div>";
			
			if (ydrClientPane.currentPane() != m_popupElement)
				ydrClientPane.currentPanePush(m_popupElement);
			// if the popup area is adopting the same basic class as the underling main area then see if we can force wide popups to reduce their
			// width to the same as the main 
			try 
			{
				if (m_popupElement.getAttribute("className") == m_mainElement.getAttribute("className"))
				{
					if (m_popupElement.scrollWidth > m_mainElement.scrollWidth)
					{
						m_popupElement.firstChild.style.width= m_mainElement.scrollWidth+"px";
						m_popupErrors.style.width= m_mainElement.scrollWidth+"px";
						
						if (m_popupElement.scrollWidth > m_mainElement.scrollWidth)
						{
							with (m_popupElement)
							{
								if (previousSibling.className == 'leftMenuStyle' && previousSibling.firstChild)
								{
									m_zoneToLeft = new Object();
									m_zoneToLeft.element = previousSibling.firstChild;
									m_zoneToLeft.visibility = m_zoneToLeft.element.style.visibility;
									m_zoneToLeft.element.style.visibility = "hidden";
								};
								
								var offset = Math.min(offsetLeft, scrollWidth +30 - m_mainElement.scrollWidth );
								getAttribute("style").marginLeft = - offset;
								firstChild.style.width = (m_mainElement.scrollWidth.scrollWidth + offset) +".px";
							};
						};
					};
				};
			} catch (e) {};			
		};

		m_popupErrors = ydrBrowser.html.lastElementChild(m_popupElement);
		while (m_popupErrors && m_popupErrors.nodeType == 3)
			m_popupErrors = m_popupErrors.previousSibling;
				
		m_popupButtons = document.getElementById(m_this.instanceName+"Buttons");			
		
		if (m_popupErrors == null)
			window.alert ("## developer error - div for error messages omited from dialogue");
		
		/*m_popupElement.innerHTML =
			innerHTML
				.replace(/<input /g,"<input onblur='ydrPopup.validate(this)' ")
				.replace(/ type=['"]submit["']/g," type='submit' onclick='return ydrPopup.closeSubmit(this)';")
				.replace(/<textarea /g,"<textarea onblur='ydrPopup.validate(this)' ")
				.replace(/<select /g,"<select onblur='ydrPopup.validate(this)' ")
			+"<div></div>";
		*/
		restorePoint = m_popupElement.innerHTML;

		/*var cacheExtractMode=false;
		if (cacheExtractMode)	
		{
			// only works on IE 
			alert("cache pause loop") // this just hangs until you stop the debugger - cannot put a stop here as trapping can cause confusion
	
			var cachable="<div id='ydrPopupEditor' style='position:absolute;width:100%; height:100%; visibility:hidden'>\n"+innerHTML.replace(/>/, " >")+"\n<div></div>\n</div>";
			// in developermode copy cachable to clipboard and past into built file
			// eg ../ydr/ydrClientTextEdit.html to ../ydr/ydrClientTextEditBuilt.html  
			// then refer to it in the menu .xml (eg see belmontroadregiate >> contacts/menExtender.xml >> boilerPlate("ydrClientTextEdit") and place it the webpage 
			//		(eg see list.asp >> <%= menu.boilerPlate("ydrClientTextEdit") %>
		};
		*/
		
		var tempAE =m_activeElement; // really only for debugging
		if (m_activeElement ==null)
			m_activeElement = m_popupElement
		else if (typeof m_activeElement=="string")
			m_activeElement = document.getElementById(m_activeElement);
			//stop();
		if (m_activeElement == null)
		{
			window.alert("developer set up error for ydrPopup " 
				+ tempAE
				+ "\n\nCheck if\n\n <%= ydrMenu.boilerPlate('ydrClientTextEdit') %> \n\nappears immediately after\n\n<%= menu.boilerPlate('content') %>\n\nAlso check that an element with id ydrPopupEditor exists.") ;
			return
		}; 
		
		m_activeElements = m_this.nodeWalk(true);
		//stop();
		//why did we do this in reverse ? for (var i=m_activeElements.attributes.length-1; i>=0; i--)
		// it causes all the validations to be done in the reverse order
		if (newInnerHTML) // otherwise readopting one already presumed processed
		{
			for (var i=0; i<m_activeElements.attributes.length; i++)
			{
				attr=m_activeElements.attributes[i];
				if (attr.nodeName.charAt(0) != "_")
				{				
					target= document.getElementById(attr.nodeName);
				
					if (target == null)
						window.alert ("ydrPopup.adopt() failed to locate the edit field for "+ attr.nodeName)
					else
					{			
						var ydrfmt=target.getAttribute("ydr:fmt");
						var ydrvalidation = target.getAttribute("ydr:validation") ;
						
						if (( ydrfmt != undefined) || (ydrvalidation != undefined))
						{
							// the blur event triggers the validation - there might already be one there
							// create an event that triggers the validation in this popup combined with any previous event
							var newBlurEvent;
							var previousBlurEvent = "";
							if (target.onblur)
								previousBlurEvent = target.onblur.toString();
							
							eval('newBlurEvent = function(e){' 	+previousBlurEvent +'; '+m_this.instanceName  +'.validate("' + target.getAttribute("id") + '")}');
							ydrBrowser.html.addEvent(target, "blur", newBlurEvent);
							
							var myEl;
							with (myEl = m_activeElements.appendChild(m_activeElements.ownerDocument.createElement("execute")))
							{
								setAttribute("id", attr.nodeName);
								
								if (ydrvalidation != undefined)
								{
									try 
									{ 					
										var vals=ydrBrowser.xml.document("<validations>"+ydrvalidation+"</validations>").documentElement;
										vals = ydrBrowser.xml.selectNodes(vals, "validation");
										var valItem;

										for (var j=vals.length-1; j>=0; j--)
										{ 
											valItem = vals[j];
											if (valItem.nodeType == 1 && valItem.getAttribute("action") == "formattable" )
												try 
												{
													if (ydrClientTextEdit && ! target.disabled) 
													{
														with (target.parentNode.insertBefore(document.createElement("span"), target))
														{
															// note doing it this with with a spurious span avoid the hassle of using ydrBrowser.html.addEvent
															// which has a problem since attr.nodeName has changed by the time the event runs
															var myId = target.getAttribute("id")+"EditButton";
															
															innerHTML="<a id='" + myId +"' class='button' href='' onclick='"
																	+m_this.instanceName+".ydrClientEditInitiator(this, document.getElementById(\""
																		+attr.nodeName+"\"), \"" + m_title +"\")"
																+" ; return false"								
																+"' style='position:absolute; left:1em; width:3em;'>"
																+"Use<br />Editor</a>";
															
															// see if this looks like HTML possibly surrounded by whitespace
															// if so then auto go to editor after the form is created
															if (ydrBrowser.html.elementValue(targetI).search(/^\s*<.*>\s*$/) == 0)
																window.setTimeout("ydrBrowser.html.elementClick(document.getElementById('"+myId+"'))",1)
															
														};
													}									
												} catch (e) {} // just fails if the object to support it is loaded on the page
											else
												ydrBrowser.xml.appendChild(valItem, myEl, "copy");										
										 }
									}
									catch (e) 
									{
										 window.alert(m_this.instanceName+".show - validation attribute of " 
											+ attr.nodeName + " is not a valid XML substring\n\n"
											+ ydrvalidation +"\n\n"
											+e.message) 
									};
								};		
							}
						};
					};
				};
			};
		};

		try {
			document.getElementById(m_activeElements.attributes[0].nodeName).focus()
		} catch (e) {};
		
		m_activeElements = m_activeElements.childNodes;		

		try {ydrExclaimListClient.init(m_this) } catch (e) {}; // 20081231 was at line 175 - monitor move for side effects
		m_this.validate();	
		
		//if (innerHTML != null)
			//m_popupElement.innerHTML = m_popupElement.innerHTML; // ie bug - the ydrPopup.ydrClientEditInitiator events dont fire
				// without this line - stopping one getting to the editform
		
		try { ydrClientPane.paneExpand(m_popupElement, null, "noSave")  } catch (e) {};
	
		// now ydrPaneExpand wont find any onload event because it is hidden deep in m_popupelement
		// likewise ydr:focus
		
		var onLoadEvent;
		try
		{	
			onLoadEvent = ydrBrowser.html.firstElementChild(m_popupElement).getAttribute("ydr:onload") 
		} catch (e) {};
		
		if (onLoadEvent == null)
			onLoadEvent = ydrBrowser.html.firstElementChild(m_popupElement).getAttribute("onload");
			
		if (onLoadEvent)
		{
			try
			{
				eval(onLoadEvent);
			}
			catch (e) { window.alert("Cannot execute onload event for ydrPopup.show pane\n\n"
				+e.message) };
		};
		
		// if requested by the caller we wrap the event for anchors of class button with the given script
		// ??? does it work in firefox???
		if (onInternalClick)
		{
			var attr;
			var anchorElement;
			
			var items;
			with (items = m_popupElement.getElementsByTagName("a"))
			{
				for (var i=0; i<length; i++)
				{
					anchorElement = items[i];
					attr = anchorElement.getAttribute("onclick");
					if (attr == null )
						attr = "return true"
					else
						attr = attr.toString().replace(/anonymous/, "click");
						
					if (anchorElement.getAttribute("className")=="button")			
						eval("ydrBrowser.html.addEvent(anchorElement, 'click',"
								+ "function(e) {" + attr + ";"
								+ onInternalClick
								+"})");			
				};
			};
			onInternalClick = null;
		};
		
		// find the value of the ydr:focus attribute if any
		if (newInnerHTML == null)
			newInnerHTML = m_popupElement.innerHTML;
		
		if (newInnerHTML.search(/ydr:focus/) >= 0)	
		{
			/* the reg exp function below is dreadfully slow if there is no focus string to be found */ 
			var focus = newInnerHTML.replace(/([\s\S]* ydr:focus=['"])(.*?)(['"][\s\S]*)/,":$2");
			
			if (focus.charAt(0)==":")
				document.getElementById(focus.substring(1)).focus();
		};
		//window.scrollTo(0,0);	
	};

	// revert to the data when the form was first opened
	this.restore = function()  {		adopt(restorePoint);	}
	
	// takes the given HTML, marks up textarea, input and select elements to have onblur event
	// (expecting that no such event pre-exists)
	// the second div represents the place for the 
	this.adopt = function(element, newInnerHTML)
	{
		m_popupElement=element;
		adopt(newInnerHTML);
	}
	
	this.deeperModal = function(state)
	{
		// when deeperModal we have another object taking the focus - we dont care what, could be another popup
		// hence we dont want our exit buttons active
		
		switch (state)
		{
			case "push" :
				m_popupButtons.innerHTML = "&#160;";
				break
			
			case "pop" :
				m_popupButtons.innerHTML = buttonsHTML;
				break; 
				
			default : window.alert("invalid call");
		};
	}
	
	this.reshow = function()
	{
		m_popupErrors.style.display='block';
		m_popupElement.style.visibility='inherit';
		if (m_mainElement != undefined)
			m_mainElement.style.visibility='hidden';
		m_popupButtons.innerHTML = buttonsHTML;
	}

	this.show =  function(message, title, size, onExit, onValidate, buttons, mainElement, popupElement, activeElement)
	{
		m_title=title;
		m_activeElement=activeElement;
		try { 	m_lastMenuSelectedItem = ydrClientMenus.lastMenuSelectedItem; } catch (e_) {};
		/*
		message to be issued - format is innerHTML OR null in which case the popupelement has already been populated
		title banner for the message - format is innerHTML
		size is small medium or large; or it may be a css string 
			optional rendering size for the message - legacy values accepted but ignored - can instead hold css
		
		onExit is optional, if present then is the name of the method in the main page to call when then window closes; this should be the	
			method name only, it will be called with a single parameter being the value of the button pressed
		buttons if missing then is the presumed to be the gstring '1^OK' - gstring can be a string or a gstring object naming 
			the buttons to show
		
			
		 not using dialogArguments gets around microsofts changes that now prevent dialogArguments being used
		 easily across security zones
		 
		 activeElement is the element ID (within the popElement that contains data to be managed).  If missing then is assumed to be 
			the same as the popupelement
		*/
			
		if (title == undefined)
			title=""
		else if (title.indexOf("<")<0)
			title="<h2 style='text-align:center'>"+title+"</h2>";
		
		m_onExit = onExit;
		m_onValidate = onValidate;
		
		m_popupElement=popupElement;
		if (m_popupElement == undefined)
			m_popupElement = document.getElementById(m_this.instanceName);
			
		if (m_popupElement == undefined)
		{
			m_popupElement = document.createElement("div");
			m_popupElement.style.cssText="position:absolute";
			m_popupElement.setAttribute("id",m_this.instanceName);
			document.body.appendChild(m_popupElement);
		};
		
		m_popupElement.style.visibility='inherit';

		m_mainElement=mainElement;		
		if (m_mainElement == undefined)
			m_mainElement = document.getElementById("ydrPopupMain");
		
		if (m_mainElement == undefined)
			m_mainElement = document.body.childNodes[document.body.childNodes.length-2];  
				// ie the last element before the new popup element
		
		//alert(m_mainElement == undefined);
		//alert("style="+m_mainElement.style); //**FF**
		try {
			m_mainElement.style.visibility='hidden';
		} catch (e) { stop(); /* fire fore firefox only*/ };
				
		// the following is to ensure legacy calls still word even though size is not supported - but does 
		// facilitate css if present
		if (size == null)
			size=""
		else
		{
			switch (size.toLowerCase())
			{
				case "small" : 
					 size="width:60%";					
					 break;
					 
				case "medium" : 
					 size="width:40%";					
					 break;
				case "large" : 
					// these are legacy items no longer supported
					 size="width:90%";					
			};
		};
		
		buttonsHTML="";
		if (buttons == undefined)
			buttons =  new ydrGenericScripts.gString("'1^OK'","^")
		else if (typeof (buttons) == "string")
		{
			buttons =  new ydrGenericScripts.gString(buttons,"^");
			buttons.add(m_this.restoreIndex, "Restore") //TODO only to add to data entry dialogs
		};
		
		buttonsHTML=buttons.buttonsHTML(m_this.instanceName);

		if (message == null)
			adopt()
		else
			adopt (
				("<table class='ydrPopup' "
					+"style='%size%'"
					+">"
					+"<tr><td colspan='2' style='border-bottom:thin solid blue'>%title%</td></tr>"
					+"<tr>"
						+"<td>%message%</td>"
						+"<td id='" + this.instanceName+"Buttons' class='YDRpopupButtons'>"+buttonsHTML+"</td>"
					+"</tr>"
					+"</table>"
						).replace(/%title%/,title)
						.replace(/%size%/,size)
						.replace(/%message%/, message)
				);
		return false;
	};
	
	// this is necessary only because different browsers have different object counts in a form - ie netscape typically hsa 5 and ie has 3 for the same innerHTML
	// we assume the parent node is a TR and this returns the first TD of that TR 
	this.firstPeerTD = function(nodeTD)
	{
		while (nodeTD && nodeTD.nodeName.toLowerCase() != "tr")
			nodeTD= nodeTD.parentNode;
		
		return nodeTD.getElementsByTagName("td")[0];
	};
	
	function formalName(targetI)
	{
		var result="";
		var formal =   ydrBrowser.html.elementValue(targetI).replace(/\./g," ").replace(/\s+/g," ").trim().split(/\s*,\s*/);
		if (formal.length == 3)							
		{
			//ensure the standard abbreviations are terminated by full stop
			switch (formal[2].toLowerCase())
			{
				case "mr" : 
				case "mrs" :
				case "dr" :
					formal[2]=ydrGenericScripts.proper(formal[2])+".";
			};
			
			var forenames = formal[1].split(/[\s]/);
			for (var ji=0; ji<forenames.length; ji++)
			{
				forenames[ji] = ydrGenericScripts.proper(forenames[ji]) 
						+ ((forenames[ji].length == 1) ? "." : "");
			};
			formal[1] = forenames.join(" ");
			formal[0] = ydrGenericScripts.proper(formal[0]); 
			formal[2] = ydrGenericScripts.proper(formal[2]); 
			ydrClientScripts.elementValueUpdate(targetI, formal.join(", "));
		}	
		else
			result = "<p class='error'><span class='object'>"
				+ ydrBrowser.html.text(m_this.firstPeerTD(targetI))
				+ "</strong> please enter in the form <span class='emph'>family name, first name and other initials, title</span>."
				+"&#160; For example <em><strong>Bloggs, Fred C., Mr.</strong></em></p>";
				
		return result;
	}
	
	// closes the popup and shows the original screen -also triggers call to the callers event onExit handler
	var recentlyTriedUrls=new Object();
	var currentlyTryingURL;
	var currentlyTryingRequest;
	xx=0
	this.validate = function(element, finalFlag)
	{//stop();
		var valMessage = "";
		var elementI; // the surrogate element in the walk
		var targetI; // the real element in the document
		var format;
		var validations;
		var validation;
		var i;
		var isHidden;
		var value;
		//stop();
		
		if (typeof element == "string")
			element = document.getElementById(element);
		
		// defensive code for error situations
		if (! m_activeElements ) return null
		
//		if (m_popupErrors.style == null)
//			m_popupErrors.setAttribute("style", "display='block'")
//		else
			m_popupErrors.style.display='block';
		
		var l=m_activeElements.length // have to do in stages as firefox fails to run the loop otherwise
		for (i=0; i<l; 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");
				value = ydrBrowser.html.elementValue(targetI)	;		
						
				// see if the element is the currently changing element and do any normalisation of values
				if (! isHidden)
				{				
						if ((format != undefined) && (value.length>0))
						{
							switch (format)
							{
								
								case "0decimals" :
									{ 
										var value= parseInt(value);
										if (! isNaN(value))
											ydrClientScripts.elementValueUpdate(targetI, value);
									};
									break;
									
								case "associative" :
									break;
								
								case "counter" :
									break; 
												
								case "date" :	
									try {ydrClientScripts.elementValueUpdate(targetI, ydrDateStatic.user(value).user())  }
									catch (e) {};
									break;
									
								case "email" :
									ydrClientScripts.elementValueUpdate(targetI, value.replace(/\s*/g, "").trim());
									break;
																	
								case "file" :
									// no checks as yet
									break; 
								
								case "reference" : 
									// handled by ydrExclaimListClient.validate()
									break;
								
								case "postcode" :
									ydrClientScripts.elementValueUpdate(targetI, value.toUpperCase().replace(/-/g, "").replace(/\s{2,}/g," ").trim());
									if ((value.indexOf(" ")<0) && (value.length>4))
										ydrBrowser.html.elementValue(targetI, value.substr(0,value.length-3)+" "+ value.substr(value.length-3));			
									break;
									
								case "string" :
									ydrClientScripts.elementValueUpdate(targetI, value.trim());
									break;
									
								default :
									window.alert ("ydrPopup["+m_this.instanceName+"]: unrecognised format " + format + " at " +targetI.id)
							};
						};		
					//};
					
					// see if there are any validation errors
					// firstly as implict for the value format			
					// note we redo the test here since the above corrections can have changed the value
					value = ydrBrowser.html.elementValue(targetI);		// repeated because it might have changed
				
					if (!isHidden && (format != undefined) && (value.length>0))
					{
						switch (format)
						{
							case "0decimals" :
									if (isNaN(parseInt(value)))
										valMessage += "<p class='error'><span class='object'>"
											+ m_this.firstPeerTD(targetI).innerHTML
											+"</span> Integer value expected</p>";
									break;
								
							case "associative" :					
								break;

							case "counter" :
								break;
								
							case "date" :
								if (value =="--" || ydrDateStatic.user(value).user() != value )
									valMessage += "<p class='error'><span class='object'>"
										+ m_this.firstPeerTD(targetI).innerHTML
										+"</span> Date format is not recognised, expected <span class='emph'>dd-mmm-yyyy hh:mm:ss</span>"
											+" for a date and time or just  <span class='emph'>dd-mmm-yyyy</span> for a date and where mmm is"
											+" in the language of your local machine, several other formats accepted including <span class='emph'>dd mm yy</span>"
											+"  - to try again enter a new date and press the <strong><em>tab</em></strong> key</p>";
								
								break;
							
							case "email" :
								if (value.search(/^[\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[a-z]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$/i) <0)
								{
									valMessage += "<p class='error'><span class='object'>"
										+ m_this.firstPeerTD(targetI).innerHTML
										+"</span> Must be a valid email address</p>";
								}
								break;
								
							case "file" :
								// no checks as yet
								break; 
												
							case "postcode" :						
								if (value.search(/^[A-Z]([0-9]|[A-Z0-9]{2}|[A-Z][0-9][A-Z0-9]) [0-9][A-Z]{2}$/i) <0)
								{
									valMessage += "<p class='error'><span class='object'>"
										+ m_this.firstPeerTD(targetI).innerHTML 
										+"</span> must be formatted as a valid postcode</p>";
								}			
								break;
							
							case "reference" :  // handled by ydrExclaimListClient.validate()
								break; 
								
							case "string" : 
								// nothing to do 
								break;
										
							default :
								window.alert("unrecognised value format " + format + " in " + targetI.id);
						};		
					};
					
					var action;
					for (var j=0; j<validations.length; j++)
					{
						validation=validations[j];
						
						value = ydrBrowser.html.elementValue(targetI);		// repeated because it might have changed
						action  =validation.getAttribute("action");
						switch (action)
						{
							case "assert" :
								// asserts that a javascript condition is valid
								if (! ydrUtils.exclaimEval(validation.getAttribute("value")))
									valMessage += "<p class='error'><span class='object'>"
												+ m_this.firstPeerTD(targetI).innerHTML 
												+"</span>: " 
												+ ydrClientScripts.HTMLencode(validation.getAttribute("reason"))
												+"</p>";
												
							case "cleanSpace" :
								// removes multiple spaces and other whitespace down to a single space then trims and remaining from front and end
								if (value.length>0)
									ydrClientScripts.elementValueUpdate(targetI, value.replace(/\s+/g," ").trim());
								break; 
								
							case "css" : // restricts a string to be a valid css whole or part
								if (value.length>0)
								{
									var cssSection=validation.getAttribute("section");
									with (document.createElement("div"))
									{
										var myCss=value;
										if (cssSection)
											myCss= cssSection+":"+myCss;
										
										try { style.cssText = myCss; }
										catch (e)
										{
											valMessage += "<p class='error'><span class='object'>"
												+ m_this.firstPeerTD(targetI).innerHTML 
												+"</span> must be " 
												+ ((cssSection == null) ? " a general CSS statement "
													: " a CSS <span class='emph'>" + cssSection + "</span> statement")
												+".</p>";
										};
									};
								};
								
								break;
								
							case "date" : // must be whole date no time
								if (value.length>0)
									ydrClientScripts.elementValueUpdate(targetI, ydrDateStatic.user(value).user().substr(0,11));
								break;

							case "dateAlive" : // must be a whole date and plausible for a living human 0..120 years old
								if (value.length>0)
								{
									var newValue;
									with (ydrDateStatic.user(value))
									{
										ydrClientScripts.elementValueUpdate(targetI, user().substr(0,11));
										newValue = xml();
									};
									
									with (new ydrDate())
									{
										if (newValue>xml() || newValue < changeBy(-120, "years").xml())
										{
											valMessage += "<p class='error'><span class='object'>"
												+ m_this.firstPeerTD(targetI).innerHTML 
												+"</span> expected to be in living human range</p>";
										};
									};
								};
								break;	
							
							case "differs" :
								var otherField = document.getElementById(validation.getAttribute("value"));
								
								if (value.length >0 && value == ydrBrowser.html.elementValue(otherField))
									valMessage += "<p class='error'><span class='object'>"
												+ m_this.firstPeerTD(targetI).innerHTML 
												+"</span> may not have the same value as <span class='object'>"
												+ m_this.firstPeerTD(otherField).innerHTML 
												+"</span></p>";
								break;
								
							case "formalNameFuzzyList" :
								// after any keystroke we want to do another validate
								if (target.onkeyup == null)
								{
									var newEvent;
						
									eval('newEvent = function(e){ window.setTimeout('
										+ m_this.instanceName  +'.validate("' + target.getAttribute("id")
										+ '", "formalNameFuzzyList"),1)}');
										//alert(newEvent.toString())
									ydrBrowser.html.addEvent(target, "keyup", newEvent);
								}
								// note drop through
								
							case "formalNameFuzzy" :
								//with a fuzzy name we try to match the parts of the name to a contact on the server - we change to it if a 
								// unique entry is found - if not we leave the field as is until an ACCEPT button is pressed
								// at which point we force it into a standard format if not already there
								
								if (elementI.getAttribute("testedValue") != value && value.length>0)
								{
									elementI.setAttribute("testedValue", value)
									elementI.setAttribute("testedClass", "info");	
									elementI.removeAttribute("ydr:value"); // so we dont get leftovers from a previous one
									
									if (validation.getAttribute("allowNew") == "allowNew")
										elementI.setAttribute("testedallowNew", "allowNew")
									else
										elementI.setAttribute("testedResponse", "Checking contact name");	
									
									with (ydrClientScripts.XMLHttpRequest())
									{
										onreadystatechange = 
											function()
											{	
												//alert("ready state" + this.readyState)
												if (this.readyState ==4)
												{
													//alert(ydrBrowser.xml.xml(this.responseXML))
													try {
														with (this.responseXML.documentElement)
														{
															if ((responseXML.documentElement == null || nodeName =="div") 
																&& childNodes.length > 0
																) // otherwise ignore it
															{													
																var requestElementI = ydrBrowser.xml.selectSingleNode(m_activeElements[0].parentNode
																	, "execute[@id='" + getAttribute("id") +"']");
																var requestTargetI = document.getElementById(requestElementI.getAttribute("id"));
																requestTargetI.removeAttribute("ydr:data");
																var action = getAttribute("action");
																
																var items;
																with (items = ydrBrowser.xml.selectNodes(responseXML.documentElement, "//p"))
																{
																	switch (items.length)
																	{
																		case 0 :
																			if (requestElementI.getAttribute("testedallowNew") == "allowNew")
																			{
																				requestTargetI.title="No match: This will become a new contact";
																				var msg = formalName(requestTargetI);
																				if (msg.length > 0)
																					requestElementI.setAttribute("testedResponse", msg)
																				else
																					requestElementI.removeAttribute("testedResponse");																		
																			}
																			else
																			{	
																				requestElementI.setAttribute("testedResponse", "contact is not recognised");
																				requestElementI.setAttribute("testedClass", "error");
																			};
																			if (action == "formalNameFuzzyList")
																				try {document.getElementById("formalNameFuzzyList").style.visibility=="hidden" } catch (e) {};
																			
																			break;
																			
																		case 1: 
																			 requestTargetI.removeAttribute("title");
																			 ydrBrowser.html.elementValue(requestTargetI, ydrBrowser.xml.text(items[0]));
																			 requestTargetI.setAttribute("ydr:value", items[0].getAttribute("id"));
																			 
																			 var data = items[0].getAttribute("data")
																			 if (data)
																				requestTargetI.setAttribute("ydr:data", data);
																				
																			 elementI.setAttribute("testedValue",  ydrBrowser.html.elementValue(requestTargetI));
																			 requestElementI.removeAttribute("testedResponse");
																			 
																			 if (action == "formalNameFuzzyList")
																				try {document.getElementById("formalNameFuzzyList").style.visibility=="hidden" } catch (e) {};
																				
																			 window.setTimeout("ydrBrowser.html.elementClick( document.getElementById('"	
																				+ requestTargetI.id +"'),'blur')",1);
																			 break;
																			 
																		default :	
																			if (action = "formalNameFuzzyList" || length > 15)
																			{
																				
																				var fuzzyList=document.getElementById("formalNameFuzzyList");
																				
																				// if there is a list for a different field then remove it
																				if (fuzzyList!=null
																					&& requestTargetI.parentNode != fuzzyList.parentNode
																				)
																				{
																					fuzzyList.parentNode.removeChild(fuzzyList);
																					fuzzyList = null;
																				};
																				
																				// if there is no list make one
																				if (fuzzyList == null)
																				{
																					fuzzyList = requestTargetI.parentNode.appendChild(document.createElement("select"));
																					fuzzyList.setAttribute("id", "formalNameFuzzyList");
																					ydrBrowser.html.addEvent(fuzzyList,"change"
																						,function() 
																						{
																							var listElement = ydrBrowser.html.eventElement();
																							
																							if ( ydrBrowser.html.elementValue(listElement) !="")
																							{
																								 ydrBrowser.html.elementValue(requestTargetI, ydrBrowser.html.elementValue(listElement)); 
																								
																								 requestTargetI.setAttribute("ydr:value"
																									, listElement.options[listElement.selectedIndex].getAttribute("ydr:value"));  
																								 																								
																								 requestElementI.removeAttribute("testedResponse"); // note inheritance
																								 m_this.validate();
																							}; 
																						}
																					);	
																					ydrBrowser.html.addEvent(fuzzyList,"click"
																						,function() 
																						{ return
																							if ( ydrBrowser.html.elementValue(ydrBrowser.html.eventElement()) !=""
																								&& ydrBrowser.html.elementValue(ydrBrowser.html.eventElement()) !="--")
																							{
																								ydrBrowser.html.eventElement().style.visibility="hidden";
																								try {targetI.focus();} catch (e) {};
																							}; 
																						}
																					);	
																					ydrBrowser.html.addEvent(fuzzyList,"blur"		
																						,function() 
																						{
																							fuzzyList = ydrBrowser.html.eventElement();
																							fuzzyList.parentNode.removeChild(fuzzyList);
																							fuzzyList = null;
																						}
																					);
																				};
																				fuzzyList.style.cssText="";
																				
																				// remove all the entries from the list and create the new one
																				fuzzyList.innerHTML="";
																				var node;
																				for (var i=-1; i<length; i++)
																				{
																					node = fuzzyList.appendChild(document.createElement("option"));
																					
																					if (i<0)
																						ydrBrowser.html.text(node ,"--" )
																					else
																					{
																						ydrBrowser.html.text(node, 
																							ydrBrowser.html.elementValue(node, ydrBrowser.xml.text(items[i]))
																						);
																						node.setAttribute("ydr:value", items[i].getAttribute("id"));								
																					};																					
																				};		
																				
																				fuzzyList.focus();																		
																			}
																			else
																			{
																				requestTargetI.title="Multiple match: your input gives more than match";
																				requestElementI.setAttribute("testedResponse"
																					, "contact name is ambiguous - please type some more characters or words of the name");
																				requestElementI.setAttribute("testedClass", "error");
																			};
																	};
																};
															}
															else
															{
																elementI.setAttribute("testedResponse", "No matches were found");	
																requestElementI.setAttribute("testedClass", "error");
															};
														};
													} catch (e)
													{
														// we cannot report error as we cannot find out which field it relates to 
														/*requestTargetI.title="";
														requestElementI.setAttribute("testedResponse", "look up failed to find a match");
														requestElementI.setAttribute("testedClass", "developer");*/
													}
													
													m_this.validate();
												};
											};
										open("GET", rootDir+"/ydr/data/ydrContact.asp?action=contactFuzzy&contact="
											+ escape(value)
											+"&id=" + elementI.getAttribute("id")
											+ (validation.getAttribute("xPath") ? "&xPath=" + escape(validation.getAttribute("xPath")) : "" )
											+ (validation.getAttribute("data") ? "&data=" + escape(validation.getAttribute("data")) : "" )
											+"&action="+action);
										send(null);
									};
								};
								//stop();
								if (elementI.getAttribute("testedResponse") == null)
								{
									if (finalFlag == "final" && value.split(/,/).length != 3 && ! targetI.getAttribute("ydr:value") )
										valMessage += formalName(targetI) // note we just want the correct format
								}
								else if (elementI.getAttribute("testedResponse").charAt(0) == "<")
									valMessage += elementI.getAttribute("testedResponse")
								else
								{
									valMessage += "<p class='" + elementI.getAttribute("testedClass") + "'><span class='object'>"
										+ ydrPopup.firstPeerTD(targetI).innerHTML 
										+"</span> "+ydrClientScripts.HTMLencode(elementI.getAttribute("testedResponse"))
										+"</p>";
								};
								break;

							case "formalName" :
								// there must be at least three sectors in the formal name
								// which for the time being is the same as the CONTACTID
							
								// remove dots and multiple spaces and split at commas
								if (value.length>0)
									valMessage +=formalName(targetI)
								break;						

							// case lower >> see upper
							case "lowerCase" :
								if (targetI == element)
									ydrClientScripts.elementValueUpdate(targetI, value.toLowerCase()) ;
								break;
		
							case "monospace" : 
								if (value.search(/  /)>=0)
									ydrClientScripts.elementValueUpdate(targetI
										, value.replace(/ {2,}/g, " " ));
									
								break;			
		
							case "notNull" :
								if (value.length==0 || value=="--")
									valMessage += "<p class='error'><span class='object'>"
										+ m_this.firstPeerTD(targetI).innerHTML 
										+"</span> may not be <span class='emph'>null</span></p>";
								break;
								
							case "pattern" :
								if (value.search(validation.getAttribute("value"))<0)
									valMessage += "<p class='error'><span class='object'>"
										+ m_this.firstPeerTD(targetI).innerHTML 
										+"</span> must match the expected pattern: <span class='emph'>" 
											+validation.getAttribute("message")
											+"</span></p>";
								break;										
										
							case "proper" :
								if (targetI == element)
									ydrClientScripts.elementValueUpdate(targetI, value.charAt(0).toUpperCase() + value.substr(1));
								break;							
						
							case "range" :
								if (value.length>0)
								{
									var lower = ydrUtils.exclaimEval(validation.getAttribute("lower"));
									var upper = ydrUtils.exclaimEval(validation.getAttribute("upper"));
									var val;
									if (format == "date")
									{
										if (lower.constructor != ydrDate)
											lower = new ydrDate(lower);
										lower = lower.jsDate.valueOf();
										
										if (upper.constructor != ydrDate)
											upper = new ydrDate(upper);
										upper = upper.jsDate.valueOf();
										
										val=ydrDateStatic.user(value).jsDate.valueOf();
									}
									else
										val=value;							

									if (val<lower || val > upper || isNaN(val))
									{
										val = Math.min(Math.max(val, lower),upper);
										
										/* this forced the value to be in range - but this tended to hide to the user what the user had entered wrongly
										if (format == "date")
										{
											ydrDateStatic.jsDate = new Date(val);
											targetI.setAttribute("value", ydrDateStatic.user());
										}
										else
											targetI.getAttribute("value", val);
										*/
										
										if (format == "date")
										{
											lower = new ydrDate(lower).user();
											upper = new ydrDate(upper).user();
										};										
										
										valMessage += "<p class='error'><span class='object'>"
											+ m_this.firstPeerTD(targetI).innerHTML 
											+"</span> permitted range of values is <span class='emph'>"+lower+"</span>..<span class='emph'>"+upper+"</span></p>";
									};
								};
								break;
								
							case "replace" :
								if (value.search(validation.getAttribute("value"))>=0)
									ydrClientScripts.elementValueUpdate(targetI
										, value.replace(new RegExp(validation.getAttribute("value"), "g"), validation.getAttribute("becomes")) );
									
								break;			
								
							case "script" :
								if (validation.getAttribute("name") == "clientSide")
								{								
									try
									{
										var script =  eval("script="+validation.getAttribute("value"));
										if (script(targetI) != true)
										{
											valMessage +="<p class='error'><span class='object'>"
												+ m_this.firstPeerTD(targetI).innerHTML 
												+"</span>: " + validation.getAttribute("message") +"</p>";
										};										
									}
									catch (e) 
									{ valMessage +=
										"<p class='developer'><span class='object'>"
											+ m_this.firstPeerTD(targetI).innerHTML 
											+"</span> script failed: " + ydrClientScripts.HTMLencode(e.message)+"</p>";
									};	
								};
								break;							

							case "trim" :
								if (value.length>0)
									ydrClientScripts.elementValueUpdate(targetI, value.trim());
								break; 
							
							case "unique" :
								//handled in ydrExclaimListClient.validate() and serverside in ydrData.updater()
								break;
								
							case "upper" :
							case "lower" :
								if (value.length>0)
								{
									var val;
									var limit = ydrUtils.exclaimEval(validation.getAttribute("limit"));
									if (format == "date")
									{
										if (limit.constructor != ydrDate)
											limit = new ydrDate(limit);
										limit = limit.jsDate.valueOf();
										val=ydrDateStatic.user(value).jsDate.valueOf();
									}
									else
										val=value;							
									
									if ((action == "upper" && val > limit )
									 || (action == "lower" && val < limit )
									 || isNaN(val) )
									{
										
										if (format == "date")
										{
											ydrDateStatic.jsDate = new Date(limit)
											limit = ydrDateStatic.user();
										};
										/*else
											targetI.setAttribute("value", limit);
										*/
											
										valMessage += "<p class='error'><span class='object'>"
											+ m_this.firstPeerTD(targetI).innerHTML 
											+"</span> "+ validation.getAttribute("action")+" permitted value is <span class='emph'>"+limit+"</span></p>";
									};
								};
								break;
							
							case "upperCase" :
								if (targetI == element)
									ydrClientScripts.elementValueUpdate(targetI, value.toUpperCase()) ;
								break;
											
							case "url" :
								// firstly standardise the url and defeat any attempts at relative addresses
								var protocols = validation.getAttribute("protocols");
								if (protocols==null)
									protocols ="'http'https'";
											
								if (value.length>0)
								{
									var myValue=value.trim().replace(/ /g,"%20").split(/:/); 								
									myValue[0] = myValue[0].toLowerCase();
									
									if (myValue[1] ==null)
									{
										myValue[1]="//"+myValue[0];
										myValue[0]="http";
									}
									else
									{	
										if (protocols.indexOf("'"+myValue[0]+"'")<0)
											myValue[0]="http";
									};
									
									if (myValue[0].substr(0,4) == "http")
										while (myValue[1].substr(0,2) != "//")
											myValue[1]="/"+myValue[1];
										
									ydrClientScripts.elementValueUpdate(targetI, myValue.join(":"));
														
									if (myValue[1].length<5)
										valMessage += "<p class='error'><span class='object'>"
												+ m_this.firstPeerTD(targetI).innerHTML 
												+"</span> the URL address is incomplete</p>"
									else
									{	
										switch (myValue[0])
											{
												case "http" :
												case "https" :							
													var temp=recentlyTriedUrls[value]+""; // ie scripting bug - cannot use value directly
													switch (temp)
													{
														case "undefined" :
														case "null" :
														case "aborted" :
															valMessage += "<p class='warning'><span class='object'>"
																+ m_this.firstPeerTD(targetI).innerHTML 
																+"</span> checking URL</p>";
																// need to check if another one
															
															if (currentlyTryingURL != value)
															{	
																if (currentlyTryingURL != null)
																	recentlyTriedUrls[currentlyTryingURL] = "aborted";
																
																currentlyTryingURL = value;	
																recentlyTriedUrls[currentlyTryingURL] ="waiting";
																
																currentlyTryingRequest = ydrClientScripts.XMLHttpRequest();
																
																currentlyTryingRequest.onreadystatechange=
																	function urlCheck()
																	{
																		if (this.readyState == 4)
																		{
																			recentlyTriedUrls[currentlyTryingURL] = (this.statusText == "OK") ? "present" : "absent";
																			currentlyTryingRequest=null;
																			currentlyTryingURL=null;	
																		};													
																	};
																
																try { // can fail if the client does not allow pages to access the information
																	currentlyTryingRequest.open("GET", value);	
																	currentlyTryingRequest.send();
																} catch (e)
																{
																	recentlyTriedUrls[currentlyTryingURL]="absent";
																	currentlyTryingURL = null;
																	currentlyTryingRequest = null;
																	
																	if (e.number != -2147024891 /*access is denied */)
																		valMessage += "<p class='error'><span class='object'>"
																			+ m_this.firstPeerTD(targetI).innerHTML 
																			+"</span> the URL cannot be accessed</p>"
																	else
																		recentlyTriedUrls[currentlyTryingURL] ="present";
																};
															};
																	
															break;
														
														case "waiting" :
															valMessage += "<p class='warning'><span class='object'>"
																+ m_this.firstPeerTD(targetI).innerHTML 
																+"</span> checking URL</p>";
															
														case "present" :
															// yipee
															break
															
														case "absent" :
															valMessage += "<p class='error'><span class='object'>"
																+ m_this.firstPeerTD(targetI).innerHTML 
																+"</span> the URL cannot be accessed</p>";
															
															break; 
															
														default :
															window.alert("ydrPop.validate[url]: invalid state item 1");
													}
												
													break;
													
												case "callto" :
													if (myValue[1].search(/^\+[1-9][0-9\- \*\#]*$/i) <0)
													{
														valMessage += "<p class='error'><span class='object'>"
															+ m_this.firstPeerTD(targetI).innerHTML
															+"</span> Must be a valid telephone number in international format starting <span class='emph'>+</span></p>";
													}			
													break;
													
												case "mailto" :
													if (myValue[1].search(/^[\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[a-z]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$/i) <0)
													{
														valMessage += "<p class='error'><span class='object'>"
															+ m_this.firstPeerTD(targetI).innerHTML
															+"</span> Must be a valid email address</p>";
													}			
													break;
													
												default :
													valMessage += "<p class='error'><span class='object'>"
														+ m_this.firstPeerTD(targetI).innerHTML 
														+"</span> invalid protocol </p>";
											}
										};
									};
									
									break; 
								
								break; 
							
							case "xmlClean" :
								if (targetI == element)
									ydrClientScripts.elementValueUpdate(targetI, value.replace(/'/g,"").replace(/</g,"").replace(/>/g,""));
								break;
							
							case "xpath" :
								//handled in ydrExclaimListClient.validate() and serverside in ydrData.updater()
								break;
					
								
							default :
								window.alert("unrecognised validation action " + validation.getAttribute("action") + " in " + targetI.id);
						};
					};
				};
			};
		};
		//stop();
		// if we also have clientlist loaded then see if it wants to do anything for this element
		try { valMessage += ydrExclaimListClient.validate(element) ; }
		catch (e) {};
//stop();
		if (m_onValidate != undefined)
			valMessage += m_onValidate(element);
					
		m_this.advisory(valMessage);
		
		var result = (valMessage.length== 0);
		
		// set the positive valued buttons to enabled or disabled according to if there is an error
		// negative ones (= go back, cancel, etc are always enabled
		/*
		var buttons= document.getElementById("YDRpopupButtons").getElementsByTagName("input");		
		
		for (var i=0; i<buttons.length; i++)
		{
			with (buttons[i])
			{
				if (value>=0)
					disabled = ! result;
			};
		};
		*/
	
		return result;
	};
	
	// if this popup is overlaid visbily with another popup, we wont want this popups
	// error messages poking around the edges - for it can be confusing on the screen
	// the called app has to set this for every validate - it is always reset at the start of 
	// each validate
	this.advisoryHide = function()
	{
		m_popupErrors.style.display='none';
	};
	
	// sets or returns the advisory string
	this.advisory = function(innerHTML)
	{
		var result;
		
		if (innerHTML == undefined)
			result = m_popupErrors.innerHTML
		else	
			m_popupErrors.innerHTML =innerHTML;
			
		return result;
		
		/*debug*/
		ydrClientScripts.xhtml(document.body);
		//ydrClientPane.intervalReset();
	};
	
	function dispatchData()
	{
		// important - this changes the values of national language dependant fields in to international standard values
		// hence it MUST ONLT BE CALLED once before data is sent
		// the result is such that if the data is .reshow[n] then the first pass of validate will turn the values back to local values
		// must be aware of this if your are going to use the values locally rather than posting them to a web server
		// this works because validate is programmed to accept initial values from the loaded web page in international format
		var targetI;
		var format;
		
		if (m_activeElements == null) return;
		
		for (var i=0; i<m_activeElements.length; i++)
		{
			targetI = document.getElementById(m_activeElements[i].getAttribute("id"));
			format = targetI.getAttribute("ydr:fmt");
			try
			{
				switch (format)
				{
					case "date" :
						value = ydrBrowser.html.elementValue(targetI)	;		
						if (value.length>0)
							ydrClientScripts.elementValueUpdate(targetI, ydrDateStatic.user(value).xml()/*jsDate.toUTCString()*/);
					break;
				};
			}
			catch (e)
			{ window.alert("ydrClientScripts.ydrPopup["+m_this.instanceName+".hide():failed for "
				+ m_this.firstPeerTD(targetI).innerHTML 
				+"\n\n"+e.message)
			};
		};
	}
	
	// makes the popup go away and reshows the origin
	this.hide = function(cascade)
	{
		if (m_popupElement && (m_popupElement.style.visibility!='hidden' || cascade))
		{
			try { dispatchData() } catch (e) {};
			
			ydrClientPane.currentPanePop();
			try 
			{	
				m_popupElement.style.visibility='hidden';
				if (m_mainElement != undefined)
					m_mainElement.style.visibility='inherit';
					
				if (m_zoneToLeft)
					m_zoneToLeft.element.style.visibility = m_zoneToLeft.visibility;				
			} catch (e) {};
		
			if (m_this.parentPopup && cascade == "cascade")
				m_this.parentPopup.hide(cascade)
			else if (cascade == "cascade" || cascade == "redo")
			{
				if (typeof ydrClientPane.lastSelectedItem == "string")
					eval(ydrClientPane.lastSelectedItem);
				else
					ydrBrowser.html.elementClick(ydrClientPane.lastSelectedItem);
			}
			else
				ydrClientPane.jiggle();
		};
	}
	
	// if intermediateURL is present then optionally nextURL tells us where to navigate to if all is OK
	this.nextURL = function()
	{
		var result;
		//stop();
		try 
		{
			with (m_popupElement.firstChild.firstChild.lastChild.firstChild.firstChild) // should be the form element if it exists
			{
				if (nodeName.toLowerCase() == "form" 
					&& getAttribute("method").substr(0,3) == "ydr"
					&& getAttribute("ydr:nexturl") == "%page%"
					)
						return  ydrClientPane.currentPage();
			};
		}catch (e)	{};
		
		if (result == null)
		{
			if (m_lastMenuSelectedItem)
			{
				// if m_lastMenuSelectedItem is present then we dont honour this request at all - we reinvoke the last menu click
				return m_lastMenuSelectedItem;
			}
			else
				return window.location.href
		}
		else
			return result;
	}
	
	this.intermediateURL = function()
	{
		// if the popup is an HTML form it returns the url IF the action is ours to comply with
		// see also in nodewalk for security
		try 
		{
			var formElement = m_popupElement.getElementsByTagName("form")[0];
			
			//window.alert(formElement.cloneNode(false).outerHTML+"\n\n"+formElement.getAttribute("action"));
			if (formElement.nodeName.toLowerCase() == "form" && formElement.getAttribute("method").substr(0,3) == "ydr")
				return formElement.getAttribute("action")
			else
				return null;				
		
		}catch (e) {return null};
	}
		
	// when the form has an action button created via <input type='submit' onclick='return ydrPopup.closeSubmit(this)'
	// the event is added automatically via this.adopt to each submit button and returns false if the form is not ready to be sent
	// if there is already an onexit event then it is fired
	//if "this" is omitted from the call then no action is taken and no onexit event occurs - if you still want it then preceed the "return" with a call to it
	// note the onexit gets the element not the index as for this.close
	// the form is not .reshow(able) if true is returned as it is presumed that we now navigate to where the form method said
	// note in this case the m_onExit gets the element clicked and not the index of the chosen option as by default
	this.closeSubmit = function(element)
	{
		var result=false;
		m_this.validate(null,"final");
		
		if (element == undefined) 
			result = true
		else if (m_popupErrors.innerHTML.length == 0)
		{	
			// if m_onexit is present then we invoked it - if it returns true we dont hide the form but leave it present with the actions showing again
			if (m_onExit == undefined)
				result = true
			else
				result = m_onExit(element , m_popupElement) == true; // we do the = true to capture lax events that return nothing
		};
		
		if (result) 
		{
			dispatchData();
			try {m_popupButtons.innerHTML = "Please wait"}	catch (e) {};
			if (element != undefined)
				try { element.style.disabled='disabled' } catch (e) {};
			try {ydrExclaimListClient.exit()} catch (e) {};
		}
		else
			window.alert ("Please fix the validation errors that are listed at the bottom of the page");
		
		return result;
	}
	
	// when the form has an action button we created 
	this.close = function(index)
	{
		// dont validate the abort buttons like cancel which have values less than 0
		//stop();
		// can be called from outside popup to externally force an action on the window
		// in this case 
		if (typeof index == "number" && index<0 )
		{
			if (index == m_this.restoreIndex) 
				m_this.restore()
			else
			{		
				// these are the other cancel type buttons
				m_this.hide()
				if (m_onExit != undefined && typeof m_onExit != "string")
					m_onExit(index, m_popupElement) ;
			};
		}
		else
		{	
			m_this.validate(null, "final");
			// these are the perform type buttons
			if (m_popupErrors.innerHTML.length == 0)
			{	
				// if m_onexit is present then we invoke it - if it returns true we dont hide the form but leave it present with the actions showing again
				if (m_onExit != undefined)
				{
					if (typeof index == "number" && typeof m_onExit == "string")
						window.location.href=m_onExit
					else 
					{
						if (typeof index == "number")
							index = m_onExit(index, m_popupElement, this);
							
						switch (index)
						{ 
							// leave everything alone - the caller has an event that wll fire later (eg asynch http request) and clear up
							// user cannot press buttons again until the caller's event
							case true:
								m_popupButtons.innerHTML = "Please wait";
								break;
						
							// changes rejected - just stay where we are 
							// user can press buttons again
							case false :
								break;
							
							// go back to a new version of the original page 
							case "basePage" :
								window.location.href = window.location.href;
								break;
							
							// hide without navigation - restores previous pane
							case "resume" :
								this.hide();
								break;
								
							case "redo" :
							// asks for a remake of the page/pane that had created this dialog
								this.hide("redo");								
								break;
								
							// normally null - get rid of me and if there is a navigator in nextURL - then perform it
							default :
								var nextURL = this.nextURL();
								this.hide();
								
								if (typeof nextURL =="string")
								{	
									if (nextURL.indexOf("javascript:") == 0)
										eval(nextURL.substr(11))
									else
										window.location.href = nextURL;
								};
						};
					};
				}
				else
					this.hide();	
			}
			else
			{
				window.alert ("Please fix the validation errors that are listed at the bottom of the page");
			};
		};
	};

	this.showRemote = function(url)
	{
		//initiates a show() using the details given by the url - response is an xml object with attributes the names of the parameters
			//stop();
		with (ydrClientScripts.XMLHttpRequest())
		{
			open("GET", url.standardMarkups(), false);
			send (null);

			var myResponse = responseXML;
			try 
			{		
				// trap internet explorer bug - sometimes fails  to load XML
				if (myResponse.documentElement == null)
					myResponse = ydrBrowser.xml.document(responseText);
				
				if (myResponse.documentElement == null)
				{
					if (ydrCredentials.isAdministrator()) stop();
					myResponse = ydrBrowser.xml.document(responseText.replace(/ /, " %ydrNamespace% "));
				};
						
				with (myResponse.documentElement)
				{
					if (nodeName != "ydrPopup")
						this.show(ydrBrowser.xml.xml(myResponse.documentElement).standardMarkups())
					else
					{
						var onExit;				try {	eval("onExit =" + getAttribute("onExit")) } 
													catch (e) 
													{window.alert("Script for onExit failed to load\n\n"+e.message)};
													if ( getAttribute("onExit") && ! onExit) 
														window.alert("Script for onExit failed to assign function");
						var onValidate;		try {	eval("onValidate =" + getAttribute("onValidate")) } 
													catch (e) 
													{window.alert("Script for onValidate failed to load\n\n" + e.message)};
													if ( getAttribute("onValidate") && ! onValidate)
														window.alert("Script for onValidate failed to assign function");
								
						var buttons;			if (getAttribute("buttons")) buttons = new ydrGenericScripts.gString(getAttribute("buttons"),"^");
						
						var mainElement;	
						if (getAttribute("mainElement"))
							mainElement = document.getElementById(getAttribute("mainElement")) ;
						
						var popupElement;	
						if (getAttribute("popupElement"))
							popupElement = document.getElementById(getAttribute("popupElement"));
						
						//  show(message, title, size, onExit, onValidate, buttons, mainElement, popupElement, activeElement)
						var message = getAttribute("message");
				
						with (ydrBrowser.xml.document(message.standardMarkups()))
						{
							if (ydrBrowser.xml.parseError(documentElement.ownerDocument))	
							{
								// it may not matter but should be resolved
								if (gc_rights.indexOf('administrator')>=0)
									throw new Error("<div class='stop'><p class='stop'>Invalid XML in ydrPopup.showRemote[message]</p><p class='code'>"
										+ ydrBrowser.xml.parseError(documentElement.ownerDocument) +"</p></div>");
							}
							else
							{
								ydrClientPane.scriptNode(documentElement);
								message = ydrBrowser.xml.xml(documentElement);								
							};
						};

						this.show(message.standardMarkups(), getAttribute("title")
							, getAttribute("size"), onExit, onValidate, buttons, mainElement, popupElement);
					};
				};
			} catch (e)
			{
				if (e.message.indexOf("<div class='stop'>")>=0 )
					this.show(e.message)
				else if (responseText.indexOf("<ydrPopup")==0)
					window.alert("Unable to render popup [1]\n\n" + e.message) 
				else
					try { this.show(responseText.standardMarkups()) }
					catch (e) { window.alert("Unable to render popup [2]\n\n" +e.message) };
			};
		};
	};
	 
	this.cookie =  function(mainElement, popupElement, title)
	{
		m_this.show( "<p><span class='object'>Cookie:</span> "
			+"A small file that is held on your computer that enables a web server to recognise "
			+"who you are each time you visit another page of the web site.</p><p>  For this web "
			+"site the cookie contains a session identifier that correlates to your details held on the web server."
			+"&#160; It is created when you log on and removed when you log off."
			+"&#160; This avoids you having to log in each time you access this site via the Internet, or if your session times out and has to be restarted.</p>",title, "medium");
	}
	
	// components to allow the  ydr text editor to run as a nested popup
	// this is invoked solely by the validation below in a textarea html	
		// ydr:validation="&lt;validation action='formattable'&gt;" 
	//var requestHTTP;
	var ydrClientEdit_inMe=false;
	var ydrClientEdit_popupElement;
	var ydrClientEdit_callerButton;
	var ydrClientEdit_targetTextArea;
	var ydrClientEdit_html;
	var ydrClientEdit_initialised; 
	// put a copy of the editor in the overlay area ydrClientEdit_popupElement
	/*
	function cei_response() 
	{
		if (requestHTTP.readyState == 4)
		{			
			// if we did not get an OK message then reshow the form with the response as the validation message
			ydrClientEdit_popup =  new ydrPopupCreator("ydrClientEdit_popup")
			ydrClientEdit_popupElement = document.getElementById("ydrPopupEditor")
			
			if (ydrClientEdit_popupElement == undefined)
			{
				ydrClientEdit_popupElement = document.createElement("div");
				ydrClientEdit_popupElement.style.cssText="position:absolute";
				ydrClientEdit_popupElement.setAttribute("id",'ydrPopupEditor');
				m_popupElement.parentNode.appendChild(ydrClientEdit_popupElement);
			};
			
			ydrClientEdit_html = requestHTTP.responseText.standardMarkups();
			
			editorStart();			
		};
	};
	*/
	
	function editorStart()
	{	
		ydrClientEdit_initialised = false;
		ydrClientEdit_popup.parentPopup = m_this;
		m_popupEditorContainer.style.display="block";
		ydrClientEdit_popup.show(ydrClientEdit_html
			, "<h2 style='text-align:center'>Editing <span class='emph'>"
				+m_this.firstPeerTD(ydrClientEdit_targetTextArea).innerHTML 
				+"</span> in <span style='font-size:smaller'>"+m_title+"</span></h2>"
			, "width:100%; height:100%", editorEnd, editorValidate
			, "'0^Save'-1^Cancel'"
			, m_popupElement, ydrClientEdit_popupElement, "ydrTextEdit")
	};
	
	function editorValidate(element)
	{
		var i;
		
		if (! ydrClientEdit_initialised)
		{
			ydrClientEdit_initialised = true;
			ydrClientTextEdit.init(ydrClientEdit_targetTextArea, document.getElementById('ydrTextEditTarget')
				, "<link type=\"text/css\" href=\""+rootDir+"/ydr/ydr.css\" rel=\"stylesheet\">"  );		
			
			ydrClientTextEdit.editDone();	
			ydrClientEdit_popup.advisory("");	
		};
	
		return ydrClientTextEdit.validate(element);
	}
	
	function editorEnd(index, m_popupElement)
	{
		var xhtml;
	//stop();
		m_popupEditorContainer.style.display="none";
		if (index>=0)
		{
			ydrBrowser.html.elementValue(ydrClientEdit_targetTextArea,  ydrClientTextEdit.result());
			
			// once text editted then cannot be seen in plane text
			if (ydrClientEdit_targetTextArea.previousSibling == null || ydrClientEdit_targetTextArea.previousSibling.nodeName.toLowerCase() != "iframe")
			{
				ydrClientEdit_targetTextArea.style.visibility = "hidden";
				with (ydrClientEdit_targetTextArea.parentNode.insertBefore(document.createElement("iframe"), ydrClientEdit_targetTextArea))
				{
					style.position ="absolute";
					style.overflow = "auto";
					style.fontSize = ydrBrowser.html.currentStyle(ydrClientEdit_targetTextArea, "fontSize");
					style.width = ydrClientEdit_targetTextArea.style.width;
					style.height = ydrClientEdit_targetTextArea.style.height;
					
					ydrClientEdit_callerButton.title =  "This is a formatted document, please use the editor to amend it";
				};
			}
			
			with (ydrClientEdit_targetTextArea.previousSibling.contentWindow.document)
			{
				open();
				write(ydrBrowser.html.elementValue(ydrClientEdit_targetTextArea));
				close();
			}
		}
		else
			ydrClientTextEdit.finish();
		
		ydrClientEdit_callerButton.innerHTML="Use<br />Editor";	
		ydrClientEdit_inMe =false;
		
		return "resume"
	};
	
	this.ydrClientEditInitiator = function(callerButton, targetTextArea, title)
	{
		if (ydrClientEdit_inMe)
			window.alert ("I have not yet retrieved the editor code to insert in this page.\n\nPlease wait")
		else
		{
			// get the popup editor if it not already loaded
			{
				m_popupEditorContainer = document.getElementById("ydrPopupEditorContainer");
				
				if (m_popupEditorContainer && m_popupEditorContainer.firstChild == null)
					m_popupEditorContainer.innerHTML = ydrClientScripts.urlFetchString("%rootDir%/ydr/ydrClientTextPopupBuilt.htm");
			};
		
			if (! ydrClientScripts.isIE)
			{
				window.alert("We are sorry that your browser '" + window.navigator.appVersion  +"' is not yet supported for formatted editting.\n\nPlease use Internet Explorer 7 or 8 or accept that some of the functionality in this editor is not yet fully operational with your browser.  Some features may not perform as expected.")
			};

			// for backwards compatibility only
			try {document.getElementById("ydrPopupEditorSource").innerHTML=title;} catch (e) {};
			
			ydrClientEdit_inMe =true;
			
			ydrClientEdit_callerButton=callerButton;
			ydrClientEdit_targetTextArea=targetTextArea;
			
			ydrBrowser.html.text(callerButton, "Please wait");	
			
			if (ydrClientEdit_popup )
				editorStart()
			else if (document.getElementById("ydrPopupEditor") == null)
			{
				window.alert("developer - element ydrPopupEditor is missing from container document.  Consider placing"
					+"\n\n<div id='ydrPopupEditorContainer'></div>	"
					+"\n\n after "
					+"\n\n<%= menu.boilerPlate(\"content\")) %>");
				/*
				//TODO consider scrapping this next and the associauted cei_response
				// no cached version of the editor built into the document - developer phase only)
				requestHTTP=ydrClientScripts.XMLHttpRequest();
				requestHTTP.onreadystatechange=cei_response;
				requestHTTP.open("GET",rootDir+"/ydr/ydrClientTextPopupBuilt.htm");	
				//requestHTTP.setRequestHeader("Cache-Control", "no-cache"); // remove when stable
				requestHTTP.send();		
				*/
			}
			else
			{
				// note this leaves ydrClientEdit_html null causing the popup show to retrieve the prebuilt (ie non-dynamic page)

				if (ydrClientEdit_popup == null)
				{
					ydrClientEdit_popup =  new ydrPopupCreator("ydrClientEdit_popup")
					ydrClientEdit_popupElement = document.getElementById("ydrPopupEditor")
				};
				editorStart();
			};
			ydrClientPane.jiggle()// cos I keep disappearing			
		};
	}
	
	this.activeElements = function() { return m_activeElements };
	
	// withnulls is boolean true if null values included in list as "" values
	// with grid is string present if grid expected from ydrExclaimListClient = implies withNulls
	this.nodeWalk = function(withNulls, withGrid ) 
	{ 
		withGrid = withGrid == "withGrid";
		
		var result=ydrClientScripts.nodeWalker(m_activeElement, withNulls || withGrid);
		if (withGrid)
		{	
			try{ydrExclaimListClient.valueList(result) } catch (e) {};			
		};
		
		// if a security code was provided with the form it must be returned with the data
		// this stops manual fiddling because even if someone copies this code, it is valid only for a short time and only for
		// the current server sesssion, and is a once only use item, and only for the responder for the specific asp page that issued it
		
		// note that dialogs dont have to have forms and forms dont have to have security code - but server issued forms that cause
		// RDBMS updates really ought to do so - note this is automatic via eg ydrCredentials.asp and ydrExclaimClientList.asp 
		
		// furthermore attempts to bust this code are noted on the server and the user+IP address are automatically blacklisted
		
		// this is pretty unbustable but if someone wants to spend for ever then they can work out a way 
		// the value of the security code is not required to be kept secret

		try 
		{
			with (m_popupElement.getElementsByTagName("form")[0]) // should be the form element if it exists
			{
				if (nodeName.toLowerCase() == "form" && getAttribute("method").substr(0,3) == "ydr")
					result.setAttribute("_security", getAttribute("ydr:security"));				
			};
		}catch (e) {};
		
		return result;
	};
}
var ydrPopup = new ydrPopupCreator("ydrPopup");
var ydrClientEdit_popup; // if used then a surrogate client editor popup



