//START OF COMMON CODE WITH SERVER SCRIPTS

// this file contains java that is common to client and server side scripts
// for client side it is included in ydrClientScripts.js
// for server side it is included in ydrServerScripts.asp

// the code is to be kept IDENTICAL with the client side being editted and the
// server side copied save that the server side is encapsulated in < % % >

// the method and the function trim remove whitespace from the front and end of a string 
// the function trim also tolerates an undefined variable

// implements three static objects (see each one for more details
	// ydrClientScripts()
	// ydrClientScriptsExclaim()
	// ydrPopup()


function ydrAllCountValidate(id, property, expectedValue, minCount, maxCount)
{
	var foundAll = document.all(id);
	property=property.toLowerCase();

	var encountered=0;
	
	if (foundAll != undefined)
	{
		if (foundAll.length == undefined)
		{
			switch (property)
			{
				case "checked":
					if (minCount == 1)
					{
						foundAll.checked= expectedValue;
						encountered = 1;
					}
					else
						encountered = (foundAll.checked == expectedValue ? 1 : 0);
						
					break;
			};
		}
		else
		{	
			for (var i=0; i<foundAll.length; i++)
			{
				switch (property)
				{
					case "checked":
						if (foundAll(i).checked == expectedValue)
							encountered++ ;
							
						break;
				};
			};
		};
	}
	else
	{
		return "Error in Page: Cannot validate object '"+property+"'";
	};
		
	if (encountered < minCount)
		return "\n\nError in page data: field '"+id+"' must have at least " + minCount
			+ " members with '"+property+"' set to '"+expectedValue+"'.";
	else if (encountered > maxCount)
		return "\n\nError in page data: field '"+id+"' must have at most " + maxCount
			+ " members with '"+property+"' set to '"+expectedValue+"'.";
	else
		return "";
}

// early exclaim type validation of a data field
function ydrValidate(object, fmt, validation)
{
	var errorMessage="";
	var value;
	
	switch (fmt)
	{
		case "ft0decimals":
			value=ydrBrowser.html.elementValue(object).trim();
							
			if (value != undefined)
				if (value != parseInt(ydrBrowser.html.elementValue(object)))
				{
					errorMessage += "\nValue is expected to be an integer";
					value = parseInt(ydrBrowser.html.elementValue(object)).toString();
					if (typeof(value) == "number")
						ydrBrowser.html.elementValue(object, value);
					else
						ydrBrowser.html.elementValue(object, "0");
				};
			break;
			
	}
	
	if (errorMessage.length >0)
	{
		ydrPopup.show ("Error in data content for "+object.id+" = '"+value+"'.\n"+errorMessage);
		return false;
	}
	else
		return true;
}

// static object serving client scripting functions
// fullURLpath - turns a local relative path to a full relative path for the document location
// nodeWalker(node) - takes an XHTML document element and returns an XML document that contains updates (per ydrData) for each input and text area element found therein
// keyPressIs(event) // returns a javascript character for a keypress event - handles browser differences
// XMLHttpRequest - gets the best/nearest HTTP get object
// XMLdocument([textXML]) if textXML is specified then the XML document is opened with that text otherwise you will
	//need to use.load(url) to get a document - does not support opening a document with no rootnode
// once(clickelement,messageelement)
// isHidden(htmlElement) // returns true if the current or an inherited parent node has visibility hidden
//stop();
var ydrClientScripts = new function ()
{
	// very simple xpath over a list made by listMaker()

	// returns the top document element not the ownerdoc which applies only to Microsoft!
	var m_this=this;
	var expires;
	
	function expiresRestart() 	{ this.expires = new ydrDate().changeBy(28, "minutes").valueOf(); };
	expiresRestart();	
	
	this.fetchX = function(anchorElement, preamble, target, targetContainer, returnPoint) 
	{
		var result = this.fetch(anchorElement, preamble, target, targetContainer, "fetchX", returnPoint);
		return result;
	}
	//stop();
	this.fetch = function(anchorElement, preamble, target, targetContainer, fetchX, returnPoint) 
	{
		// sets the default pane to remote fetched content	
		// anchor element in a menu creating this - is expected to have an ID that identifies the file ending .htm if ending not given
		// or may simply be the name of the source file relative to where we are
		// preamble is the preable added to the id by the menu system and to be removed from the source name - or null
		// target is where the result is to be placed (string or element)
		// target container is the whole pane (not whole page) that contains the data being fetched, can be same as target		
		// if target is missing is assumed to be same as preamble (and preamble cannot be null in that case)
		// returnPoint marks this as a superior return point than the default one set by menuclient click
		// so that if a child pane decides to return it returns here rather than to the last menu item
		
		//stop();
		
		if (returnPoint)
		{
			if (typeof anchorElement == "string")
				ydrClientPane.lastSelectedItem = "ydrClientScripts."
					+ ((fetchX == "fetchX") ? "fetchX" : "fetch")
					+"(\""+anchorElement +"\")"
			else
				ydrClientPane.lastSelectedItem = anchorElement;
		};
		
		if (typeof target == "string")
			target = document.getElementById(target);
			
		if (target == null)
			try { target = document.getElementById(browseClient.overlayArea); } catch (e) {};
		if (target == null)
			try { target = document.getElementById(ydrClientPane.overlayArea); } catch (e) {};
		
		if (targetContainer == null)
			targetContainer = 'ydrPopupMain';		
		if (typeof targetContainer == "string")
			targetContainer = document.getElementById(targetContainer);
		if (targetContainer == null)
			targetContainer = target;

		if (typeof anchorElement != "string")
		{
			source=anchorElement.getAttribute("id")
			if (preamble)
				source = source .replace(preamble, "");
		}
		else if (anchorElement.indexOf("menu:") == 0)
			return ydrBrowser.html.elementClick(anchorElement.substr(5))
		else
			source = anchorElement;
			
		if (target == null)
			window.alert ("ydrClientScripts.fetch - unable to locate target where\n\n" + source + "\n\n is to be placed");
		else
		{			
			// make sure anchorElement has .htm ending unless it has a recognised other one
			if (source.indexOf(".asp")<0)
			{
				source = source.split(".");   // note using reg expr /\./ fails for some strings
				//if  (source.length == 1)
				//	source = source[0] + ".htm"
				//else
				{
					switch (source[source.length-1].substr(0,3).toLowerCase())
					{ 
						case "htm" :
						case "asp" : 
							// fine as is 
							break;
							
						default :
							source[source.length-1] += ".htm";
					};
					source = source.join(".");
				};
			};
			
			ydrClientPane.paneExpand(
				targetContainer
				, target
				, "base"
				, ((fetchX == "fetchX")
					? ydrClientScripts.urlFetchXML(source)
					: ydrClientScripts.urlFetchString(source)
					)
				);
		};
		
		return false;
	};
	
	this.elementValueUpdate = function(element, newValue)
	{
		//TODO manage cursor position
		if (newValue)
		{
			if (element)
				ydrBrowser.html.elementValue(element, newValue) 
		}
		else
		{
			if (element)
				element.removeAttribute("value");
		};		
	}
	
	this.urlFetchString = function(url)
	{	
		chronInitiator.chronTrigger ("urlFetchString: "+url);
		
		with (this.XMLHttpRequest())
		{
			open("GET", url.standardMarkups(), false);
			send(null);
			return responseText.standardMarkups();
		};
	};
	
	String.prototype.standardMarkups = function()
	{
		var result = this;
		if (result.indexOf("%currentPage%")>=0)
			result =  result.replace(/%currentPage%/g, escape(ydrClientPane.currentPage()));
		
		return result
			.replace(/%print%/g, "<img style='float:right' onclick='ydrClientPane.printFragment(this.parentNode)'  src='%rootDir%/ydr/images-f/printer.png'  alt='print' />")
			.replace(/%userState%/g,userState)
			.replace(/%emailState%/g, emailState)
			.replace(/%browserCheckIssued%/g, browserCheckIssued)
			.replace(/%siteName%/g, siteName)
			.replace(/%ydrNamespace%/g, ydrNamespace)		
			.replace(/%newWindow%/g,"&#160; <img class='leftPlainIcon' src='%rootDir%/ydr/images-f/arrow_branch.png' alt='starts in a new window' />")
			.replace(/%rights%/g, gc_rights)	
			.replace(/%rootDir%/g,rootDir)	
			
			;
	}
	
	this.urlFetchXML= function(url)
	{	
		with (this.XMLHttpRequest())
		{
			open("GET", url.standardMarkups(), false);
			send(null);
			
			var xml = responseXML;
			
			// now this real silly fiddle is a response some divs that arrive straight from file and ndont have pre
			// processed standardmarkups
			{
				if (xml == null || xml.firstChild == null)
					xml = ydrBrowser.xml.document(responseText.standardMarkups()); 
			};
		
			try 
			{
				if (ydrBrowser.xml.parseError(xml))
					 xml = ydrBrowser.xml.document("<div><p class='stop'>Unable to load pane as xml: " 
						+ url 
						+"</p><p class='code'>" 
						+ydrClientScripts.HTMLencode(xml.parseError.reason)+"</p></div>");
			} catch (e) {xml =  ydrBrowser.xml.document("<p class='developer'>Unable to load frame as xml: " + url +"</p>")};
					
			chronInitiator.chronTrigger ("urlFetchXML: "+url);
			return xml;
		};
	};
	
	this.XMLdocument = function(textXML, filename) 	{	return ydrBrowser.xml.document(textXML, filename) 	}
	this.isIE = window.navigator.appName == "Microsoft Internet Explorer";

	this.XMLHttpRequest=function ()
	{	 
		if (expires < new Date().valueOf())
		{			
			if (userState == 'normal')
			{
				with (getRequestor())
				{
					open("GET", (rootDir+"/ydr/ydrreconnect.asp?reconnection="+gc_reconnection).standardMarkups(), false);
					send(null);
					
					var xml = responseXML;

					// use only for debugging window.alert(xml.xml);
					
					if (xml.documentElement.getAttribute("class")  != "OK")
					{
						// warning - without this there is a high risk that the call to the server may be treated as invalid and lead the user to be barred
						window.alert("Sorry, your server session has timed out.\n\nI will try and reset your page, but"
							+" you may get an error reported while I do this.\n\n After this, under some circumstances"
							+", you may have to log on again.");
						window.location.href = window.location.href;
						//throw new Error("session timeout");	
					};
				};
			};
		};
		
	   expiresRestart();
	   return getRequestor();
	};
   
   function getRequestor()
   {
		var request;
		try
	    {
			if (window.XMLHttpRequest)
			{
				//Mozilla-based browsers
				request = new window.XMLHttpRequest();
			} 
			else if (window.ActiveXObject)
			{
				// older IE browsers
				request=new ActiveXObject("Msxml2.XMLHTTP");
				if (! request)
					request=new ActiveXObject("Microsoft.XMLHTTP");
			}
		} catch(e) {};
		
		if (request == undefined)
			throw new Error("ydrClientScripts.XMLHttpRequest: Your browser does not support XML HTTP Requests via JavaScript objects!")
		else
			return request;		
   }
   
   this.isHidden = function(element)
   {
		//if (element.id == "password") stop();
		try
		{
			if (ydrBrowser.html.currentStyle(element, "display") == 'none' 
				/*|| ydrBrowser.html.currentStyle(element.parentNode, "display") == 'none'
				|| ydrBrowser.html.currentStyle(element.parentNode.parentNode, "display") == 'none'*/)
		{
				return true
			}
			else
			{
				switch (ydrBrowser.html.currentStyle(element, "visibility"))
				{
					case "hidden" :
						return true; 
					
					case "inherit" :
						return m_this.isHidden(element.parentNode); 
						
					default :
						return false;
				};
			};
		} catch (e) {return false /* occurs when there is no explict statement one way or the other */}
   };
   
	this.nodeWalker = function(docElement, withNulls)
	{
		// given a doc element, finds all the input fields in it and returns a real XML updates structure as used by ydrData
		// this basically gets the data in a raw form from the form so it is easy to manipulate
		
		// hates mr microsoft and netscape etc - why can we not have functions that work the same everywhere!!!
		// thought to be done by a simple selectNodes but it doesnt work so I have to walk the entire structure
		// the result is a single XML element of the form that is used in the ydrData update - this will then need to be transformed or manipulted into whatever 
		// is needed
		
		// if withNulls == true then fields with nulls as values are included in the list with value as empty string otherwise they are left as null
		//stop();
		var result = ydrBrowser.xml.document("<nodeWalk />").documentElement; 
		nodeWalkDo(result, docElement, withNulls);
		return result;
	}
	
	function nodeWalkDo(result, element, withNulls) // recursive drill through higherarchy
	{
		var i;
		var tagname;
		var value
		var formElements;

		if (element.nodeName.toLowerCase() == "form" )
		{
			formElements = ydrBrowser.html.formElements(element);
			
			for (i=0; i<formElements.length;  i++)
			{
				IOelement = formElements[i];  //picks up just the active IO elements
				
				if (IOelement.getAttribute("id") == null && IOelement.getAttribute("ydr:fmt") != 'none')
				{
					window.alert("ydrClientScripts.nodeWalk - developer error: XHTML for data input element number "
						 + i + " is required to have an id attribute\n\n"
						+ ydrClientScripts.HTMLencode(IOelement.parentNode.innerHTML) );
					IOelement.setAttribute("ydr:fmt", 'none');
				};
				
				if (IOelement.getAttribute("ydr:fmt") != 'none')
				{
					value = IOelement.getAttribute("ydr:value");
						if (value == null)
							value = ydrBrowser.html.elementValue(IOelement);
					if (value == null)
						value="";
							
					tagname = IOelement.nodeName.toLowerCase() // wanr bug in IE general failure if 
						// skip the intermediate tagname variable when of the value input - works alright if use if elseif rather than switch
					if (tagname == "input")
					{
						switch (IOelement.getAttribute("type"))
						{
							case "password" :			
							case "text" :
							case "submit" :
							case "file" :									
								if ((value.length>0) || (withNulls == true))
								{
									if (IOelement.getAttribute("id").search(/ /) >=0)
										window.alert("ydrClientScripts.nodeWalker: Ids may not contain spaces - ref "+IOelement.getAttribute("id"))
									else if (IOelement.getAttribute("ydr:fmt") == "date" && value != "")
									{
										var validation  = IOelement.getAttribute("ydr:validation");
										if (validation && validation
												.search(/validation action=['"]dateAlive['"]|validation action=['"]date['"]/)
												>=0
											)					
										
											result.setAttribute(IOelement.getAttribute("id"), new ydrDate().userUTC(ydrBrowser.html.elementValue(IOelement)).xml());	
										else
											result.setAttribute(IOelement.getAttribute("id"), new ydrDate().user(ydrBrowser.html.elementValue(IOelement)).xml());		
									}
									else
									{
										result.setAttribute(IOelement.getAttribute("id"), value);							
									}
								};
								break;
								
							case "button" :
								// ignore buttons that are there to close or navigate
								if (IOelement.onclick.toString().indexOf("ydrPopup.close")<0)
								{
									window.alert ("nodewalker does not yet handle input IOelements of type "+ IOelement.type);
								};
								break;					
								
							default:
								window.alert ("nodewalker does not yet handle input IOelements of type "+ IOelement.type);
						};
					}
					
					else if (tagname == "textarea")
					{
						if (IOelement.getAttribute("id").search(/ /) >=0)
							window.alert("ydrClientScripts.nodeWalker: Ids may not contain spaces - ref "+IOelement.getAttribute("id"))
						else if ((value.length >= 0) || (withNulls == true))
							result.setAttribute(IOelement.getAttribute("id").replace(/ /g,""), value);
					}
					
					else if (tagname == "select")
					{
						var IOelementValue;
						if (IOelement.getAttribute("id").search(/ /) >=0)
							window.alert("ydrClientScripts.nodeWalker: Ids may not contain spaces - ref "+IOelement.getAttribute("id"))
						else if (IOelement.multiple) // pos also check IOelement.type //TODO bring multiple into ydr: schema
						{
							// multiple entries have to be numeric
							var IOelementChildNodes = ydrBrowser.html.childElements(IOelement);
							IOelementValue=0;
							for (j=0; j<IOelementChildNodes.length; j++)
							{
								option=IOelementChildNodes[j];
								if (option.nodeName.toLowerCase() == "option")
									if (option.selected == "selected")
									{
										IOelementValue = IOelementValue | parseInt(ydrBrowser.html.elementValue(option));
										break;
									};	
							};
						}
						else
							IOelementValue = value;
						
						try
						{
							if ((IOelementValue != null && IOelementValue != 0) || (withNulls == true))
								result.setAttribute(IOelement.getAttribute("id").replace(/ /g,""), IOelementValue);
						} catch (e) {};
					};
				};
			};		
		}
		else
		{		
			// did not find form here - look in children
			var items;
			items = element.childNodes;
			
			for (i=0; i<items.length; i++)
				nodeWalkDo(result, items[i], withNulls) 
		};
	}
	
	// given a string or an XML object determines if it contains an error DIV
	// if an error is found then forces a page refresh and returns false
	// otherwise returns true
	this.checkResponseOK = function(response)
	{
		var result = true;
		if (typeof response == "string" )
		{
			if (response.search(/\<div class=['"]error['"]|\<!DOCTYPE/) >=0)
				result = false;
		}
		else
		{	
			try 
			{
				if (response.getAttribute("class") == "error")
					result =  false;				
			}
			catch (e) {	result=false; }
		};
		
		if (! result)
			window.location.href = window.location.href;							
		return result;
	};
	
	this.fullURLpath = function(filename)
	/* generates a full path name from
		http:// ......... ' aboslute ref
		/........ ' relative to root dir of web site (htdocs)
		......  ' full relative name	
	*/	
	{
		if (filename.substring(0,6) == "http://" || filename.substring(0,7) == "https://")
			return filename
		else if (filename.charAt(0) == "/")
			return document.location.href.substring(0,document.location.href.indexOf(document.location.pathname))
				+filename;
		else
			return document.location.href.substring(0,document.location.href.indexOf(document.location.pathname))
				+document.location.pathname.substring(0,document.location.pathname.lastIndexOf("/")+1)
				+filename;
	}
	
	//		var encoder=document.createElement("conv");
	var encoder;
	this.HTMLencode = function(s, embedding)
	{
		// converts simple text to xhtml if it does not already start with an "<" as its first character and ">" as its last
		// embedding can be 
			// plain - this is plain text where newlines become paras
			// attribute - used where so that quotes in the text do not conflict with XML syntax when used as the 
				// value of an attribute in manual text based hcoding of XML - has not impact if used for setAttribute values
			// plain - extension of attribute where the text does not start with an < - in which case newlines become <br> or <p>
				
		/*if (encoder == null)
		{
			encoder=document.createElement("x")
		};
					
		encoder.setAttribute("a", s);
		s=encoder.attributes("a").xml;
		return s.substr(3,s.length-6);*/
		if (s != undefined)
			s=s.toString().replace(/\</g,"&lt;").replace(/\>/g,"&gt;").replace("&","&amp;");
			if (embedding == "attribute")
				s=s.replace(/'/g, "&#39;").replace(/\"/g, "&#34;");
				
			else if (embedding == "plain" && s.charAt(0)!="<")
				s = "<p>"
					+s.replace(/\n\n/g, "</p><p>").replace(/  /g," &#160;").replace(/\n/g, "<br />").replace(/'/g, "&#39;").replace(/\"/g, "&#34;")
					+"</p>";
			return s 
		// sadly code below not reliable in netscape
		//encoder.innerText = s;
		//return encoder.innerHTML;
	};
	
	this.onceEnter = function(activity, eventObj)
	{
		if ('\r'.indexOf(ydrClientScripts.keyPressIs(eventObj))>=0) 
		{
			ydrBrowser.html.elementClick(document.getElementById(activity))
		};
	};
	
	this.once =function(activity, observation, restore)
	{
		// given a button make sure it can click only only 
		// with resotre="restore" then undoes the block
		if (restore == "restore")
		{
			if (activity)
				document.getElementById(activity).style.visibility="visible"; //enables self working 	;
				
			if (observation)
				document.getElementById(observation).innerHTML="";
		}
		else
		{
			var msg= "Request Submitted - Please wait";
			//stop();
			if (activity && observation)
			{
				if (document.getElementById(observation).innerHTML.trim().length>0)
				{
					document.getElementById(activity).style.visibility="hidden"; //stops self working 		
					document.getElementById(observation).innerHTML="<span style='font-weight:bold; color:red;'>Your request has already been submitted - Please wait</span>";
					return false;
				}
				else
				{
					document.getElementById(observation).innerHTML=msg;
					return true;
				};
			}
			else
			{
				document.getElementById(observation).innerHTML=msg;
				return true;
			};
		};
	};
	
	/*  function ydrClientScripts.messenger(distribution, subject, body [, from]) 
	
	Opens a popup window that contains a dialog form for the user to input an email.  
	
	Uses /ydr/ydrEmailer.asp as replicated to the current website - where it can be tailored 
	
	If the user is already logged on via ydrCredentials (not as guest), 	then the sending user email field is automatically populated.  
		If no user is logged on, ydrCredentials will attempt to autologon to the guest account if that 
		account is supported in the contacts data + security data - this needs no client side coding.
	
	The email is saved in the system log as an XML fragment
	
	The email is sent to the distribution. 
	
	A separate copy of the email, without any response buttons (where they had been requested) is copied to the originator.  This copy does not create a 
		separate log entry
	
	To prevent misue of the facility, the emails may only be sent to contacts in CONTACT/@EMAIL or to CONTACT_CLIENTUSERNAME/@CLIENTGROUP 
	as named in user database.  

	// IMPORTANT the following changes apply from the previous version of ydrClientScripts.messenger
	 - it is held in a script at http://yorkdevres.co.uk/ydr/ydrClientScripts2.js
	 - its code is now within the global static object called ydrClientScripts
			ie you call it as ydrClientScripts.ydrMessenger(distribution, subject, body) 
	 
	 - destination is structured, ie type1:name1:value1; type2:name2;value2 etc.  case sensitive
		type can be one of
			user  - the individual user version is only intended for manual replies to an earlier ydrClientScripts.messenger user enquiry
			group
			always be empty for system
		names can be one of the following - case sensitive
			to
			toMayReply  (include the tracked response buttons on the email - tracked reply also causes the reply to be logged and reuses ydrClientScripts.messenger)
			cc
			bcc
			
		A group is found in CONTACT where CORPORATESTATUS = role - the members of the group are where xrefpurpose is 'Member Of' eg <CONTACT_XREFPURPOSE XREFPURPOSE="Member Of" XREFCONTACT="3"/> .  [xrefcontact points to CONTACT_rowid] Groups can contain groups in the data but this is not implemented yet here.  Also note that the contact_rowid of a row created here is not the same as the one in any related database - the import/export functions map them.  
		
		examples with the following in the contacts database
		<CONTACTmailgroups>
			<!-- emailSendToGroups member of mailGroup administrator may receive emails from users whose clientGroup invokes the guest right -->
			<CONTACTmailGroup sendTo="administrator" right="guest"/>
			 
			<!-- emailSendToGroups member of mailGroup all  may receive emails from users whose clientGroup invokes the administrator right-->  
			 <CONTACTmailGroup sendTo="all" right="administrator"/>   
		  </CONTACTmailgroups>
		 ..
		 <CONTACT email="martinwbaker@ntlworld.com" emailSendToGroups="administrator all guest" formal="Martin Baker"  >
		 <CONTACT email="martinwbaker@yorkdevres.co.uk" formal="developer at YDR">
			<CONTACT_CLIENTUSERNAME clientUsername="22" clientPassword="155699594" clientGroup="ydr"/>
		</CONTACT>
		
		<CONTACT email="kevin_chadwick@ntlworld.com" emailSendToGroups="all"  formal="Kevin Chadwick">
			<CONTACT_CLIENTUSERNAME clientUsername="20" clientPassword="111" clientGroup="committee">
			</CONTACT_CLIENTUSERNAME>
		</CONTACT>
		 
		
				'this  can be issued by any guest or more specifically logged in user to the administrator
		  function sendMessage1() {ydrClientScripts.ydrMessenger("user:ToMayReply:administrator", "test subject", "<p>test message</p>");};
					
				'this can only be issued by the someone with the administrator right
		  function sendMessage2() {ydrClientScripts.ydrMessenger("user:BCC:all", "test subject", "<p>test message</p>");}
		 
		 you may find it convenient to generate for the body parameter XHTML by the technique below 
		 
		 document.getElementByID("sendMessage2")
		 
		 where perhaps you have placed the text in a a division
		 
		 <div id='sendMessage2' style=position:absolute; visibility:hidden'>
		 <form action='myASP'> 
		 ..your message XHTML ..
		 </form>
		 </div>
		
		or perhaps
		
		ydrClientScripts.XMLhttpRequest() to fetch an XML/XHTML file and user the responseXML property
					
						
	 - subject is any reasonable plain text without tabs or newlines
	 - bodyis 
			string: any plain text eg as issued by the previous ydrClientScripts.messenger function
			xmlElement <body> .XHTML. </body> being the body of a completed message ready to be sent (ie there are no dialogs to be given to the issuing user although there may be <form> that will be sent to the addressee's to complete
			xmlElement <form action='yourASPinThisDomain'> .XHTML.</form> being the full xhtml rendering of the dialog to be given to the user to be sent - there should only be one form and that form is issue to the current user to complete - the result is sent as an email to the current user.  
				xmlElement this should include links to css, scripts in the normal way - but beware that emails with scripts may be subject to filtering - such as 
					blocking the entire email or by having the scripts stripped out by anti virus / malware software.  
				
				You may wish to consider fetching the xmlElement from a server page and using it straight or amended by your script.  You can use eg 
					var xml=ydrClientScripts.XMLHttpRequest()
					
					xml.responseXML.xml.replace(/%placeholder%/,customValue) 
					as a browser independent way of getting this
					
					In the <form>
			The <form> version may contain <YDRexclaimfielddef> elements which define data entry form fields and their handling characteristics.  It is often possible to reuse existing fields 
			<YDRexclaimfielddef objectname="FORMAL" like="contact!formal" prompt="my special prompt") 
				will render a field with the same properties as the formal field in table contact but with the prompt customised
			<YDRexclaimfielddef objectname="FORMAL" fmt="ftString" prompt="my special prompt")	
				will do much the same thing but wont have any hemp messages or validations etc
			
			The <form> element may NOT contain <input> elements directly (hence it wont have a submit button), nor should it contain <a> unless the href is missing, an empty string, or a relative reference to the current page.
			
			If there are any YDRexclaimfielddef fields and you want to process them via your handler, the <body> must contain one ore more <form> elements.  The action attribute of the form element shall be to your handler for the page (note that an intermediate standard asp page will actually receive the page and forward an xml representation of the response in the form <response objectname1="value" objectname2="value" .. /> where objectnames are as defined in the YDReclaimfielddefs.  Your thread will continue in the dialogbox.
			
			Each form shall contain a <table><tbody> with two columns surrounding any <YDRexclaimfielddef> field you define  (<YDRexclaimfielddef>  generates a two column table row)	
	
	*/
	
	/* mainElement, popupElement are optional as  for show */
	var m_onExit;	

	this.messenger = function(destination, subject, body, from, onExit, onValidate, mainElement, popupElement, xpath)
	{	
		var requestHTTP;

		function emailResponse()
		{
			if (requestHTTP.readyState == 4)
			{			
				// if we did not get an OK message then reshow the form with the response as the validation message
				if (requestHTTP.responseText.indexOf("class='emailerOK'")<0)
				{
					ydrPopup.advisory(requestHTTP.responseText);
					ydrPopup.reshow();
				}
				else
				{
					ydrPopup.show (requestHTTP.responseText, subject, 'large');
					requestHTTP = null;				
				};
			};
		};
			
		var destinationName;
		var destinationTarget;
		if (destination.indexOf(":")>0)
		{
			destinationName = destination.substr(destination.indexOf(":")+1);
			destinationTarget = destination.substr(0,destination.indexOf(":"));
		}
		else
		{
			destinationName="";
			destinationTarget = destination;
		};
		
		m_onExit = onExit;
			
		if (typeof (body) == "string") 
			// if not a real object first see if it plain text version of a real one
		{
			if (body.search(/^\s*<body/)<0)
				body = ydrBrowser.xml.document(
					"<form "
					+ ydrNamespace
					+" ydr:fieldstyle='width:40em'>"
					+"<p >" + ydrClientScripts.HTMLencode(body,"plain") + "</p>"
					+"<table>"
					+"%predefined%" /* the entries here will indicate to whom the email is sent and allow the user to define the subject */
					+"</table></form>")
			else
				body = ydrBrowser.xml.document(body.replace(/&amp;/g, "&#160;"));
					
			if (ydrBrowser.xml.parseError(body))
					throw new Error ("String version of message body is not loadable\n\n" + ydrBrowser.xml.parseError(body));
			
			body=body.documentElement;
		};
		
		try 
		{
			switch (body.nodeName)
			{
				case "body" :
					// this directly sends an email without a user dialog
					{
						var requestBody =  ydrBrowser.xml.document("<ydrEmailer />").documentElement;				

						requestBody.setAttribute("subject",subject)
						if (from != null)
							requestBody.setAttribute("from", from);
						requestBody.setAttribute("body", ydrBrowser.xml.xml(body));		
						
						if (destinationTarget && destinationTarget.indexOf("_")>0)
							requestBody.setAttribute("id",destinationTarget)					
						else
						{
							requestBody.setAttribute("id",ydrBrowser.html.elementValue(document.getElementById("messengerTo")));
						};
						
						requestHTTP=this.XMLHttpRequest();
						requestHTTP.onreadystatechange=emailResponse;
						requestHTTP.open("POST",rootDir+"/ydr/data/ydrEmailer.asp");	
						requestHTTP.send(ydrBrowser.xml.xml(requestBody));		
					};
					
					break

				case "form" :
					// this displays the requested dialog and sends an email if successful
		
					{
						function popupResponse(index, bodyResult)
						{
							switch (index)
							{
								case 0 /*OK*/ :
									var message = "";
									var nodeWalk= m_this.nodeWalker(bodyResult);
									var localDomainSender = nodeWalk.getAttribute("messengerFrom").indexOf("@"+gc_emailDomain)>0
									
									//message = "<body>";
									
									if (m_onExit != undefined)
									{
										message +=m_onExit(nodeWalk);
										m_onExit = undefined;
									}
									else
									{
										if (! localDomainSender) 
											message += "<h2>"
												+ ydrClientScripts.HTMLencode(nodeWalk.getAttribute('messengerTitle')) 
												+"</h2>";
										
										message +="<p>";
										
										var attr;
										var messageExtra = "";
																				
										for (var i=0; i<nodeWalk.attributes.length; i++)
										{	
											attr = nodeWalk.attributes[i];
											switch (attr.nodeName)
											{
												case "messengerTitle" :
													// ignore this one
													break; 
													
												case "messengerFrom" :
													// note these wont work if they are textareas
													
													if (! localDomainSender)
														message += "<span class='object'>" + attr.nodeName.replace(/messenger/,"").replace(/-/g," ")
																+":</span> %email%<br />"
																	.replace(/%email%/g, ydrClientScripts.HTMLencode(attr.nodeValue,"attribute"));	
																	/*+":</span><a href='mailto:%email%'>%email%</a><br />"*/
													break;	
												
												case "messengerTo" :
													if (! localDomainSender)
														message += "<span class='object'>" + attr.nodeName.replace(/messenger/,"").replace(/-/g," ")
																+":</span> " + ydrClientScripts.HTMLencode(attr.nodeValue,"attribute")+ "<br />"
																+"<span class='object'>Sent:</span> " + new ydrDate().user()+ "<br />";	
													break;

												case "messengerSubject" :
													if (! localDomainSender)
														message += "<span class='object'>" + attr.nodeName.replace(/messenger/,"").replace(/-/g," ")
																+":</span> " + ydrClientScripts.HTMLencode(attr.nodeValue,"attribute")+ "<br />";	
													break;
													
												default :
													if (document.getElementById(attr.nodeName).nodeName.toLowerCase() == "textarea")
													{
														if (! localDomainSender)
															messageExtra += "<p><span class='object'>" + attr.nodeName.replace(/messenger/,"").replace(/-/g," ")
																+":</span></p>" ;
														
														if (attr.nodeValue.search(/^\s*</)<0)
															messageExtra += ydrClientScripts.HTMLencode(attr.nodeValue, "plain")
														else
															messageExtra += attr.nodeValue.replace(/<body .*?>|<\/body>/g,"");
													}
													else
														messageExtra +=((! localDomainSender) 
																? ""<p>"<span class='object'>" + attr.nodeName.replace(/messenger/,"").replace(/-/g," ")	+":</span></p>"
																: "")
															+"<p>" + ydrClientScripts.HTMLencode(attr.nodeValue)+ "</p>";		
											};												
										};
										message += "</p>";
																	
										if (messageExtra.length >0)
										{
											if (messageExtra.search(/\<body.*\>/) < 0)
												message = "<body>" +  ((! localDomainSender) ? message + "<hr />" : "") + messageExtra + "</body>"
											else
												message = messageExtra.replace(/(\<body.*?\>)/,"$1"+message)
										}
										else
											message="<body>" + message + "</body>";
									};
											
									// this wont show but does enable the recipient to use the original data before formatting
									// if the recipient of the email wants to read it electronically 
									// but we dont want two whole copies of the message itself
									/*  removed cos it never got used - also with some email agents the display:none can get ignored when forwarding emails
										with (nodeWalk.cloneNode(true))
										{
											removeAttribute("messengerMessage");
											message +=  "<div style='display:none'>"
												+	ydrClientScripts.HTMLencode(xml)
											+"</div>"
											+ "</body>";						
										};
									*/
									
									from = nodeWalk.getAttribute('messengerFrom');
									
									// now we can produce a normal direct action messenger request which we recurse into
									// only recurses 1 level
									m_this.messenger(destinationTarget
										, nodeWalk.getAttribute('messengerSubject')
										, message
										, from, undefined, undefined
										, mainElement, popupElement
										);
									break;
								case -1 /*cancel*/ :
									break;
										
								default:
									// should never get here except in developer mode with errors in code
									stop();
							};
						};
						
						var fieldStyle;
						fieldStyle="style='"+body.getAttribute("ydr:fieldstyle")+"'";
						if (fieldStyle == undefined)
							fieldStyle="style='width:50em'";
										
						var predefined = "";
						// if the from field is not manually defined then it becomes the fixed current user if logged in otherwise it becomes a 
						// forced logon field
						if (ydrBrowser.xml.getElementById(body, "messengerFrom") == undefined)
						{
							switch (userState /* variable set in client boilerplate via menu.xml*/)
							{
								case "none":
								case "guest":
									predefined += "<tr><td>From</td><td><input id='messengerFrom' type='text' " 
										+ fieldStyle + " ydr:fmt='email' ydr:validation='&lt;validation action=&quot;notNull&quot; /&gt;'/></td></tr>";				
										
									break;
									
								default :
									predefined += "<tr><td>From</td><td>"
										+ this.urlFetchString(rootDir+"/ydr/data/ydrExclaimList.asp?action=currentUserEmails").replace(/%fieldStyle%/g, fieldStyle)
										+"</td></tr>";
							};
						};
						
						if (destinationTarget.search(/#[a-zA-Z_@0-9]*#/)>=0 )
						{
							// watch this one - tricksy
							// we are only writing the xhtml here  - but at the same time we trigger a fetch of the options list.
							// By the time we have loaded the xml into the ydrPopup it has probably responded (if results cached)
							// as is waiting in an event queue - the onreadystate then fires a few times for response progress and
							// finally stuffs the options into the cell of table - all in a blink!!!
							
							destinationTarget = destinationTarget.replace(/#/g, "");
							
							with (ydrClientScripts.XMLHttpRequest())
							{
								onreadystatechange=	function ()
								{
									if (this.readyState == 4)
									{
										var element = document.getElementById("messengerTo");
										element.removeAttribute("disabled");
										
										{
											var optionElement;
											var items = ydrBrowser.xml.selectNodes(this.responseXML.documentElement, "option");
											
											for (var i=0; i<items.length; i++)
											{
												optionElement = element.appendChild(element.ownerDocument.createElement("option"))
												
												ydrBrowser.html.text(optionElement
													, ydrBrowser.html.elementValue(optionElement, items[i].getAttribute("value"))
												)												
											};
										};
									};
								};
								
								var customDef = "&customDef=";
								
								switch (destinationTarget)
								{
									case "Role" :
									case "Group" :
										//TODO need to check access to items
										customDef += escape(
											"<YDRexclaimfielddef><YDRreferences table='CONTACT' display='CONTACTID' "
											+" whereXpath=\"@CORPORATESTATUS='" + destinationTarget + "'\" />"
											+"</YDRexclaimfielddef>"
											); 
																
										break;
										
									case "Person" :
										customDef += escape(
											"<YDRexclaimfielddef><YDRreferences table='CONTACT' display='CONTACTID' "
												+" whereXpath=\"@CORPORATESTATUS='Person'"
												+"  and contains(@EMAIL,'@')\" />"
											+"</YDRexclaimfielddef>"
											); 
										
										break;
										
									default : // assume is an xpath of contacts that can be used
										customDef += escape(
											"<YDRexclaimfielddef><YDRreferences table='CONTACT' display='CONTACTID' "
												+" whereXpath=\"useRule:" + destinationTarget + "\" />"
											+"</YDRexclaimfielddef>"
											); 
								};								
								
								open("GET",rootDir+"/ydr/data/ydrExclaimList.asp?action=referenceList"
										+ "&source=XREFCONTACT"
										+ "&origin=userDetails"
										+ "&table=CONTACT"
										+ customDef);	
								send(null);			
							};
							
							predefined += "<tr><td>To "+destinationName+"</td><td><select id='messengerTo' type='text'"
								+" onchange='ydrClientScripts.messengerToChange(this)'"
								+" value='loading ..' disabled='disabled' " + fieldStyle 
									+"ydr:validation='&lt;validation action=&quot;notNull&quot;/&gt;' />"
								+"</td></tr>"
								+"<tr><td/><td id='messengerToExplanation' style='margin-top:-.5em; margin-bottom:-5.em'/></tr>";
						}
						else if (destinationName.length>0)
							predefined += "<tr><td>To</td><td><input id='messengerTo' type='text' value='" 
								+ ydrClientScripts.HTMLencode(destinationName, "attribute") + "' disabled='disabled' " + fieldStyle +"/></td></tr>";
						
						if (ydrBrowser.xml.getElementById(body, "messengerSubject") == undefined)
							predefined += "<tr><td>Subject</td><td><input id='messengerSubject' type='text' "
								 + fieldStyle +"ydr:validation='&lt;validation action=&quot;notNull&quot; /&gt;'/></td></tr>";				

						if (ydrBrowser.xml.getElementById(body, "messengerTitle") == undefined)
							predefined += "<tr><td>Title</td><td><input id='messengerTitle' type='text' value='" 
								+ ydrClientScripts.HTMLencode(subject, "attribute") +"' disabled='disabled' "  + fieldStyle +" ydr:validation='&lt;validation action=&quot;notNull&quot; /&gt;'/></td></tr>";				
						
						if (ydrBrowser.xml.getElementById(body, "messengerMessage") == undefined)
							predefined += "<tr><td>Message</td><td><textarea id='messengerMessage' "+ fieldStyle.replace(/'/,"'height:10em;") +" ydr:validation='&lt;validation action=&quot;notNull&quot; /&gt;&lt;validation action=&quot;formattable&quot; /&gt;'></textarea></td></tr>";				
																		
						ydrPopup.show(ydrBrowser.xml.xml(body).replace(/%predefined%/,predefined)
							, "Send an email"
							, null, popupResponse, null/*onValidate*/, "'0^Send Email'-1^Cancel'"
							, mainElement, popupElement);		
						
						if (emailState == 'none')
						{
							// fill in invalid fields so we can send form to server - and get the server's complaint
							ydrBrowser.html.elementValue(document.getElementById("messengerSubject"), "dummy text");
							ydrBrowser.html.elementValue(document.getElementById("messengerMessage"), "dummy text");
							ydrPopup.close(0); 
							
							ydrPopup.show(ydrPopup.advisory());
						};
					};
					
					break;
					
				default :
					ydrPopup.show("<h2>Error calling ydrClientScripts.messenger</h2><developer /><p class='error'>There has been an an unrecognised call to the messenger: unrecognised BODY name or type</p><p class='info'>The parameter must be of type string or iXMLDomElement</p>"
						, subject
						, "medium", null, null, null
						, mainElement, popupElement);
			};	
		}
		catch (e)
		{
			ydrPopup.show( "<p class='error'>"+ this.HTMLencode(e.message) +"</p>"
				,"<h2>Error executing ydrClientScripts.messenger</h2>"				
				, "small", null, null, null
				, mainElement, popupElement
				);
		};
		
		return false;
	}

	// puts an explanation of the contact selected under the TO field if there is one 	
	this.messengerToChange = function(element)
	{
		document.getElementById("messengerToExplanation").innerHTML="";
				
		with (ydrClientScripts.XMLHttpRequest())
		{
			onreadystatechange=	function ()
			{
				if (this.readyState == 4)
					document.getElementById("messengerToExplanation").innerHTML=responseText;
			};
			
			open("GET",rootDir+"/ydr/data/ydrExclaimList.asp?action=contactExplanation" + "&contactid=" +escape(ydrBrowser.html.elementValue(element)));
			send(null);			
		};			
	}
	
	this.keyPressIs = function(event)
	{
		if (m_this.isIE)
			return String.fromCharCode(event.keyCode)
		else
			return String.fromCharCode(event.which)
	};
	
	this.testBlowOut = function(id)
	{
		Response.Write("<h4>Got to trace point " + id +" </p>");
		stop();
	}
	//header anchor is a direct child of a TD or TH in a THEAD
	// table sort causes all the rows in the tbody of the table to be sorted according to the innerHTML of the TD of the  column
	// implemented so as to require the minimum impact on a table
	//invoked by <thead><tr><td><a href="" onclick="return ydrClientScripts.tableSort(this)">colname</a></td> .. etc
	// all rows to be sorted must be in the first TBODY - other rows in subsequent tbody, and in thead/tfoot are untouched
	
	// if the noAnchor is "noAnchor" then the cell is the TD element itself
	
	// WARNING tablesort is NOT compatible with colspan and rowspan - use sub tables	
	
	// starter bloats all values to start with a numeric part - result is that both numeric and string strings sort properly
	// fails to sort all correctly if integer part of number > 10 characters long or number is exponential format - OK with decimals with fixed 
	// number of decimal places (dot or comma notation), non numbers or a mix - no good with financial format numbers with 
	// thousands or () for negative
	var zero="0000000000";
	function starter(s)
	{
		var numericPreamble;
		var i;
		var posn=s.search(/[^0-9]/);
		if (posn<0)
			posn=s.length;
		return zero.substr(0,Math.max(0,10-posn))+s;
	}
	
	// see also ydrgenericScripts.plainSorter()	
	var direction=-1;// makes ascending or descending on alternate uses
	this.tableSort = function(headerAnchor, noAnchor , actions)
	{   
		//                               <A>            <TD/TH>     <TR>    
		if (noAnchor != "noAnchor")
			headerAnchor = headerAnchor.parentNode;
			
		var thead_tr =headerAnchor.parentNode;
		//                         <TR>  <THEAD>  <TABLE>
		var table = thead_tr.parentNode.parentNode;
		var td;
		var colPos=-1;
		var i=0;
		var priorVisible = (actions == "allPriorFields");
		
		direction=-direction;
		function sorter(left,right)
		{
			var myColPos = colPos;
			var myLeft;
			var myRight;			
			var done;
			
			while (myColPos>=0)
			{
				myLeft=starter(left.childNodes[myColPos].innerHTML);
				myRight=starter(right.childNodes[myColPos].innerHTML);
								
				done = ((myLeft == myRight) ? 0 : (myLeft > myRight) ? -1 : 1) * direction;
				
				if (!priorVisible || done != 0)
					break;
		
				myColPos--;
			};
			
			return done;
		};
		
		if (table.nodeName.toLowerCase() != "table")
		{
			alert("scripting error - incorrect column identifier");
			return false
		};
		
		var tbody;
		var items;
		with (items = table.childNodes)
		{
			for (i=0; i<length;  i++)
			{
				switch (items[i].nodeName.toLowerCase())
				{
					case "tbody" : tbody = items[i]; break;
				};
			};			
		};
		
		// find my column 
		for (i=0; i<thead_tr.childNodes.length; i++)
		{
			if (thead_tr.childNodes[i] == headerAnchor)
			{
				colPos= i;
				break;
			};
		};
			
		if (colPos == -1)	
			alert("scripting error: column not located")
		else
		{
			var rows = new Array();
			var originalRows = tbody.getElementsByTagName("tr");
			for (i=0; i<originalRows.length; i++)
				rows[i]=originalRows[i].cloneNode(true);					
			
			rows.sort(sorter);
			
			for (i=0; i<rows.length; i++)
				tbody.replaceChild(rows[i], originalRows[i]);
		}
		return false;
	}
	
	// given an browser node/element generates a tolerably reasonable xhtml object from it
	this.xhtml = function(node, /*optional*/ cleanser )
	{;
		var i,j;
		var result = "";
		var spanElement;
		var fontElement;
		var newElement;
		
		var doc=ydrBrowser.xml.document("<body />").documentElement;
		
		// convert to xml = but this is with junk (eg microsoft office) and with inline elements outside blocks eg <font><p>sdgfd</p></font>
		xhtmlDo(node, doc, false) ;
		
		// prevent ever deepening div nodes - because the given node could be a body
		if (doc.firstChild && doc.firstChild.nodeName.toLowerCase() == "body")
			doc = doc.firstChild; 
	
		//window.alert(node.parentNode.innerHTML);
		//window.alert(ydrBrowser.xml.xml(doc));
	
		if (cleanser == "simpleXML")
			return doc;
			
		if (cleanser == null)
		{
			cleanser  = new ydrHTMLcleanser();
			cleanser.removeAsp = true;
			cleanser.removeNamespace = true;
			cleanser.removeScript = true;
			cleanser.removeColonisedElements = true;
			cleanser.removeColonisedAttributes = true;
			
			cleanser.changeFont2Style = false;			
		};
		
		return cleanser.cleanse(doc);
	};

	// recursive element of xhtml() to glean from idiot IE outerHTML into XML
	// replaces unquoted attributes with quotes and boolean attributes with proper ones
	// - ie loses any case settings so you cannot get these back anyway
	function xhtmlDo(sourceNode,resultXML) 
	{
		var resultElement;
		var canCleanEmpty;
		var child;
		var thisNodeName;
		var firstChildNodeName;
		var scopeName;
		
		switch (sourceNode.nodeType)
		{
			case 8: // comment
				resultXML.appendChild(resultXML.ownerDocument.createComment(sourceNode.nodeValue));
				
				break;
			
			case 3: // text sourceNode
				resultXML.appendChild(resultXML.ownerDocument.createTextNode(sourceNode.nodeValue));
				break;
				
			case 1: // element
				thisNodeName = sourceNode.tagName.toLowerCase();
				if (thisNodeName.charAt(0) != "/") // exceptions can occur through pasting in crap html - generally is spurious close tags 
					// of elements that are supposed to be bodyless - eg should be <img .. /> and not <img..></img>
				{
					// deal with legacy font declarations
					switch (thisNodeName+"disabled temporarily") 
					{
						case "p" :
						case "font" :
							firstChildNodeName = sourceNode.firstChild;
							if (firstChildNodeName)
								try 
								{
									firstChildNodeName = firstChildNodeName.tagName.toLowerCase();
								} catch (e) {};
								
							switch (firstChildNodeName)
							{
								case "div" :
								case "dl" :
								case "p" :
								case "table" :
								case "ul" :
								case "ol" :
									thisNodeName = "div";
									break;
										
								default: 
									if (thisNodeName == "font")
										thisNodeName = "span";
							};
							break;
							
					};
					
					try 
					{
						scopeName = sourceNode.scopeName;
						if (scopeName == null)
						{
							// scopename is only available in Internet Explorer
							scopeName = sourceNode.nodeName.split(/:/);
							if (scopeName.length ==1 || scopeName[0] == "ydr")
								scopeName = "HTML"
							else
								scopeName = scopeName[0];
						};
						
						if ( scopeName == "HTML" || "'imagedata'shape'".indexOf(sourceNode.nodeName)>=0)
						{
						
							resultElement =  resultXML.appendChild(resultXML.ownerDocument.createElement(thisNodeName))
							canCleanEmpty = true;
								
							// we do these backwards because sometimes IE sticks in extra nodes as you navigate
							// this is a bugger cos once it starts happening it makes the loop redo the same data it has just done 
							// until you have an infinite XML document
							for (var j=sourceNode.attributes.length-1; j>=0; j--)
							{
								with ( sourceNode.attributes[j])
								{
									if (specified || nodeName == "value") // ie bug reports value as unspecified even when a value is specified for it
									{
										if (typeof nodeValue == "boolean")
											resultElement.setAttribute(nodeName.toLowerCase(), nodeName.toLowerCase())
										else if (nodeValue != null  && nodeValue != "")
											resultElement.setAttribute(nodeName.toLowerCase(), nodeValue)
										else if (nodeName == "style")
											resultElement.setAttribute("style", sourceNode.style.cssText.toLowerCase()); 
									};
								};
							};			
						}
						else if (scopeName == "o")
						{
							if (sourceNode.outerText.length>0)
								resultXML.appendChild(resultXML.ownerDocument.createTextNode(sourceNode.outerText));
								
							resultElement = resultXML;
							canCleanEmpty = false;
						}
						else 
						{
							resultElement = resultXML;
							canCleanEmpty = false;
						};
					} catch (e) {};
					
					if (resultElement)
					{
						child = sourceNode.firstChild;
						
						while (child)
						{
							xhtmlDo(child, resultElement);
							child = child.nextSibling
						}
						
						/*
						for (var j=0; j<sourceNode.childNodes.length; j++)
						{
							xhtmlDo(sourceNode.childNodes.item(j), resultElement)
						};*/
					};
					
					// font p span etc can end up empty - in which case they can be removed
					if ( canCleanEmpty && resultElement.childNodes.length == 0)
						if (resultElement.nodeValue == null)
							switch (resultElement.nodeName+"temp dia")
							{
								case "b" :
								case "em" :
								case "font" :
								case "p" :
								case "span" :
								case "strong" :
								case "u" :
								case "ul" :
								case "li" :
									if (resultElement.parentNode != null)
										resultElement.parentNode.removeChild(resultElement);
							};
				};	
				break;
				
			default :
				window.alert ("xhtmlDO unknown nodetype [" + sourceNode.nodeNam + ":" + sourceNode.nodeType + "] - ignored");
		};
		
		return;
	}
}  


///-----------------------------------------------------------------------------------------------------------

// this are scripts for processing fields created via ydrExclaimfieldDEF 
// at present only good for mask type assoc fields
// they are essentially static based upon data in the document at a given id as created by ydrTAGGET and YDREXCLAIMFIELDDEF
var ydrClientScriptsExclaim = new function()
{
	// get all nodes having a name the same as the id of the given node and the same element type
	function getAll(id)
	{
		var node=document.getElementById(id);
		if (node != null)
			return node.parentNode.getElementsByTagName(node.nodeName)
		else
			return new Array();
	}
	
	this.getValue = function(id, surrogateListValue)
	{
		// presently only implements assoc ydrexclaimfields
		// if present then uses the structures in the document but the value of the textual list
		var foundAll = getAll(id);
		var result = 0;
		
		if (surrogateListValue == undefined)
		{
			// classic form - data from the database
			
			for (var i=0; i<foundAll.length; i++)
			{
				if (foundAll[i].getAttribute("checked"))
					result = result | ydrBrowser.html.elementValue(foundAll[i]);
			};
		}
		else
		{
			if ((typeof surrogateListValue) != "string")
				surrogateListValue= surrogateListValue.toString();
				
			for (var i=0; i<foundAll.length; i++)
			{
				if (surrogateListValue.indexOf(
					surrogateListValue.charAt(0)
					+foundAll[i].nextSibling().nodeValue().trim()
					+surrogateListValue.charAt(0))
				>=0)
					result = result | ydrBrowser.html.elementValue(foundAll[i]);
			};
		};		
		
		return result;
	};

	this.setProperty = function(id, property, newValue)
	{
		// sets all instances of property for tags named id to the value newvalue
		// property must be a member of the implemented list below
		// if node is given then only looks for name in the given node 
		// otherwise uses legacy document.all

		try 
		{ 	
			var foundAll = getAll(id);
			property=property.toLowerCase();
		
			for (var i=0; i<foundAll.length; i++)
			{
				foundAll[i].setAttribute(property,newValue);		
			}
		} catch (e) {window.alert (e.message)};
	}

	// takes a delimited list |v1|v2|...|vn| and return with |t1|t2|...|tn| 
	// where t is the locale text equivalent of the value v according to the given xml data type
	// conversion is via xml in IE and by custom code for 
	var converter;
	
	/*if (ydrClientScripts.isIE)
		converter=document.createAttribute("conv");*/
	
	this.valueList2Text = function(newValue, dataType)
	{
		var result = newValue;
		switch (dataType)
		{
			case "date" :
			case "dateTime" :
				// note this gives the user dates in the user's language not that of the spreadsheet
				
				if (selElement.type =="Microsoft Internet Explorer")
				{
					var dates=result.split(delimEntry);

					for (j=0;j<dates.length;j++)
					{
						if (dates[j].length)
						{
							try
							{
								dates[j]= new ydrDate(dates[j]).user();								
							} catch(e) {};
						}
					};					
					
					result=dates.join(delimEntry);
				};
										
				break;
			
			case "int" :
				// fine as is
				break;
				
			case "string" :
				// fine as is
				break;
			
			default :
				throw "ydrClientScriptsExclaim.valueList2Text: Unrecognised data type "+ dataType;
		};
		
		return result;
	}
	
	
		// takes a delimited list |v1|v2|...|vn| and return with |t1|t2|...|tn| 
	// where t is the locale text equivalent of the value v according to the given xml data type
	// conversion is via xml in IE and by custom code for 
	// if delimiter is not provided then the default delimiter is used
	// if value is not provided then the value in the document is used

	this.textList = function(id, value, delimiter)
	{
		if (delimiter == undefined)
			delimiter = delimEntry;
			
		if (value == undefined)
			value=this.getValue(id);
			
		var foundAll = getAll(id);
		var result = delimiter;
		
		{
			if ((typeof surrogateListValue) != "string")
				value=value.toString();
				
			for (var i=0; i<foundAll.length; i++)
			{
				if (foundAll[i].getAttribute("checked"))
					result = result + foundAll[i].nextSibling().nodeValue().trim() + delimiter;
			};
		};		
		
		return result;
	};
	
	this.tagYDRHTML = function(script, savePage)
{
	// script is the script/html to be output
	// savePage if is "forceLegal" then
		// the current page is left unchanged and the legality checks are omitted
		// if is undefined then the current page is not changed
		// otherwise the currentPage is updated with this page name if the page is legal to access
	// forceLegal MUST be set client side
		
	var isLegal=true; 
	//var page=requestedPage(); not used?
	var pageRight = "*";
	
	var posnBody = script.search(/<\s*body/i);					// the start of the body tag
	var metaParams;

	tagOffset=0;
	var finished = false;
	
	while (! finished)
	{
		metaParams = tagGet(script.substring(0,posnBody-1), "meta");
		
		if (metaParams == undefined || metaParams.length == 0)
			finished = true;
		else
		{
			try{
				if (metaParams.item("name").toLowerCase() == "ydraccessright")
				{
					pageRight = metaParams.item("content").toUpperCase();
					finished = true;
				}
			} catch (e){};
		}
	};

	if (ydrGenericScripts.isAtServer())
		{
		if (pageRight != "*" && (ydrServerScripts.sessionGet("rights").indexOf("'"+pageRight+"'")<0))
			isLegal = false;

		// remember the page for the future in case of later need to revert to it, but dont do this
		// for a forced legal page
		if (savePage == "forceLegal")
			isLegal = true
		else if (savePage != undefined && isLegal)
			Session("currentPage") = savePage;
				
		// force us into a logon or similar page if the page session has timed out, unless this is a forced OK page
		if (savePage != "forceLegal")
			if (! sessionIsActive())
				ydrServerScripts.responseEnd();
	};

	if ( ! isLegal)
	{
		// illegal access to a page where the user is logged in and does not have rights
		// note funny structure here as the stuff is part processed
		
		responseErrorOnAccess(pageRight);
	};

	var matches=0;
	var matchEndPosn;
	
	while (true)
	{
		matches=script.search(/<!-*\s*YDRCUSTOMTAG /i);
		
		if (matches < 0)
			break;	
		
		matchEndPosn = script.indexOf(">", matches);
		
		if (matchEndPosn <0)
			break;
		
		try 
		{
			script=script.substring(0, matches) 
				+ tagYDRcustom(script.substring(matches, matchEndPosn+1))
				+ script.substring(matchEndPosn+1)
				;		
		} catch (exception) {break;};
	};	
	
	while (true)
	{
		matches=script.search(/<!-*\s*YDREXCLAIMFIELDDEF /i);
		
		if (matches < 0)
			break;	
		
		matchEndPosn = script.indexOf(">", matches);
		
		if (matchEndPosn <0)
			break;
		
		script=script.substring(0, matches) 
			+ tagYDRexclaimFieldDef(script.substring(matches, matchEndPosn+1),"","")
			+ script.substring(matchEndPosn+1)
			;		
	};	

	if (ydrGenericScripts.isAtServer())
	{
		posnBody = script.indexOf(">",posnBody)+1;
		script = script.substring(0,posnBody)
			+"\n" + Session("clientBody")
			+script.substring(posnBody);
		
		return tagYDRHOME(tagYDRA(script
				.replace(/<\s*HEAD\s*>/i,
					"\n<head>"
					+"\n\t<meta NAME=\"GENERATOR\" Content=\"York Development and Research - YDR Java Script Tools 2004.2008\">"
					+"\n\t"+Session("clientHeader"))					
		))
	}
	else
		return script;	
}


function tagYDRHOME(script)
// put the Session("home") text into where <!--YDRHOME-->  text is  <!--/YDRHOME-->
// also if there is a <!--YDRHOMEBODY--> in the value of the Session("home") variable then inserts the "text is" part where the tag is
// otherwise "text is" gets lost
// leaves tags in place if error
{
	while (true)
	{
		var posn = script.search(/<!-*\s*YDRHOME\s*-*>/i);
		
		if (posn<0)
			posn = script.search(/<!-*\s*YDRHOME\s*-*>/i);
			
		if (posn <0)
			return script;

		var posnEnd = script.search(/<!-*\s*\/YDRHOME\s*-*>/i);
		if (posnEnd<0)	
			posnEnd = script.search(/<!-*\s*\/YDRHOME\s*-*>/i);
		if (posnEnd <= posn) 
			return script;
	 
		script = ""+  // fails without the "" when the first substring is empty
			script.substring(0,posn)
			+"\n"+Session("home").replace(/<!-*\s*YDRHOMEBODY\s*-*>/ig, script.substring(script.indexOf(">",posn)+1, posnEnd ) )
			+"\n"+script.substring(script.indexOf(">",posnEnd)+1);
	};		
}

};

// the following invokes chron on the server periodically
var chronInitiator = new function()
{
	var chronProbability =.99;// run the ratio times of page openings 
	var chronLatency = 1; // wait one seconds after page load to call chron
	
	this.timeout = function(code)
	{
		try 
		{
			with (ydrClientScripts.XMLHttpRequest())
			{
				if (gc_rights.indexOf("'administrator'")>=0)
					onreadystatechange =  function()
					{
						if (this.readyState == 4)
						{
							if (this.responseXML == null)
							{
								try
								{
									window.alert("Error in chron response\n\n" 
										+ydrGenericScripts.simpleHTMLtoPlain(this.responseText.split(/<\/ul>/)[1]) )
								}
								catch (e)
								{ window.alert("Error in chron response") };
							}
							else
							{
								with (this.responseXML)
								{
									if (documentElement)
									{
										var status = documentElement.getAttribute("status");
										if (status == "repeat")
											window.setTimeout("chronInitiator.timeout()",chronLatency*1000)
										else if (status && status != "")
											window.alert("Event reported from CHRON\n\n" + status+"\n\n" + documentElement.getAttribute("action"));
									};
								};
							};
						};
					};
				open("GET",rootDir+"/ydr/chron.asp?action=ticker"
					+((code)
						? "&code=" + escape(code)
						: "")
				);	
				send(null);
			};
		} catch (e) {};
		// note we dont care about the result all we want is the server to run a page
	};

	this.chronTrigger = function(code)
	{
		window.setTimeout("chronInitiator.timeout("
			+ ((code) 
				? "\"" + ydrGenericScripts.xmlEncode(code) +"\""
				: "")
			+")",chronLatency*2000);
	}

	// always executes when script is loaded (no procedure call as part of constructor!)
	if (Math.random() < chronProbability)
		window.setTimeout("chronInitiator.timeout()",chronLatency*1000);
}
 
var ydrHTMLcleanser =  function()
{
///// sub classes
	this.removeAsp=false;
	this.removeNamespace=false;
	this.removeScript=false;
	this.removeComment=true;	

	this.removeMSO=true;

	this.removeColonisedElements=false;
	this.removeColonisedAttributes=false;
	
	this.removeEmptySpan = true;
	this.changeFont2Style = true;

	this.cleanse=function(doc)
	{
		var i, j;
		var results;	
		var isSafari = window.navigator.appVersion.search(/Chrome|Safari/)>0;
						
		if (this.removeMSO)
		{
			var styleText;
					
			var items;
			with (items = ydrBrowser.xml.selectNodes(doc, "//*[@class]"))   // was //.[@class]" 20100218
			{
				for (i=0; i<length; i++)
				{
					with (items[i])
					{
						switch (getAttribute("class").toLowerCase())
						{
							case "msodel" :
								removeAttribute("class");
								var styleText = getAttribute("style");
								if (styleText== null)
									styleText="";
								styleText += " display:none";
								setAttribute("style", styleText);	
								
								break;
							
							default :
								if ( getAttribute("class").substr(0,3).toLowerCase() == "mso")
									removeAttribute("class");
						};
					};
				};
				
				with (items = ydrBrowser.xml.selectNodes(doc, "//@style"))
				{
					var changed;
					for (i=0; i<length; i++)
					{
						with (items[i])
						{
							styleText =  nodeValue.split(/\s*;\s*/);
							changed=false;
							for (var j=0; j<styleText.length; j++)
							{
								if (styleText[j].search(/mso-/)==0)
								{
									styleText[j] = null;
									changed=true;
								};
							};
							if (changed)
								nodeValue=styleText.join(";").replace(/;{2,}/,';');
						};
					};
				};
			};
						
			with (items = ydrBrowser.xml.selectNodes(doc, "//*[@style]"))  // was //.[@style]" 20100218
			{
				for (i=0; i<length; i++)
				{
					with (items[i])
					{
						try
						{
							styleText=getAttribute("style").trim().split(/\s*;\s/)
							
							for (j=0; j<styleText.length; j++)
							{
								if (styleText[i].substr(0,4) == "mso-")
									styleText[i]=null;
							};					
						}
						catch (e) {};
					};
				};
			};
			
			// imagedata is found in office documents and represents a non standard way of showing images specific to MS Office 
			// turn these into standard pictues
			
			var replacement;
			var items;
			with (items = ydrBrowser.xml.selectNodes(doc, "//imagedata"))
			{
				for (i=length-1; i>=0; i--)
				{
					with (items[i])
					{
						replacement =  doc.ownerDocument.createElement("img");
						replacement.setAttribute("src", getAttribute("src"));
						try { replacement.setAttribute("alt", getAttribute("o:title")) } catch (e) {};
						try {replacement.setAttribute("style", parentNode.getAttribute("style"))} catch (e) {};
						
						parentNode.parentNode.replaceChild(replacement, parentNode);
					};
				};
			};
			
			with (items = ydrBrowser.xml.selectNodes(doc, "//shapetype"))
			{
				for (i=length-1; i>=0; i--)
				{
					items[i].parentNode.removeChild(items[i]);
				};
			};			
		};
		
		if (this.changeFont2Style)
		{
			var changeableFonts=ydrBrowser.xml.selectNodes("//font");
			for (i=changeableFonts.length-1; i>=0; i--)
			{
				fontElement=changeableFonts[i];
				
				if (fontElement.xml == "<font/>" || fontElement.xml  == "<font></font>")
					fontElement.parentNode.removeChild(fontElement);
				else if ((fontElement.attributes.length == 1) && (fontElement.attributes[0].nodeName == "style"))
				{
					newElement = fontElement.ownerDocument.createElement("span");
					newElement.setAttribute("style", fontElement.getAttribute("style"));
					for (j=fontElement.childNodes.length-1; j>=0; j--)
					{
						newElement.appendChild(fontElement.childNodes[j])
					};
					
					fontElement.parentNode.insertBefore(newElement, fontElement);
					fontElement.parentNode.removeChild(fontElement);
				};
			};	
		};
		
		if (this.removeEmptySpan)
		{
			var removableSpans=ydrBrowser.xml.selectNodes(doc, "//span[not(@style)]");
			for (i=removableSpans.length-1; i>=0; i--)
			{
				spanElement=removableSpans[i];
				
				if (spanElement.xml == "<span/>" || spanElement.xml == "<span></span>")
					spanElement.parentNode.removeChild(spanElement);
				else if (spanElement.attributes.length == 0)
				{
					switch (spanElement.childNodes.length )
					{
						case 0 :
							spanElement.parentNode.removeChild(spanElement);
							break;
							
						case 1 :
							if (spanElement.firstChild.nodeType == "#text")
								if (spanElement.firstChild.text == "")
								{
									spanElement.parentNode.removeChild(spanElement);
								}
							else
								try
								{
									spanElement.parentNode.insertBefore(spanElement.firstChild, spanElement);
									spanElement.parentNode.removeChild(spanElement)
								} catch (e) {};
							
							break;
					};
				};	
			};
		};

		// make sure this is not really an empty item - including heaps of hard spaces
		// cant do this - some valid xhtml has just input/select elements and these have no text 
		//stop();
		//if (doc.text.replace(/\s*/g,"").replace(/\xA0*/g,"") == "")
		//	return ""
		//else
		{
			// some systems paste with hard spaces which makes line folding a mess
			result=ydrBrowser.xml.xml(doc).replace(/(\w) (\w)/g,"$1 $2");
			
			// phase 2 prune out further junk
			if (this.removeAsp)
				result=result.replace(/<%.*?%>/g," ");  // removes all <% .... %>

			if (this.removeNamespace)
				result=result.replace(/<\?.*?\?>/g," ");  // removes all <? .... ?>
				
			if (this.removeScript)
				result=result.replace(/<script .*?\/script>/g," "); // removes all <script .. /script>
				
			if (this.removeComment)
				result=result.replace(/<!-- .*?-->/g," "); // remove all <!-- .... -->
		
			if (this.removeColonisedElements)
			{
				result = result.replace(/<\w+:.*?>/g," ") /* removes opening tags */
					.replace(/<\/\w+:.*?>/g," "); // removes closing tags
			};
			
			if (this.removeColonisedAttributes) /* removes xxx:yyy="zzzz" */
				result = result.replace(/ \w+:\w+=".*?"/g,"")
						
			return result
				.replace(/\<p\/>/g,"")
				.replace(/\<span\/>/g,"")
				.replace(/\<font\/>/g,"")
				.replace(/\<div\/>/g,"")
				.replace (/(\/>|>)/," "+ydrNamespace+" $1")
				;
		};
	};			
}

//stop();
var ydrClientPane = new function()
{
	var m_expanders;
	var m_expanderItems;
	
	// used only for transitional pages
		var lastBodyWidth=0;
		var lastBodyHeight=0;

	var m_PaneStack=new Array();
	var intervalId;
	var m_suppressExpander = false;
	
	// events
	this.printPre;
	this.printPost;
	
	// carried
	this.overlayArea; // the element where the overlays are put - if allowed as a global according to page
	
	// ours	
	this.currentPane = function() { return m_PaneStack[m_PaneStack.length-1] }
	this.currentPanePush = function(nextPane) { return m_PaneStack.push(nextPane) }
	this.currentPanePop = function() { try {return m_PaneStack.pop()} catch (e) { return null} }
	
	this.lastSelectedItem=null;
	this.lastSelectedItemThreshold=0; // to represent a date valueOf that externals can set to stop  others
		// set by menuclick processor and monitored by fetch and fetchX
	
	// currentPage is the applications opinion of the currentPane (ie majors on menu clicks and explicit markers
	// as set by .fetchX(....."returnPoint")
	// currentPageSet PRESUMES that we are on a complaint PAGE and resets the pane - although a lot faster
	// currentPage it does make a lot of assumptions as to the readiness of the page to accept the pane
	this.currentPageSet = function()
	{
	
	}
	this.currentPage = function()
	{
		// returns a string that is suitable for use as an href (eg in window.location or httpRequest
		// in principle the same as currentPane, but may include variants for when it is not known if the pane's page 
		// is currently on show. 
		
		var invoke;
		var result;
		//stop();
		if (this.lastSelectedItem)
		{
			if (typeof this.lastSelectedItem == "string")
			{
				// if is a string then was set by fetch or fetchX
				result = this.lastSelectedItem
			}
			else
			{
				// if is an object then set by client menu and is an anchor
				result=window.location.href.split(/[\&\?]invoke\=/)[0];
				result+= (ydrClientMenus.lastMenuSelectedItem
						? ((result.indexOf("?")>0) ?"&amp;" :"?") 
							+ "invoke=" + ydrClientMenus.lastMenuSelectedItem.getAttribute("id")
						: "" );		
			};
		} 
		else
		{
			try
			{
				result = "window.location.href=\"" + this.overlayArea.getAttribute("ydr:invoke") +"\"";
			} catch (e) 
			{
				result = ""
			};
		};
		
		return result;
	};
	
	// constructor code implements a this.currentPage() instruction as set on a visit to a previous page 
	// note does not run immediately so as to allow and presets caused by any body.onload() event to fire	
	// used for example by ydrCredentialsPlus.proxy
	//stop();
	
	var invoke = window.location.href.split(/[\&\?]invoke=/)[1];
	
	if (invoke)
	{			
		window.setTimeout(
			(" try{%invoke%} catch (e) { window.setTimeout(\"try{%invoke%} catch (e) {if (gc_rights.indexOf('administrator')<0 ) window.alert('developer problem: autoinvoke failed - check url\\\\n\\\\n" + invoke+"\\\\n\\\\n' + e.message)}\",1000)}")
			.replace(/%invoke%/g,  "ydrBrowser.html.elementClick('" + invoke + "')"), 300)
			// IMPORTANT re apparent duplication in the above
			// this tends to fail if 100 msecs or less (document body not yet available!!!!!) sometimes 
		// even longer - hence the retry
	};
	
	// end constructor code
	
	this.jiggle = function()
	{
		try
		{
			with (document.body)
			{
				style.display='none'; 
				scrollTop=0;
				scrollLeft=0;
			};
				
			window.setTimeout("ydrClientPane.jiggleTimeout()",1);
		} catch (e) {};
	}
	this.jiggleTimeout = function()
	{
		document.body.style.display='block';
	};
	
	function rambleToNextChild(child, canGoDeeper)
	{
		while (child)
		{
			if (child == this.currentPane())
				return null
			else if (canGoDeeper == "canGoDeeper" && child.firstChild)
				child = child.firstChild
			else if (child.nextSibling == null)
			{
				if (child.parentNode.nextSibling)
					child = child.parentNode.nextSibling
				else
					return rambleToNextChild(child.parentNode)
			}
			else 
				child=child.nextSibling
			
			if (child.nodeType == 1 && child.offsetParent == null)
				return child
		};	
		return null;
	}
	this.printCache;
	this.print = function(silent, onExit)
	{//stop();
		if (this.currentPane()  == null)
			window.alert("Sorry I cannot reformat this page for printing")
		else
		{	
			/*// remember		
			printCache = new Object();
			printCache.button = document.getElementById("printableVersion");
			printCache.printContainer = ydrBrowser.html.firstElementChild(document.body);	
			printCache.docElement = ydrBrowser.html.nextElementSibling(printCache.printContainer);
			printCache.docElementDisplay = printCache.docElement.style.display; 
			printCache.bodyStyleWidth = document.body.style.width;
			printCache.silent=silent;
			if (onExit) 
				printCache.onExit=onExit
			else
				printCache.onExit =  this.printPost;
				
			try { this.printPre(this.currentPane()) } catch (e) {};
			*/
			// fetch new content
			var content = ydrBrowser.html.firstElementChild(ydrBrowser.html.xhtml(this.currentPane(), null, "simpleXML"));
			/*
			ydrGenericScripts.XHTMLneutralise(content);		
			
			// set the button to return to normal
			with (content.appendChild(content.ownerDocument.createElement("a")))
			{
				setAttribute("id", "printElementRestore");
				setAttribute("class","button");
				setAttribute("style", "position:absolute; top:1em; right:0; z-index:100")
				setAttribute("onclick","ydrClientPane.printRestore()"); 
				text="X";				
			};
			
			// set new properties
				
			printCache.printContainer.setAttribute("className","printPane");
			
			//printCache.docElement.style.display="none";
			printCache.docElement.style.visibility="hidden";
			*/
			
			var contentXML = ydrBrowser.xml.xml(content).replace(/class=[\"']content2['\"]/g,"");
			//ssprintCache.printContainer.innerHTML = contentXML;
			
			////////////////
			with (ydrClientScripts.XMLHttpRequest())
			{
				open("POST", "%rootDir%/ydr/ydrReflect.asp?action=printCapture".standardMarkups(), false);
				send(contentXML);
				if (responseText.indexOf("class='error'")<0)
					window.open("%rootDir%/ydr/ydrReflect.asp?action=printGenerate".standardMarkups())
				else
					window.alert("Failed to generate print image\n\n" + ydrGenericScripts.gAlert(responseText));
			};			
			///////////////
						
			//ssdocument.body.style.width="900px";  // for A4
			
			/*
			var lastClassName=m_lastPane.getAttribute("className");
			//m_lastPane.className="printPane";
			//m_suppressExpander=true;		
			
			m_lastPane.style.paddingRight="1.5em"
			
			var child=document.body.firstChild;
			while (child)
			{
				child.style.display="none";
				child=child.nextSibling;
			};
			
			m_lastPane.style.display="inherit";
			m_lastPane.style.height="auto";
			
			
			if (m_lastPane.getAttribute("ydr:paginated") != "paginated")
			{		
				var maxPageHeight=800;	
				m_lastPane.setAttribute("ydr:paginated", "paginated")
				
				var thisPageSoFar=0;
				var currentChild=m_lastPane.firstChild;

				//stop();				
				if (m_lastPane.scrollHeight > maxPageHeight)
				{
					while (currentChild)
					{						
						if (currentChild.getAttribute("currentStyle").pageBreakBefore == "always")
						{
							thisPageSoFar = currentChild.scrollHeight;
							currentChild =  rambleToNextChild(currentChild,"canGoDeeper");
						}
						else if (thisPageSoFar + currentChild.scrollHeight > maxPageHeight)
						{
							// need to force start a new page
							// if the current element takes us over the page break and is smaller than 20% of a page then
							// go to the next page otherwise drill into the next inner element
							if (currentChild.scrollHeight>.2*maxPageHeight && currentChild.firstChild)
								currentChild = rambleToNextChild(currentChild,"canGoDeeper");
							else
							{
								currentChild.getAttribute("style").pageBreakBefore = "always";
								thisPageSoFar = 0;
								currentChild = rambleToNextChild(currentChild);
							};
						}
						else
						{	
							// adopt this into current page
							thisPageSoFar += currentChild.scrollHeight;
							currentChild = rambleToNextChild(currentChild);	
						};						
					};
				};
			};
			*/
			//if (! silent)
				//window.alert("Please click the button to the top right to return to normal view");
		}
		return false;
	}
	
	this.printAlert = function()
	{
		window.alert("You are presently in print presentation mode - none of your buttons are operative."
			+"\n\nAfter printing, please close this window and return the main application window where you can continue normally.");
	}
	
	this.printClear = function()
	{ printCache = null; }
	
	//stop();
	this.printRestoreCache = function()
	{
		if ( ! printCache.silent )
		{
			window.alert("You will need to refresh the page if you want to use the Printable Version button again.\n\nNote that "
			+" visiting another pane within this page will not refresh the page");
			// next should not be necessary and doesnt work anyway
			ydrBrowser.html.addEvent(printCache.button, "click", function() {ydrClientPane.print()});  
		};
		
		// so we hide it 
		printCache.button.style.display="none";
		
		if (printCache.stackLastPane)
		{
			printCache.stackLastPane.style.display =printCache.stackLastPaneDisplay;
		}
		
		if (printCache.onExit)
			printCache.onExit();
			
		printCache = null;
	}
	
	this.printRestore = function()
	{
		if (printCache)
		{
			printCache.printContainer.innerHTML = "";
			printCache.printContainer.setAttribute("className","printContainer");
			//printCache.docElement.style.display = printCache.docElementDisplay; 
			printCache.docElement.style.visibility = "inherit"; 
			
			document.body.style.width = printCache.bodyStyleWidth;
			
			// event seems to get lost on IE7 -so reinstate it
			window.setTimeout("ydrClientPane.printRestoreCache();", 1);
		};
	}
	
	this.printFragment = function(element, onExit)
	{
		// does an immediate print of the element which is not considered a full page
		if (typeof element == "string")
			element = document.getElementById(element);
		
		var stackLastPane = this.currentPane();
		this.currentPanePush(element);
		
		this.print("silent" ,onExit);
		
		if (stackLastPane && stackLastPane != element)
		{
			with (stackLastPane.style)
			{
				printCache.stackLastPane = stackLastPane;
				printCache.stackLastPaneDisplay = display;
				display='none';
			};
		};
	
		//window.print(); // not modal hence timeout below otherwise we restore the page before print sees it!
		//window.setTimeout("ydrClientPane.printRestore()",1500);
		//this.currentPanePop();
	}
	
	this.clearInterval = function()
	{
		if (intervalId)
		{
			window.clearInterval(intervalId);
			intervalId = null;
		}
	};
	
	this.paneExpand = function(targetPane, targetHTML, mode, innerHTML)
	{ 
		// make the current pane expand to fill the parent client - and keep it that way as the window resizes
		// the pane must be a first child of body otherwise results are untested
		
		//NOW A MISNOMER - before usign doctype STRICT we needed to manipulate frame sizes - but this is no longer necessary 
		// but we also had some housework functions here and they are now the dominant code
	
		// deal with the innerHTML
	//stop();
	//if (false)
		//contacts.messenger('contactrowsRowId_10','Membership Secretary');
	
		if (mode != "noScroll")
		{// ie bug -- just even looking at scrollTop or scrollLeft (not even changing a value) causes the page to disappear under DOCTYPE = strict
			// if the page is big - does not affect small pages
			//if  ((targetPane.scrollTop>0 || targetPane.scrollLeft>0))
		
			if (mode != "noSave")
			{	
				this.currentPanePush(targetPane);	
				if (m_expanders == null)
				{
					m_expanders =new Object();
					m_expanderItems=  new Array();
					//stop();
					if (false && true) // funny eh - easy to switch on and off for testing by adding /* .. */
						window.alert("pane interval is off")
					else if (intervalId  == null)
						intervalId = window.setInterval("ydrClientPane.interval()", 5000);
				};
			};
			
			try {
				document.getElementById("printableVersion").style.display 
					= ((targetPane && document.body.firstChild.getAttribute("className") == "printContainer") 
						? "inherit" : "none");
			} catch (e) {};
			
			if (targetPane)
			{
				targetPane.scrollTop=0;
				targetPane.scrollLeft=0;
				
				m_expanders[targetPane.id] = targetPane;				
			};
			if (mode != 'preset') 
					window.scrollTo(0,0);				
		};	
	
		if (innerHTML == null  || "'base'push'pop'".indexOf(mode)<0)
		{
			if ("'noSave'noScroll'preset'".indexOf(mode)<0)
			{
				window.alert("ydrClientScripts::ydrClientPane.paneExpand: new call format not yet implemented or mode not set correctly");
				return;
			};
		}
		else
		{	
			try {innerHTML = thisPage.editor(innerHTML) } catch (e) {};
			
			if (typeof innerHTML == "string")
			{
				if (targetHTML.innerHTML != innerHTML)
				{
					targetHTML.innerHTML = innerHTML;
				};
			}		
			else
			{
				// if there is a script it MUST create a single new function to be stored in this.custom
				// that function can have any number of methods and properties
				// it can be called eg <a onclick="ydrClientPane.custom.validate(this)"
				//stop();
				// remember that rootDir replacement cannot occur on the XML object so we have to do it here
				
				this.scriptNode(innerHTML.documentElement);
				targetHTML.innerHTML = ydrBrowser.xml.xml(innerHTML).standardMarkups();					
			}
			//ydrClientPane.jiggle(); ' this does work but is very annoying try for selected pages only
		};

		if (targetHTML == null && this.overlayArea)
			targetHTML = this.overlayArea;

		if (targetHTML && mode != 'preset')
		{
			// note that there is parallel code to this in ydrpopup!adopt()
			if (targetHTML.firstChild)
			{
				var onLoadEvent;
				// second is for historical compatibility only	
				try  {onLoadEvent=targetHTML.firstChild.getAttribute("ydr:onload")}
				catch (e) {};
				
				if (onLoadEvent == null)
					try  {onLoadEvent=targetHTML.firstChild.getAttribute("onload")}
					catch (e) {};				
				
				if (onLoadEvent)
					eval(onLoadEvent);
					
				try
				{	
					var focus = targetHTML.firstChild.getAttribute("ydr:focus");
					if (focus)
						document.getElementById(focus).focus();
				} catch (e) {}
			};
		};
		
		// we need to switch this thingy off when testing other stuff
		//if (m_suppressExpander) return;
		
		// now implement the pane management
		/*var parentPane;
		 try { parentPane = m_expanders[targetPane.id] } catch (e) {} ;
		
		if (targetPane)
		{
			if (false && (parentPane == null || parentPane != m_expanders[targetPane.id]))
			{
				
				targetPane.style.paddingBottom="4em"; // sometimes without this explorer wont scroll the last bit of the last line
				parentPane  = new Object();
				parentPane.height = targetPane.parentNode.clientHeight;
				parentPane.width = targetPane.parentNode.clientWidth;
				parentPane.bodyHeight = document.body.clientHeight;
				parentPane.node = targetPane;
				
				m_expanders[targetPane.id] = parentPane;
				m_expanderItems[m_expanderItems.length]= parentPane;
				
				//targetPane.style.overflow="auto";
			}
			//else if (document.body.clientHeight != parentPane.height || document.body.clientWidth != parentPane.width)
			//{
				//parentPane.height = parentPane.height + document.body.clientHeight - parentPane.bodyHeight;
				//parentPane.bodyHeight = document.body.clientHeight;
			//};
		};
			
		//targetPane.style.height = Math.max(400, parentPane.height - targetPane.offsetTop);
		//targetPane.style.width = Math.max(400, targetPane.parentNode.clientWidth - targetPane.offsetLeft-1);
		*/
	}
		
	// looks in an XHTML (in XML carrier) for a clientpane script and loads it, removing the script from the xml
	this.scriptNode = function(doc)
	{
		this.custom = null; 
			
		var scriptElement = ydrBrowser.xml.selectSingleNode(doc, "//script[@id='ydrClientPane']");
		if (scriptElement)
		{
			try 
			{
				var script = ydrBrowser.xml.text(scriptElement);
				if (script == "")
					script = ydrBrowser.xml.text(scriptElement.firstChild);
				this.custom = eval( script.standardMarkups() );
			}
			catch (e)
			{
				window.alert("ydrClientPane.paneExpand: Cannot load script given with overlay document\n\n" 
					+ e.message);
			};
			scriptElement.parentNode.removeChild(scriptElement);
		};
	};
	
	var intervalMessageNoted = false;
	var emailMismatchNoted = false;
	this.interval = function()
	{	
		// now windows and internet explorer can get their cookies in a mess and it is a bugger
		// we can some get two copies of a cookie with the same name
		// which then means that the user cannot log on - so we check if this is so then we remove the whole cookie
		// then the user will have to log in manually next time (one time only) but does not have a persistent fault
		/*if (document.cookie.split(/userYDR=/).length>1)
			document.cookie = "";*/
			//stop();
	//return;
		var docType="strict";
		var container = document.documentElement;  // for DOCTYPE = strict pages
		if (container.clientWidth == 0)	
		{
			docType = "transitional";
			container=document.body 
		};
	
		// check for the email mismatch id that is set in ydrCredentials.proxy() for administrators
		if (! emailMismatchNoted)
		{
			emailMismatchNoted = true;
			if (document.getElementById("emailMismatch"))
				window.alert("Warning: the expected email mode is not active in menumain/menu.xml[menPlate/security/emailer/@object]");
		};
	
		if (!intervalMessageNoted && container.clientWidth <830)
		{
			intervalMessageNoted = true;
			window.alert("Your browser window is too small to display content well.\n\nPlease use a larger viewing area of at least 600 pixels.");
		};			

		/* this is needed if the page is not of a DOCTYPE strict */
		if (docType !="strict" && (container.clientHeight != lastBodyHeight || container.clientWidth != lastBodyWidth))
		{				
			lastBodyHeight=container.clientHeight;
			lastBodyWidth = container.clientWidth;
			for (var i=0; i<m_expanderItems.length; i++)
				if (m_expanderItems[i].node.style.visibility != "hidden")
					ydrClientPane.paneExpand(m_expanderItems[i].node, "noScroll");
		};		

		// global browserCheckIssued is set along with rootDir in menu boilerplate("head")
		{
			var extra = ""; 
			if ( ! browserCheckIssued )
			{
				browserCheckIssued  =true;
				if (! ydrClientScripts.isIE)
				{	/*window.alert(("Some parts of this site do not operate reliably due to implementation differences in this version of %browser%."
						+"\n\nPlease use Internet Explorer v7 or above."
						+"\n\nIf you do not have Internet Explorer available then please be aware that errors may occur on some pages, some pages will render"
						+" incorrectly, and you may not be able to input data."
						+"\n\nOnce the present implementation is stable under Internet Explorer we will try to address the problems with Netscape/Firefox/Chrome/Safari.  Other browsers are unlikely to be supported in the forseeable future."
						+"\n\nWe are sorry if this limits your ability to use this site but we do not have the resources to address these matters more quickly.  If you would like %browser% supported, please put yourself forward as the volunteer to make the site work with %browser% - it will probably require at least 200 hours work.")
						.replace(/%browser%/g,window.navigator.appName))*/ a=1
				}
				else if (window.navigator.appVersion.search(/MSIE 7|MSIE 8/) < 0)
				{
					browserCheckIssued =false;
					document.body.innerHTML =
					"<div style=width:60%; margin-left:20%; top:10%><h2 class='overlayArea' >Unsupported browser</h2>"
					+"<p class='stop'>I am sorry.&#160;  You are using an early version of Internet Explorer which does not accurately implemented international standards for the web"
						+".  You cannot use this website using this non-standard Microsoft product.</p>"
						+"<p class='info'>Please upgrade to Internet Explorer 8</p>"
						+"<p class='info', or if you have an early"
						+"<p class='warning'>If you have a version of the operating system which Microsoft no longer supports (such as Windows 2000) you cannot perform this"
						+" upgrade because Microsoft would like you to pay for an upgrade of the Operating System to a later version.&#160; May we suggest you consider"
						+" loading Firefox instead which will operate on Windows 2000 and above.  You can download Firefox from <a href='http://www.mozilla-europe.org/en/firefox/'>http://www.mozilla-europe.org/en/firefox/</a>"
						+" for free and be running in less than 5 minutes.</p>"
						+"<p class='info'>You can use your Browser's <span class='emph'>Back</span> to return to your previous page</p></div>";					
				};	
			};		
		};	
	}
}

var ydrBrowser = new function()
{
	// generate browser independent functionality
	// elementClick = clicks an element - but this only works for onclick that is set via HTML rather than addEvent
	// does not pass event properties in the case of non IE so use great care when applying this
	// simulation of some event properties may follow later

	//this.currentStyle  - finds the current style of a document element >>> note this not the style object but the currentStyle object!
	//this.elementClick  - can actually fire any named event on a html document element

	// at present we dont have any browser differences but in case they appear later we have client side cookies in here
	// this can drop down into browser dependent code without impact to the calling code
	this.cookie = new function()
	{
		this.getItem = function(itemId)
		{
			var cookieArr = document.cookie.split(/;/);
			
			for (var i=0; i<cookieArr.length; i++)
				if (cookieArr[i].split(/=/)[0].trim() == itemId)
					 return unescape(cookieArr[i].split(/=/)[1].trim());
			
			return "";
		}
		
		this.setItem =  function(itemId, newValue)
		{		
			document.cookie=itemId + "=" +escape(newValue);
		};
	}

	if (ydrClientScripts.isIE)
	{		
		this.html =  new function()
		{
			this.addEvent = function(element,eventName,eventFunction)
				{ element.attachEvent("on"+eventName, eventFunction) }		
	
			this.currentStyle = function (element, styleName) {	return element.currentStyle[styleName] };
			this.elementClick = function(element, action) 
			{ 
				if (action == null)
					action="click";
				
				//alert(element.toString())
					
				if (typeof element == "string")
					element = document.getElementById(element); 
				//alert(element.toString())
				
				switch (action)
				{
					case "click" :
						return element.click() ;
					case "change" :
						return element.click() ; // not operative - need to dispatch event							
				};
			};	
				
			this.eventElement= function() { return window.event.srcElement } ;
			
			this.elementValue = function(element, value) 
			{
				 if (value)
					 element.setAttribute("value", value); 
				value = element.getAttribute("value") 
				// following needed for IE 8 onwards
				if (value == null)
					value=element.value;
				return value;
			}
			this.elementValueCopy = function (fromElement, toElement) 	{	this.elementValue(toElement, this.elementValue(fromElement)); 	}

			// the combo of select input and textarea elements within this form
			this.formElements = function(element)
			{
				// with IE a default property of a form element is an array of the input select textarea elements in the form
				if (element.nodeName.toLowerCase() == "form")
					return element
				else
					return new Array();
			};
			
			this.xhtml = function(element, value, simpleXML) {if (value) { element.innerHTML = value } else {return ydrClientScripts.xhtml(element, simpleXML)}	};
			
			this.text = function(element, value) {if (value) element.innerText=value; return element.innerText};
			
			// these 5 functions are now W3C standard but do not appear in earlier browsers
			// in this case the code is the same for all browsers but it should be noted that IE provides a different childNodes list to other browsers
			// the code anticipates browsers implementing this standard and goes all the faster as a result
			this.firstElementChild = function(element) 
			{
				var result = element.firstChild;		
				if (result && result.nodeType != 1)
					 result = this.nextElementSibling(result);
				return result;
			};
			this.lastElementChild = function(element) 
			{
				var result = element.lastChild;		
				if (result && result.nodeType != 1)
					result = this.previousElementSibling(result);
				return result;
			};
			this.previousElementSibling = function(element) 
			{
				var result = element.previousSibling;
				if (result && result.nodeType != 1) 
					result = this.previousElementSibling(result);
				return result; 
			};
			this.nextElementSibling = function(element) 
			{
				var result = element.nextSibling;
				if (result && result.nodeType != 1)
					result = this.nextElementSibling(result);
				return result;
			};
			this.childElements = function(element)
			{
				var list = new Array();
				var childNodes = element.childNodes;
				
				for (var i=0; i<childNodes.length; i++)
					if (childNodes[i].nodeType == 1)
						list.push(childNodes[i]);
				return list;
			};
		}
		
		this.xml = new function()
		{
			this.document =  function(textXML)
			{
				try {xmlDoc = new ActiveXObject("MSXML2.DOMDocument."+menuCore.menuSource.serverDOMversion) }
					catch (e) { try { xmlDoc = new ActiveXObject("MSXML2.DOMDocument") }
						catch (e) {xmlDoc = new ActiveXObject("Microsoft.XMLDOM") }};
					
				if (textXML && textXML.length>0)
				{
					xmlDoc.async="false";
					xmlDoc.loadXML(textXML);
					
					if (ydrBrowser.xml.parseError(xmlDoc))
					{
						xmlDoc.loadXML(textXML.replace(/([ \/\>])/, " %ydrNamespace% $1").standardMarkups());
						if (ydrBrowser.xml.parseError(xmlDoc) && gc_rights.indexOf('developer')>0)
						{
							alert("ydrClientScripts.XMLdocument.loadXML: parse error: " +ydrBrowser.xml.parseError(xmlDoc) + "\n\n" + textXML);
							throw new Error ("parseError");
						}
					};
				};
				return xmlDoc;
			}
			
			this.parseError = function(xmlDoc)
			{
				with (xmlDoc.parseError)
				{
				  if (errorCode != 0)
					return "<p class='code'>"
						+ "Error [" +errorCode +"] at line " + line + " position " + linepos
						+ "<br />" + ydrClientScripts.HTMLencode(reason)
						+"</p>"
				};
			}
			
			this.getElementById = function(xmlDoc, id) { return xmlDoc.selectSingleNode("*[@id='"+id+"']") };
			this.selectSingleNode = function(xmlDoc, xpath)	{	return xmlDoc.selectSingleNode(xpath) }
			this.selectNodes = function(xmlDoc, xpath)	{	return xmlDoc.selectNodes(xpath) }
			this.text= function(xmlDoc) {return xmlDoc.text};
			this.xml = function(xmlDoc) { return xmlDoc.xml };
			this.appendChild = function(fromNode, toNode, copy) 
			{ 
				if (copy == "copy") fromNode = fromNode.cloneNode(true);
				toNode.appendChild(fromNode);
			};
		};			
	}
	else /* is Chrome and Firefox */
	{		
		this.html =  new function()
		{
			/*this.addEvent = function(element,eventName,eventFunction)
				{alert ("Firefox addevent " + eventName +"_"+element.getAttribute("id")+"_"+typeof eventFunction); element.addEventListener(eventName, eventFunction, false)};*/
		
			this.addEvent = function(element,eventName,eventFunction)
			{
				//element.setAttribute("on"+eventName, eventFunction);
				//eval("document.getElementById('" + element.getAttribute("id") + "').on" + eventName +" = eventFunction")
				try {
					eval(" element.on" + eventName +" = eventFunction")
				}
				catch (e)
				{
					window.alert("failed to register event " + eventName);
				};
			};
				
			this.currentStyle = function(element, styleName)
					 {return document.defaultView.getComputedStyle(element, "")[styleName] };		
			
			this.elementClick = function(element, eventName)
			{
				if (eventName==null)
					eventName = "click";
					
				if (typeof element == "string")
					element = document.getElementById(element); 
				
				var clickevent=document.createEvent("MouseEvents");
				clickevent.initEvent(eventName, true, true);
				return element.dispatchEvent(clickevent);
			};
			
			this.eventElement= function() { return window.event.srcElement } ;
	
			this.elementValue = function(element, value) 
			{
				if (value)
				{
					element.value = value;
					element.setAttribute("value", value); // in chrome these are NOT the same 
						// the .value gets updated from the user input but does not appear in the outerHTML
						// the .setAttribute can hold a completely different value
				}
				else 
				{
					element.setAttribute("value", element.value)  // these are separate values in Chrome !!!!!!!!!
				};
				
				if (element.value)
					return element.value
				else
					return "";
			};
			
			this.elementValueCopy = function (fromElement, toElement) 	{	this.elementValue(toElement, this.elementValue(fromElement));	}
	
			// the combo of select input and textarea elements within this form
			this.formElements = function(element)
			{
				var result = new Array();
				
				function formElementsDo(node)
				{
					//alert(node.nodeName.toLowerCase());
					switch (node.nodeName.toLowerCase())
					{
						case "input" :
						case "select" :
						case "textarea" :
							result.push(node);
							break;
							
						default :					
							for (var i=0; i<node.childNodes.length; i++)
							{
								if (node.nodeType == 1)
									formElementsDo(node.childNodes[i]);							
							};
					};
				};
				
				if (element.nodeName.toLowerCase() == "form")												
					formElementsDo(element);
				
				return result;
			};
			
			// note this is same as for IE but gives option if we have to make changes later
			this.xhtml = function(element, value, simpleXML) {if (value) { element.innerHTML = value } else {return ydrClientScripts.xhtml(element, simpleXML)}	};
			// this.xhtml = function(element, value) {if (value) {element.innerHTML = value} else {return element.innerHTML }	};
			
			this.text = function(element, value) {if (value) element.textContent = value; return element.textContent}
			
			// these 5 functions are now W3C standard but do not appear in earlier browsers
			// in this case the code is the same for all browsers but it should be noted that IE provides a different childNodes list to other browsers
			// the code anticipates browsers implementing this standard and goes all the faster as a result
			this.firstElementChild = function(element) 
			{
				var result;  try {result = element.firstElementChild} 
				catch (e) 
				{ 
					result = element.firstChild;		
					if (result && result.nodeType != 1)
						result = this.nextElementSibling(result);
				} 
				return result;
			};
			this.lastElementChild = function(element) 
			{
				var result;  try {result = element.lastElementChild} 
				catch (e) 
				{ 
					result = element.lastChild;		
					if (result && result.nodeType != 1)
						result = this.previousElementSibling(result);
				} 
				return result;
			};
			this.previousElementSibling = function(element) 
			{
				var result;  
				try {result =  element.previousElementSibling} 
				catch (e) 
				{
					result = element.previousSibling;
					if (result && result.nodeType != 1)
						result = this.previousElementSibling(result);
				}; 
				return result;
			};
			this.nextElementSibling = function(element) 
			{
				var result;  
				try {result =  element.nextElementSibling} 
				catch (e) 
				{
					result = element.nextSibling;
					if (result && result.nodeType != 1)
						result = this.nextElementSibling(result);
				}; 
				return result;
			};
			
			this.childElements = function(element)
			{
				var list = new Array();
				var childNodes = element.childNodes;
				
				for (var i=0; i<childNodes.length; i++)
					if (childNodes[i].nodeType == 1)
						list.push(childNodes[i]);
			
				return list;
			};
		}
		
		this.xml = new function()
		{
			this.document = function(textXML)
			{					
				var result;
				if (textXML && textXML.length>0)
				{
					var xmlDoc =new DOMParser().parseFromString( textXML ,"application/xml")
					
					if (ydrBrowser.xml.parseError(xmlDoc))
					{
						alert("ydrClientScripts.XMLdocument.loadXML: parse error: " +ydrBrowser.xml.parseError(xmlDoc) + "\n\n" + textXML);
						throw new Error ("parseError");
					};
					
					result= xmlDoc;
				}
				else
					result = document.implementation.createDocument("", "" , null);		
			
				return result;
			}
			
			this.parseError = function(xmlDoc)
			{
				if (xmlDoc.documentElement.nodeName=="parsererror")
					return xmlDoc.documentElement.childNodes[0].nodeValue
			}
			
			this.getElementById = function(xmlDoc, id) { return ydrBrowser.xml.selectSingleNode(xmlDoc, "*[@id='"+id+"']") };
			this.selectSingleNode = function(xmlDoc, xpath){	try {return xmlDoc.ownerDocument.evaluate(xpath, xmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue } catch (e) {window.alert(e.message)}}
			this.selectNodes = function(xmlDoc, xpath) 	
			{	
				// we have to do this to preserve a common way of referencing the results (IE7+ supports list[i] form of access)
				var list = new Array();
				with (xmlDoc.ownerDocument.evaluate(xpath, xmlDoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)	)
				{
					for (i=0; i<snapshotLength; i++)
						list[i] = snapshotItem(i);
				};
				return list
			}
			this.text= function(xmlDoc) {return xmlDoc.textContent};
			this.xml = function (xmlDoc) {return new XMLSerializer().serializeToString(xmlDoc) };
			this.appendChild = function(fromNode, toNode, copy) { // used since some browsers dont support appending from another document 
				// warning if not using COPY == true or copy = "copy" then the fromNode will differ between browsers afterwards
				if (copy == "copy") fromNode = fromNode.cloneNode(true);
					
				try {toNode.appendChild(fromNode.cloneNode(copy)) } 
				catch (e) 
				{
					alert ("failing xml append child")
				};	
			
			return false }
		}
	};
}
