﻿var ydrClientTextEdit = new function () 
{		
	var oW; // ydrTextEdit contentWindow
	var trail=""; // DOM path from elementSelected upwards
	var m_this=this; 
	
	this.hasBeenInitialised = false;

	// sourceElement is the documentElement that is source the  textarea
	// editElement is the iframe that holds the target text
	this.init = function (sourceElement, editElement, head)
	{ 
		var innerHTML=ydrBrowser.html.elementValue(sourceElement);
	
		if (innerHTML.charAt(0) != "<")
			innerHTML = ydrClientScripts.HTMLencode(innerHTML,"plain");
		else if (innerHTML.indexOf("<body")>=0)
			innerHTML = innerHTML.replace(/(^.*?>)(.*)(.*<\/body>)/,"$2");		
		
		m_this.hasBeenInitialised=true;  
			
		var startHTML = "<html>\n<head>\n"
			+ head
			+ '</head>\n<body style="background-color:#fffacd" onfocus="ydrClientTextEdit.editDone()"	>\n'
			    +innerHTML
				+'</body>\n</html>';
		
		oW = editElement.contentWindow;
		with (oW.document)
		{
			try{designMode = "on";} catch(e){ setTimeout('oW.designMode = "on";', 100);}
			open();
			write(startHTML);
			close();
		};
		
		if (oW.addEventListener)
			 oW.addEventListener("keypress", kb_handler, true); //keyboard shortcuts for Moz		
		addEvt(oW.document.body,"keyup", selectRangeEvent);
		addEvt(oW.document.body,"click", selectRangeEvent);
		addEvt(oW.document.body,"dblclick", doubleClickEvent);
		addEvt(oW.document.body,"paste", onpaste);
		addEvt(oW.document.body,"mouseup", onpaste);
	};
	
	var cleanseHTMLprevious="";
	this.cleanseHTML = function()
	{	
		if (! ydrClientScripts.isIE) return // other browsers returns sensible xhtml!
		
		// we can get pastes and other changes that leave a badly formed document as a result of browsers implementing legacy standards
		// when cutting and pasting - the result is sillies like <font><p>dfsggs</p><font>.  Sadly we have to leave the font statements in place
		// as these are the means the execCommand infrastructure tracks and changes fonts.  So we have to scan the document and jiggle things	
		// around so that the document valid
		// for similarities in structure see ydrClientScripts.html
		//stop();
		var inlineNodeList="|a|abbr|acronym|b|basefont|bdo|big|br|cite|code|dfn|em|font|i|img|input|kbd|label|q|s|samp|select|small|span|strike|strong|sub|sup|textarea|tt|u|var|";
		var optionalNodeList="|applet|button|del|iframe|ins|map|object|script|";	
		var singleTags="|basefont|br|col|img|hr|input|isindex|link|"
		
		var cleanseHTMLthis = oW.document.body.innerHTML;
		if (cleanseHTMLprevious != cleanseHTMLthis)
		{
			var i,j;
			var jiggled = false;
			
			cleanseHTMLprevious = cleanseHTMLthis;
			
			// parse the entire string - we cannot use document objects as they are just too different in bevaviour and the strings returned from 
			// outerHTML are weird - look at tag nesting in this real clip of outerHTML from IE7  
			// what is more - when this prevails navigating by nodes and children doesnt get all the document
			//<BODY style=\"BACKGROUND-COLOR: #fffacd\" onfocus=ydrClientTextEdit.editDone()>
				//<FONT color=#0000ff size=1>
					//<FONT color=#0000ff size=1>
						//<P>e</FONT></FONT><FONT size=1>))</P>
						//<P></FONT><FONT color=#0000ff size=1><FONT color=#0000ff size=1>else</P>
					//</FONT>
				//</FONT></BODY>"
				
			// we are gonna make it look like
			//<BODY style=\"BACKGROUND-COLOR: #fffacd\" onfocus=ydrClientTextEdit.editDone()>
				//<DIV color=#0000ff size=1>
					//<DIV color=#0000ff size=1>
						//<P><FONT color=#0000ff size=1><FONT color=#0000ff size=1>e</FONT></FONT><FONT size=1>))</FONT></P>
						//<P><FONT color=#0000ff size=1><FONT color=#0000ff size=1>else</FONT></P>
					//</DIV>
				//</DIV></BODY>"
			
			var cleanseHTMLthisArr = cleanseHTMLthis.split("\<");
			var obj;
			
			function tag(s)
			{
				this.value = function(finish)
				{
					if ( this.tagName == "")
						return ""
					else
					{
						if (this.futureTagname) this.tag[0] = this.tagName;
						this.content[0] = this.tag.join(" ");
						if (this.content.length>1)
							return this.content.join(">" + this.insert )
						else
							return this.content[0] + ">" + this.insert ;
					};
				};	
					
				this.string = s
				//this.changed=false;
				this.content = s.split(/\>/);
				if (this.content.length == 1)
					this.content.push("");
				this.tag = this.content[0].split(/ /);
					if (this.tag.length ==1)
						this.tag.push("");
				this.tagName = this.tag[0] = this.tag[0].toLowerCase();
				if (singleTags.indexOf("|"+this.tagName+"|")>0)
				{
					this.type = "single";
					if (this.tagName.charAt(this.tagName.length-1) != "/") 
					{
						if (this.content.length ==1)	
							this.tagName += "/"
						else
							this.tag[this.tag.length-1] += "/";
					};
				}
				else
					this.type = ((this.tagName.charAt(0) == "/") 
						? "close" 
						: (this.tagName.charAt(this.tagName.length-1) == "/" ? "single" : "open"));
				this.futureTagname = null;
				this.insert="";
			};
			
			for (i=0; i<cleanseHTMLthisArr.length; i++)
				(cleanseHTMLthisArr[i] = new tag(cleanseHTMLthisArr[i] )).index=i;
			
			// check it is all valid
			var stack = new Array(); // of as yet unclosed open tags ignoring singles
			for (i=0; i<cleanseHTMLthisArr.length; i++)
				with (cleanseHTMLthisArr[i])
				{
					if (type =="open")
						stack.push(cleanseHTMLthisArr[i])
					else if (type == "close")
					{
						if (tagName == "/" +stack[stack.length-1].tagName)
						{
							if (stack[stack.length-1].futureTagname)
								futureTagName = stack[stack.length-1];
								
							stack.pop();
						}
						else if (tagName == "/font")
						{
							jiggled = true;
							j = stack.length-1;

							while ( --j > 0 )
							{
								if (stack[j].tagName == "font" && stack[j].futureTagName == null)
								{	
									cleanseHTMLthisArr[i-1].insert += "<" + stack[j].content[0] + ">";
									//stack[j].futureTagname = "div";										
									break;
								};
							};
						}
						else if (tagName == "/p")
						{
							while (true)
							{
								//jiggled=true;
								obj = stack.pop();
								if (obj == null ) 
									break;
									
								if ("/p" == "/" +obj.tagName)
									break;
								else
									cleanseHTMLthisArr[i-1].content[1] += "</" + obj.tagName +">";
							};							
						}
						else if (type == "close")
						{
							// presumed just litter
							tagName = tagName.replace(/\//, "");
							type = "single"; 
							//jiggled = true;
						} 
						else
							alert ("unexpected tag structure delivered into your document by cut and paste or drag and drop");
					};
					a=1; // for debugging
				};
			
			// now put it all back - but it looses the cursor
			if (jiggled)
			{
				for (i=0; i<cleanseHTMLthisArr.length; i++)
					if (cleanseHTMLthisArr[i] )
						cleanseHTMLthisArr[i] = cleanseHTMLthisArr[i].value();
				
				oW.document.body.innerHTML = cleanseHTMLthisArr.join("<");
				oW.document.body.innerHTML = ydrBrowser.html.xhtml(oW.document.body).replace(/\<body.*?\>|\<\/body\>/g,"");
							
				cleanseHTMLprevious = oW.document.body.innerHTML;
			};
		}
		return;
	};
	
	// this is where we need to clean up the imported text
	function onpaste(event) { window.setTimeout("ydrClientTextEdit.cleanseHTML()",1); };
	function addEvt(o,e,f)	{ ydrBrowser.html.addEvent(o,e,f); };

	function doubleClickEvent()
	{
		if (currentElementForForm == null)
		{
			try 
			{
				switch (selectionRangeElement().nodeName.toLowerCase() )
				{
					case 'a' : m_this.linkEdit(); break;
					case 'img'	: m_this.imageEdit(); break;
					case "td" :
					case "tr" :
					case "tbody" :
					case "th" :
					case "thead" :
					case "table" :
						 m_this.tableEdit(true); break;
				};
			} catch (e) {};
		};
	};	

	this.doSelect = function(selectNode, selectDetails) 
	{  
		//select on toolbar used - do it
		// selectNode is usually a tag eg <H2> - causes the currently selected text to adopt the tag
		// if selectDetails are present then this is the tag name and the selectNode is a style attribute string		m_this.editDone();

		var selected;
		if (typeof(selectNode) == "string")
			selected = selectNode
		else
			selected= ydrBrowser.html.elementValue(selectNode.options[selectNode.selectedIndex]);
			
		selectNode.selectedIndex = 0; // revert to showing the topic entry
		
		if (selectDetails)
		{
			insertHTML(null, selectDetails, selected);
		}
		else
			insertHTML(null, selected,"");
	}	// set the viewing colour from the pallete within the colour editor
	this.vC = function (colour) 
	{ 
		if (colour == null || ! colour) 
			colour = ydrBrowser.html.elementValue(document.getElementById('ydrTextEdit_refColour'));
			
		document.getElementById('ydrTextEdit_colourNewPanel').style.backgroundColor = colour;
		ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_colourNew"), colour);
	}
	// generalised pre post and cancel functions for edit forms
	// pre and post to be called at the start of each specific
	// cancel to be called direct from form button

	var editTemporaryElement;
	var editBookmarkedElement;
	var editBookmarkedSelection;
	var currentElementForForm;
	function editPre(tagName, elementForForm)
	{
		// call before any form is displayed that needs a cursor point 
		// make sure we have a usable insertion/action point - empty selections replaced with a temporary anchor		
		if (currentElementForForm != null)
				m_this.editDone();
				
		// active the validations for the fields in the selected form
		var elementName;
		var items;
		with (items = ydrClientEdit_popup.activeElements())
		{
			for (i=0; i<length; i++)
			{
				with (items[i])
				{
					if (getAttribute("id").indexOf(elementForForm)==0)
						removeAttribute("ydr:disabled");
				};
			};	
		};

		// remember where we are - going to the dialog forms looses the current selection in the browser
		editBookmarkedSelection= selectionRange();
		var rangeElement = selectionRangeElement();

		editTemporaryElement = false;
		try
		{
			if (rangeElement && rangeElement.nodeName.toLowerCase() == tagName)
				editBookmarkedElement = rangeElement
			else
			{
				if (oW.document.selection.type=="None")
				{
					editBookmarkedSelection.select();
					
					if (tagName.length==0) return
					
					editBookmarkedElement = insertHTML(null,tagName,"");
					editTemporaryElement = true;
				}
				else
				{					
					if (rangeElement.outerHTML == undefined || rangeElement.nodeName.toLowerCase() != tagName)
					{
						editBookmarkedSelection.select();
						
						if (tagName.length==0) return
						
						editBookmarkedElement = insertHTML(null,tagName,"");
						editTemporaryElement=true;
					}
					else
						editBookmarkedElement = rangeElement;
				}
			};
			
			currentElementForForm = document.getElementById(elementForForm);
			currentElementForForm.style.visibility="inherit"; 
		}
		catch (e)
		{ 
			alert ("Please place the cursor at a location to save the insertion"); 
			return true;
		};
	}

	// return true is OK to close the current editform - dont call for cancels
	function editConfirm()
	{
		if (ydrClientEdit_popup.advisory().length==0)
			return true
		else
		{
			ydrClientEdit_popup.validate();
			if (ydrClientEdit_popup.advisory().length==0)
				return true;
		
			window.alert ("Please fix the validation errors that are listed at the bottom of the page"); 
			return false;
		};
	}

	function editPost(tagName)
	{
		var elementSelected;
		if (editBookmarkedElement)
			elementSelected =  editBookmarkedElement
		else
			elementSelected =selectionRangeElement();
			
		if (elementSelected == null)
			elementSelected = oW.document.body.appendChild(oW.document.createElement(tagName))
		else if ((elementSelected.nodeName.toLowerCase() != tagName)
			|| (elementSelected.nodeName.toLowerCase() == "td" && tagName=="table"))
		{
			elementSelected = insertHTML(editBookmarkedElement,tagName,"");
		};
		
		currentElementForForm.style.visibility="hidden"; 	
		currentElementForForm = null;
		m_this.editDone();
		
		return elementSelected;
	}

	// like in ydrExclaimListClient
	//do on a timeout as the value has not been set when validate was called	
	// but remember that this page might have disappeared
	// also remember that if ydrExclaimListClient is also loaded then it too is using this advisory	
	
	this.showAdvisory = function(target) 
	{
		if (target)
		{ 
			target.innerHTML = "<div style='background-color:#ffe4e1'>"+ydrClientEdit_popup.advisory()+"</div>";		
			ydrClientEdit_popup.advisoryHide();
		};	
	};

	this.editDone = function()
	{
		var items; 
		
		oW.focus();
		
		// make sure all field validations are disabled
		with (items = ydrClientEdit_popup.activeElements())
		{
			for (i=0; i<length; i++)
				items[i].setAttribute("ydr:disabled", "disabled");
		};
		
		ydrClientEdit_popup.advisory("");
	
		//if called with currentElementForForm non null then force a cancel 
		if (currentElementForForm)
		{
			if (editTemporaryElement)
				try {editBookmarkedElement.outerHTML=""} catch (e) {};
					
			if (editBookmarkedSelection)
				editBookmarkedSelection.select();
			
			currentElementForForm.style.visibility="hidden"; 		
		};
		
		// clear out values - ready for next time
		editTemporaryElement = null;
		editBookmarkedElement = null;
		editBookmarkedSelection = null;
		currentElementForForm = null;
	};

	var tableEditExisting;
	this.tableEdit = function(editingExisting)
	{
		if (trail.substr(trail.length-1) == "a")
		{
			alert ("Cannot insert table within the name of a link");
			return
		}
		
		var elementSelected = selectionRangeElement();
		var tableNode;
		tableEditExisting = editingExisting;
		
		if (editingExisting)	
		{
			ydrBrowser.html.text(document.getElementById("ydrTextEdit_tableCaption"), "Modify existing Table");
			if (elementSelected.nodeName.toLowerCase()=="td")
			{
				tableNode = elementSelected.parentNode.parentNode.parentNode;
				editBookmarkedElement = tableNode
			}
			else
				tableNode = elementSelected;
			document.getElementById("ydrTextEdit_table").style.visibility="inherit"; 
		}
		else
		{
			tableNode = elementSelected;
			if (editPre("table", "ydrTextEdit_table")) return;
	
			ydrBrowser.html.text(document.getElementById("ydrTextEdit_tableCaption"), "Insert Table");
		};
			
		if (editingExisting ) 
		{
			if (tableNode.nodeName.toLowerCase() == 'table' ) 
			{
				ydrBrowser.html.text(document.getElementById("ydrTextEdit_tableCaption"), "Modify Table");
				
				{
					var topic;
					topic=tableNode.firstChild;
					while (topic && topic.nodeName.toLowerCase()!="thead")
						topic=topic.nextSibling;
					try { ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_tableHeadingRows"),  topic.childNodes.length);} catch (e){};
					
					while (topic && topic.nodeName.toLowerCase()!="tbody")
						topic=topic.nextSibling;
						
					try { ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_tableRows"), topic.childNodes.length);} catch (e){};
					try { ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_tableColumns"), topic.firstChild.childNodes.length);} catch (e){};
					
					try { document.getElementById("ydrTextEdit_tableClass"+tableNode.className).selected = "selected" } catch (e){};
				};
			};
		};
	}	
	
	this.tableApply = function() 
	{	
		var elementSelected;
		
		if (! editConfirm()) 	return;
		
		if (tableEditExisting )
		{
			elementSelected = editBookmarkedElement;
			document.getElementById("ydrTextEdit_table").style.visibility="hidden"; 	

			tableEditExisting = false
			currentElementForForm == null;
			m_this.editDone();
		}
		else
			elementSelected = editPost("table");
			
		// note the form is modal so nothing can have changed elementSelected from last time
		if (elementSelected == null) return; // rare timing funnies cause this
		//if (elementSelected.nodeName.toLowerCase() != "table") stop;
		
		with (document.getElementById("ydrTextEdit_tableClass"))
		{
			elementSelected.className = ydrBrowser.html.elementValue(targetI)(children[selectedIndex]);
		};
		
		var rows  = Math.max(1,  ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_tableRows")));
		var headingRows  = Math.max(0,  ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_tableHeadingRows")));
		var cols = Math.max(1,  ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_tableColumns")));
		
		if (elementSelected.firstChild != null )
		{
			elementSelected.firstChild.nodeName != "thead"
			while (elementSelected.firstChild)
				elementSelected.removeChild(elementSelected.firstChild)
		};
		
		if (elementSelected.firstChild == null )
		{
			elementSelected.appendChild(oW.document.createElement("thead"))
			elementSelected.appendChild(oW.document.createElement("tbody"))
		};
		
		var head = elementSelected.firstChild
		var body = head.nextSibling;
		
		tableConform(head, headingRows, cols);
		tableConform(body, rows, cols)
	}
	
	function tableConform(node, rows, columns)
	{
		// make the table have the given rows and columns
		var i;
		var j;
		var delta = rows -  node.childNodes.length;
		
		// make sure we have the right number of rows
		if (delta !=0)
			if (delta>0)
			{
				for (i=0; i<delta; i++)
				{
					node.appendChild(oW.document.createElement("tr"))
				};
			}
			else
			{
				for (i=0; i<-delta; i++)
				{
					node.removeChild(node.lastChild)
				}
			};	
	
		//make sure each row has the right number of columns
		var child;
		for (i=0; i<node.childNodes.length; i++)
		{
			child=node.childNodes[i];
			delta = columns - child.childNodes.length;
			if (delta !=0)
			{
				if (delta>0)
				{
					for (j=0; j<delta; j++)
					{
						child.appendChild(oW.document.createElement("td")).innerHTML=node.nodeName.toLowerCase().substr(1);
					};
				}
				else
				{
					for (j=0; j<-delta; j++)
					{
						node.deleteChild(node.lastChild)
					}
				};		
			}
		}
	}
	
	// specifics for the http link edit form
	this.linkEdit=function()
	{
		var initialSelection=selectionRange().htmlText;		
			
		if (editPre("a", "ydrTextEdit_link")) return;
						
		ydrBrowser.html.text(document.getElementById("ydrTextEdit_linkCaption"), "Insert Link");
		
		if (editBookmarkedElement == null) return;
		if (editBookmarkedElement.nodeName.toLowerCase() != 'a')
		{
			if (initialSelection.length>0)
			{
				ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_linkText"), initialSelection);
			};
		}
		else 
		{
			ydrBrowser.html.text(document.getElementById("ydrTextEdit_linkCaption"), "Modify Link");
			var urlS = editBookmarkedElement.href.split("//");
			switch (urlS[0])
			{
				case "HTTP" :
				case "HTTPS" :
				case "MAILTO" :
				case "CALLTO" :	
					break;

				default : 
					urlS[0]="http";
			} 
			ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_linkURL"),  urlS.join(":"));
			document.getElementById("ydrTextEdit_link"+urlS[0]).selected = "selected";
			
			var linkText = (ydrBrowser.html.text(editBookmarkedElement) == "") ? "" : editBookmarkedElement.innerHTML;
			
			ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_linkText"),  
				(ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_linkURL")) == linkText) 
					? "" : linkText.replace(/<\{0,1}p>/ig,""));

			if (editBookmarkedElement.onclick == null)
			{
				switch ( editBookmarkedElement.target)
				{
					case null :
						document.getElementById("ydrTextEdit_linkTextSame").selected = "selected"; break;
					
					case "_blank" :
						document.getElementById("ydrTextEdit_linkTextNewPage").selected = "selected"; break;
				}
			}
			else
				document.getElementById("ydrTextEdit_linkNewWindow").selected = "selected";
		};
	};	
	
	this.linkApply = function() 
	{
		if (! editConfirm()) 	return;
		
		var elementSelected = editPost("a");
		
		if (elementSelected == null) return; // rare timing funnies cause this
		
		// note the form is modal so nothing can have changed elementSelected from last time		
		with (document.getElementById("ydrTextEdit_linkHTTP").parentNode)
		{
			elementSelected.href= ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_linkURL"));
		};
		
		with (document.getElementById("ydrTextEdit_linkSame").parentNode)
		{	
			switch (ydrBrowser.html.elementValue(childNodes[selectedIndex]))
			{
				case "same" :
					elementSelected.target = null; 
					elementSelected.onclick="";
					break;
					
				case "newPage" :
					elementSelected.target="_blank";
					elementSelected.onclick="";
					break; 
					
				case "newWindow" :
					elementSelected.target="";
					elementSelected.onclick="window.open(\""+elementSelected.href+"\"); return false";
					break; 
			};
		};
		 		
		var linkText = ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_linkText"));
		if (linkText == "")
			elementSelected.innerHTML= elementSelected.href
		else
			elementSelected.innerHTML= linkText;
			
	};	
	
	this.imageEdit = function()
	{
		var initialSelection=selectionRange().htmlText;		
		
		if (editPre("img", "ydrTextEdit_Image")) return;
		
		if (editBookmarkedElement == null) return;
		if (editBookmarkedElement.nodeName.toLowerCase() != 'img')
		{
			if (initialSelection && initialSelection.length>0)
				ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageAlt"), initialSelection);
		}
		else 
		{	
			if (editBookmarkedElement.nodeName.toLowerCase() == 'img')
			{
				ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageURL"),  editBookmarkedElement.getAttribute("src"));
				ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageAlt"), editBookmarkedElement.getAttribute("alt"));
				document.getElementById("ydrTextEdit_ImageAlign").selectedIndex=(editBookmarkedElement.align=="left")?1:(editBookmarkedElement.align=="right")?2:0; 
				ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageBorder"), editBookmarkedElement.style.border?	
						editBookmarkedElement.style.border:editBookmarkedElement.border>0?editBookmarkedElement.border:0);
				ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageMargin"), editBookmarkedElement.style.margin?
						editBookmarkedElement.style.margin:editBookmarkedElement.hspace>0?editBookmarkedElement.hspace:0);
			}			
		}
	};
	
	this.imageApply = function() 
	{ 	
		if (! editConfirm()) 	return;
		
		var style="";	
		var elementSelected = editPost("img");
		
		if (elementSelected == null) return; // rare timing funnies cause this
		
		try {
		elementSelected.setAttribute("src", ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageURL")));
		} catch (e) {window.alert("Invalid content in URL string discarded") };
		elementSelected.setAttribute("alt", ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageAlt")) 
			? ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageAlt"))
			: ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_linkURL")).replace(/.*\/(.+)\.*/,"$1") );		
		
		var side=ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageAlign"));
			if ((side == "left") || (side == "right")) 
				style+=  'text-align:' + side;
			
		var border=ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageBorder"));
		if (border)
		{
			if (border.match(/^\d+$/)) 
				border+='px solid';
			style += " border:"+border;
		};
		
		var margin=ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_ImageMargin"));
		if (margin)
		{
			if (margin.match(/^\d+$/)) 
				margin+='px';
			style += " margin:"+margin
		};	
		
		elementSelected.style.cssText=style;
	};

	var colourModeCSS;
	var colourModeExec;
	this.colourEdit = function(mode)
	{
		with (document.getElementById("ydrTextEdit_palette"))
		{
			if (childNodes.length== 0)
			{
				var r = 0, g = 0, b = 0;		
				var colour = '';
				var numberList = new Array('00', '40', '80', 'BF', 'FF');
				var palette = "<table style='padding:0; margin:0'>"
					+"<colgroup><col span='25' style='width:2em; height:2em' />"
					+"</colgroup>";

				for (r = 0; r < 5; r++)
				{
					palette += '<tr>';

					for (g = 0; g < 5; g++)
					{
						for (b = 0; b < 5; b++)
						{
							colour = numberList[r] + numberList[g] + numberList[b];
							palette += "<td style='width:2em; height:1.5em; background-color:#" + colour 
								+ "' onclick='ydrClientTextEdit.vC(\"#"
								+ colour + "\")'>&#160;</td>";	
						}
					}

					palette += "</tr>";
				}
				palette += "</table>";
		
				innerHTML = palette;				
			}
		}		
		
		editBookmarkedSelection = selectionRange();
		
		if (currentElementForForm != null)
			m_this.editDone();
				
		var colourModeIE= mode.replace(/-c/,"C");
		colourModeCSS= mode;
		
		switch (mode)
		{
			case "color":
				colourModeExec="ForeColor";
				ydrBrowser.html.text(document.getElementById("ydrTextEdit_caption"), "Change foreground colour");
				break;
				
			case "background-color":
				colourModeExec="BackColor";
				ydrBrowser.html.text(document.getElementById("ydrTextEdit_caption"), "Change background colour");
				break;
		};
				
		if (textIsSelected(true))
		{
			var colour=ydrBrowser.html.currentStyle(selectionRangeElement(), colourModeIE);
			
			document.getElementById("ydrTextEdit_colourCurrentPanel").style.backgroundColor = colour;
			document.getElementById("ydrTextEdit_colourNewPanel").style.backgroundColor = colour;
			ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_colourCurrent"), colour);
			ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_colourNew"), colour);
					
			document.getElementById("ydrTextEdit_colour").style.visibility="inherit"; 
		}		
		
		currentElementForForm = document.getElementById("ydrTextEdit_colour");
	}
	
	this.colourApply = function()
	{
		if (! editConfirm()) 	return;
		
		try { editBookmarkedSelection.select() } catch(e) {}; 
		
		try 
		{ 
			with (editBookmarkedSelection[0].style)
			{ cssText=cssText+";"+colourModeCSS+":"+ ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_colourNew"))}
		}
		catch (e)
		{
			oW.document.execCommand(colourModeExec, false, ydrBrowser.html.elementValue(document.getElementById("ydrTextEdit_colourNew")));
		};
		
		currentElementForForm.style.visibility="hidden"; 	
		currentElementForForm == null;
		m_this.editDone();
	};

	this.colourValidate = function(element)
	{
		switch (element.id )
		{
			case "ydrTextEdit_colourNew" :
				try
				{
					document.getElementById("ydrTextEdit_colourNewPanel").style.backgroundColor = ydrBrowser.html.elementValue(element);	
				}
				catch (e) {alert(ydrBrowser.html.elementValue(element) + "is not a valid colour") };
				break;
				
			case "" :
			
				break;		
		}		
	};

	this.tableRowAmend = function(toDo,below) 
	{ 
		//insert or delete a table row
		var i;
		var paNode=selectionRangeElement();
		
		try 
		{
			while (paNode.tagName.toLowerCase() != "tr")
				 paNode=paNode.parentNode;		
		} catch (e)
		{ window.alert("Please select a cell in the table to indicate the row required"); return };	
		
		var tRow=paNode.rowIndex;
		var tCols=paNode.cells.length;		
		
		while (paNode.tagName != "TABLE") 
		{	paNode=paNode.parentNode;		 };

		if (toDo == "delete") 
			paNode.deleteRow(tRow);
		else 
		{
			var newRow=paNode.insertRow(tRow+parseInt(below)); //1=below  0=above			for (i=0; i < tCols; i++)
			{
				newRow.insertCell(i).innerHTML="#";
			 };
		 }
	};
	
	this.tableColAmend = function(toDo,after) 
	{ 
		//insert or delete a column
		var i;
		var paNode=selectionRangeElement();		
		
		try
		{
			while (paNode.tagName != 'TD') 
				paNode=paNode.parentNode;		
		} catch (e) { window.alert("Please select a cell in the table to indicate the column required"); return };
			
		var tCol=paNode.cellIndex;		
		while (paNode.tagName != "TABLE") 
			paNode=paNode.parentNode;		
			
		var tRows=paNode.rows.length;		
		for (i=0; i < tRows; i++)
		{
			if (toDo == "delete") 
				try { paNode.rows[i].deleteCell(tCol); } catch (e) {}
			else 
			{
				//if after=0 then before
				paNode.rows[i].insertCell(tCol+parseInt(after)).innerHTML="#";
			}
		}
	};

	function kb_handler(e) 
	{ 
		// keyboard controls for Moz
		if (e && e.keyCode==13 && selectionRangeElement() == document.body) 
			oW.document.execCommand("formatblock", false, "&lt;p&gt;");
		if (e && e.ctrlKey) 
		{
			var bubble=true;
			switch (String.fromCharCode(e.charCode).toLowerCase())
			{
				case "b" : bubble=false; oW.document.execCommand("bold"); break;
				case "i" : bubble=false; oW.document.execCommand("italic"); break;
				case "u" : bubble=false; oW.document.execCommand("underline"); break;	
			}			if (bubble) 
			{
				e.preventDefault();  // stop the event bubble
				e.stopPropagation();
			}
		}
	};

	//inserts the html at the bookmarkRange then makes the result the current selectionRange and elementSelected
	// drill if true forces the focus to drill to the element whose text is exactly the bookmarkRange
	function insertHTML(bookmarkRange, tagName, attributes)
	{	
		var myAttributes="";
		var sttributeName = "";
		if (attributes != "")
		{
			myAttributes="style=\""+attributes+"\"";
			attributeName = attributes.split(":")[0].trim();
		};
			
		if (bookmarkRange != null)
			try {bookmarkRange.select()} catch (e) {};
			
		with (selectionRange())		
		{
			try
			{				
				pasteHTML("<"+tagName+" x='x' "+ myAttributes+">"+htmlText+"</"+tagName+">");
			}
			catch (e)
			{
				try 
				{
					if (tagName=="span") throw ("try fudger");
					
					moveStart("character",1); // because wont work on empty selection - this does not work at start of document
					pasteHTML(htmlText.charAt(0)
						+"<"+tagName+" x='x' "+ attributes+">"
						+htmlText.substr(1))
					try {moveStart("character",-1);} catch (e) {};
				}
				catch (e)
				{
					// we can get here if the selection isnt pastable because it spans a queer set of elements so we 
					// cheat outrageously by using execommand to insert some junk and then switching it by a replace statement to what we wanted - this only really works well if the tagname is span and does not work for the main inserts eg via editTable()
					// and does not cause the element to be selected but the parent of the several - how ghastly
					
					oW.document.execCommand("FontSize",null,1);
					result=selectionRangeElement();
					insertHTMLfudger(result
						, /<\/FONT>/g, "</"+tagName+">"
						,/<FONT size=1>/g,  "<"+tagName+" "+ myAttributes +">"
						,tagName, attributeName);
						
					return result;
				};
			};
			
			// drill is the name of an attribute that also has the same value - eg '<tagname x="x" />' would have drill as 'x'
			// we stay with the present elementSelected unless there is a drill or if the drill matches it
			// otherwise we look one level deeper
			
			var target=selectionRangeElement();
			 
			// well would you believe it - the node we have just created could be both above me or below me
			// because execCommand places an insertion where html schema accepts
			while (target.nodeName.toLowerCase() != "body")
			{
				if (target.getAttribute("x") == "x")
					break;
				target=target.parentNode;					
			};			
			
			if (target.nodeName.toLowerCase() == "body")
				target= lookForXchild(selectionRangeElement());
			
			if (target == null)
			{
				//alert("bad");
				return
			};
			
			target.removeAttribute("x") ;
			
			if (tagName=="span") 
			{
				try {insertHTMLfudgerSuppress(target, tagName, attributeName) } catch (e) {};
			};
			
			return target;
		};
	};
	
	function lookForXchild(node)
	{
		var result;
		var item;
		var i;
		
		for (i=0; i<node.childNodes.length; i++)
		{
			item = node.childNodes[i];
			if (item.nodeType ==1)
			{
				if (item.getAttribute("x") == "x")
					return item;
				
				result=lookForXchild(item);
				if (result)
					return result
			}
		}
	};
	
	function insertHTMLfudgerSuppress(node, tagName, attributes)
	{
		 insertHTMLfudgerSuppressDeeper(node,tagName, attributes);
		 
		 //now check upwards - if matched then we cannot remove the node as it will upset the structure of our selections
		 if (node.parentNode 
			&& node.parentNode.firstChild == node.parentNode.lastChild 
			&& node.parentNode.nodeName == node.nodeName
			&&  node.nodeName.toLowerCase() == "span"
			&& node.parentNode.outerText == node.outerText
			)
		 {
			// we make use of cssText autocorrection here in ie
			node.parentNode.style.cssText = node.parentNode.style.cssText+";" + node.style.cssText;
			node.parentNode.innerHTML = node.innerHTML;
		 }
	};
	
	function insertHTMLfudgerSuppressDeeper(node,thisTagName, thisAttribute)
	{
		var i;
		for ( i=0; i<node.childNodes.length; i++)
		{
			with (node.childNodes[i])
			{
				insertHTMLfudgerSuppressDeeper(node.childNodes[i],thisTagName, thisAttribute);
				
				if (nodeName.toLowerCase() == thisTagName) // expectd to be span
				{
					// assuming styles were put there by us!!! needs improving
					if (style.cssText.toLowerCase().indexOf(thisAttribute)>=0)
						style.cssText = (style.cssText+";") .replace(new RegExp("(.*)"+thisAttribute+".*;(.*)","i"),/$1$2/)
				}
			}			
		}
	}	;
	
	function insertHTMLfudger(node, from1, to1, from2, to2, tagName, attributes)
	{
		var i;
		if (node.nodeName == "FONT")
		{
			// turn the font into a span (if that is the way we were called)
			try {
				node.outerHTML=node.outerHTML
					.replace(from1,to1)
					.replace(from2,to2)
			}
			catch (e)
			{	
				alert ("fudger not expected")
			}		
			insertHTMLfudgerSuppress(node, tagName, attributes)
		}
		else
		{
			// traverse the remaining structures until we find FONT tags
			for ( i=0; i<node.childNodes.length; i++)
				insertHTMLfudger(node.childNodes[i],  from1, to1, from2, to2, tagName, attributes);
		}	
	};
	
	function selectionRangeElement( optionalRange)
	{
		var result;
		if (optionalRange == null)
			optionalRange = selectionRange(); //oW.document.selection.createRange();
			
		try { return optionalRange.parentElement()} catch (e) {}; // text in an element
		try { return optionalRange[0]} catch (e) {}; // a bodyless element	
	}
	
	function selectRangeEvent()
	{
		try {selectionRange() } catch (e) {};
	};
	var lastValidRange;  // because oW.document.selection does not always return a selection in this document!
	function selectionRange()
	{
		oW.focus();
		
		var result = oW.document.selection.createRange();
		var node =selectionRangeElement(result);
		
		if (node)
		{
			/*if (result.ownerDocument != oW.document)
			{
				alert ("boundec");
				
				result=lastValidRange;
				if (result) 
					node =selectionRangeElement(result);
			};*/
		};		
		
		if (node)
		{
			document.getElementById("cteToolbarBold").style.backgroundColor = ( ydrBrowser.html.currentStyle(node, "fontWeight") == 400 ? "transparent" : "#fffacd");
			document.getElementById("cteToolbarItalic").style.backgroundColor = ( ydrBrowser.html.currentStyle(node, "fontStyle") != "italic" ? "transparent" : "#fffacd");
			document.getElementById("cteToolbarUnderline").style.backgroundColor = ( ydrBrowser.html.currentStyle(node, "textDecoration") != "underline" ? "transparent" : "#fffacd");
			document.getElementById("cteToolbarLeft").style.backgroundColor = ( ydrBrowser.html.currentStyle(node, "textAlign") != "left" ? "transparent" : "#fffacd");
			document.getElementById("cteToolbarCenter").style.backgroundColor = ( ydrBrowser.html.currentStyle(node, "textAlign") != "center" ? "transparent" : "#fffacd");
			document.getElementById("cteToolbarRight").style.backgroundColor = ( ydrBrowser.html.currentStyle(node, "textAlign") != "right" ? "transparent" : "#fffacd");
		
			var myfont=ydrBrowser.html.currentStyle(node,  "fontFamily");
						
			if (myfont && myfont !="")
				with (document.getElementById("ydrTextEdit_fontFamily"))
				{
					if (value != myfont)
					{
						value=myfont;
						if (selectedIndex <0)
						{
							var node = appendChild(ownerDocument.createElement("option"));
							
							ydrBrowser.html.elementValue(node, myfont);
							ydrBrowser.html.text(node, myfont);
							node.setAttribute("selected","selected");
						}
					}
				}
		
			var myfont=ydrBrowser.html.currentStyle(node, "fontSize");
						
			if (myfont && myfont !="")
				with (document.getElementById("ydrTextEdit_fontSize"))
				{
					if (value != myfont)
					{
						value=myfont;
						if (selectedIndex <0)
						{
							var node = appendChild(ownerDocument.createElement("option"));
							
							ydrBrowser.html.elementValue(node, myfont);
							ydrBrowser.html.text(node, myfont);
							node.setAttribute("selected","selected");
						};
					}
				}
		
			var myStyle = false;
			trail=node.nodeName; 
			
			while (!node.nodeName.match(/^(html|body)/i)) 
			{
				if ( ! myStyle)
				{
					with (document.getElementById("ydrTextEdit_element"))
					{
						value = node.nodeName.toLowerCase();
						myStyle = selectedIndex >=0;
					};
				};
				
				node=node.parentNode;
				if (node == null) break;
		
				switch (node.nodeName.toLowerCase())
				{
					case "u" :
					case "underline" :
						document.getElementById("cteToolbarUnderline").style.backgroundColor =  "#fffacd";		
						break;

					case "b" :
					case "strong" :
						document.getElementById("cteToolbarItalic").style.backgroundColor = "#fffacd";
						break;
						
					case "i" :
					case "em" :
						document.getElementById("cteToolbarBold").style.backgroundColor =  "#fffacd";
						break;
						
				};
				
				trail=node.nodeName + '>' + trail;
			}
		}
		else
			trail="";
	
		window.status="YDR Text Edit: "+ (trail=trail.toLowerCase());
		document.getElementById("ydrTextEdit_tableToolbar").style.visibility = (trail.indexOf(">table")>=0) ? "inherit" : "hidden";
		
		if (result) 
			lastValidRange = result;
			
		return result;
	};

	function textIsSelected(optional) 
	{ 
		var sel=oW.document.selection.createRange();
		if (sel  && sel.type != "None") 
			return true
		else if (! optional)
			alert("Please select some text first");
		return false;
	};
	
	// used from .htm page to ensure we do this on the right object
	this.execCommand = function(what,opt) 
	{
		oW.focus();
		m_this.editDone(); 
		
		if (opt=="removeFormat") 
		{
			what=opt;
			opt = null;
		}

		if (opt == null) 
			oW.document.execCommand(what)
		else 
			oW.document.execCommand(what, "", opt);
	};	
	
	//// when we want to return a result we want to pick up the body xhtml but all that is available is junky ie rendering
	/// these components convert a node into xhtml
	
	this.finish = function() {
		oW.document.body.innerHTML="";
		oW.document.designMode = "off"}
	
	this.result = function()
	{
		//if (! ydrClientScripts.isIE ) document.getElementById("ydrClientTextEdit").cleanseHTML();
		//alert(oW.document.body.innerHTML)
		 ydrClientTextEdit.cleanseHTML();
		 //alert(oW.document.body.innerHTML)
		
		var result =  ydrBrowser.html.xhtml(oW.document.body);
		
		//alert (result);
		
		this.finish();
		return result;
	};	
		
	// this has to be manually forward by the popup element's editorvalidate function
	this.validate = function(element)
	{
		var result = "";
		var urlS;
		if (element != null)
		{
			switch (element.id)
			{
				// validations for ydrTextEdit_link" :
				case "ydrTextEdit_linkSelect" :
					with ( document.getElementById("ydrTextEdit_linkURL") )
					{
						urlS = value.split(":");
						urlS[0] = ydrBrowser.html.elementValue(element.options[Math.max(0, element.selectedIndex)]).toLowerCase();
						value = urlS.join(":");
					};
					break;
					
				case "ydrTextEdit_linkURL" :			
					urlS = ydrBrowser.html.elementValue(element).split(":");
					switch (urlS[0])
					{
						case "http" :
						case "https" :
						case "mailto" :
						case "callto" :	
							with (document.getElementById("ydrTextEdit_link"+urlS[0].toUpperCase()))
							{if (!selected) selected = true;};
							
							break;

						default : 
							with (document.getElementById("ydrTextEdit_linkHTTP"))
							{ if (!selected) selected = true; };
							
							urlS[1]=urlS[0];
							urlS[0] = "http";
							ydrBrowser.html.elementValue(element, urlS.join(":"));
					};
					
					break;
			};
		};

		//stop();
		try {
			setTimeout("ydrClientTextEdit.showAdvisory(document.getElementById('" + currentElementForForm.getAttribute("id") +"Advisory'))", 1);	
		} catch (e) {};
		
		return result;
	}
	
	this.help = function()
	{
		document.getElementById("helpWait").style.display="block";
		window.setTimeout('window.open(rootDir +"/ydr/ydrTextEditHelp.asp"); document.getElementById("helpWait").style.display="none"',1)
		return false;
	}
}


