/*
**    File: widgEdit.js
**    Created by: Cameron Adams (http://www.themaninblue.com/)
**    Created on: 2005-01-16
**    Last modified: 2005-01-30
**
**
**
**
**    Purpose:
**    -------------------------------------------------------------------------
**
**    Replaces all textareas (class="widgEditor") in a HTML document with
**    enhanced editing windows to allow basic HTML formatting in a WYSIWYG
**    manner.
**
**
**
**
**    Function list
**    -------------------------------------------------------------------------
**
**    run()
**
**    widgInit()
**
**    widgEditor(replacedTextareaID)
**    widgEditor.cleanSource()
**    widgEditor.initEdit()
**    widgEditor.insertNewParagraph()
**    widgEditor.modifyFormSubmit()
**    widgEditor.switchMode()
**    widgEditor.updateWidgInput()
**    widgEditor.writeDocument()
**
**    widgToolbar()
**    widgToolbar.addButton(theID, theLabel, theAction)
**    widgToolbar.addSelect(theID, theContentArray, theAction)
**    widgToolbar.disable()
**    widgToolbar.enable()
**
**    widgToolbarAction()
**
**    widgToolbarMouseover()
**
**    String.classExists(theClass)
**    String.isInlineName()
*/




/******************************************************************************
**    CONFIGURATION VARIABLES
******************************************************************************/
if (widgStylesheet===undefined) {
   /* Location of stylesheet file for editor content */
   var widgStylesheet = "/css/b.css";
   document.write('<!-- widgEdit.js The widgStylesheet was NOT defined using widgStylesheet='+widgStylesheet+' -->');
}

/* Items to appear in toolbar. */
var widgToolbarItems = new Array();

widgToolbarItems.push("bold");
widgToolbarItems.push("italic");
widgToolbarItems.push("hyperlink");
widgToolbarItems.push("unorderedlist");
widgToolbarItems.push("orderedlist");
widgToolbarItems.push("htmlsource");
widgToolbarItems.push("blockformat");

/* Options on block format select element. Consists of string pairs (option value, option label)  */

var widgSelectBlockOptions = new Array();
widgSelectBlockOptions.push("", "Change block type");
widgSelectBlockOptions.push("<h1>", "Heading 1");
widgSelectBlockOptions.push("<h2>", "Heading 2");
widgSelectBlockOptions.push("<h3>", "Heading 3");
widgSelectBlockOptions.push("<h4>", "Heading 4");
widgSelectBlockOptions.push("<h5>", "Heading 5");
widgSelectBlockOptions.push("<h6>", "Heading 6");
widgSelectBlockOptions.push("<p>", "Paragraph");

/* If widgInsertParagraphs = true, when content is submitted paragraphs will be
** inserted around text without a parent element. Mozilla does not
** automatically do this, so if this is set to false you will end up with some
** plain text blocks. Uses a double <br /> as a pargraph marker.
*/

var widgInsertParagraphs = true;

/******************************************************************************
**    END CONFIGURATION
******************************************************************************/




run();




function run()
{
	var oldOnload = window.onload;

	if (typeof window.onload != "function")
	{
		window.onload = widgInit;
	}
	else
	{
		window.onload = function()
		{
			oldOnload();
			widgInit();
		}
	}
}




function widgInit()
{
	if (document.designMode != null )
	{
		var theTextareas = document.getElementsByTagName("textarea");
		
		for (var i = 0; i < theTextareas.length; i++)
		{
			var theTextarea = theTextareas[i];
			
			if (theTextarea.className.classExists("widgEditor"))
			{
				if (theTextarea.id == "")
				{
					theTextarea.id = theTextarea.name;
				}
				
				
				setTimeout("new widgEditor('" + theTextarea.id + "')", 500 * (i));
			}
		}
	}
	else
	{
		return false;
	}
	
	return true;
}




function widgEditor(replacedTextareaID)
{
	var self = this;
	
	this.theTextarea = document.getElementById(replacedTextareaID);
	this.theContainer = document.createElement("div");
	this.theIframe = document.createElement("iframe");
	this.theInput = document.createElement("input");
	this.theExtraInput = document.createElement("input");
	this.wysiwyg = true;
	
	if (this.theTextarea.id == null)
	{
		this.theTextarea.id = this.theTextarea.name;
	}
	
	this.theTextarea.style.visibility = "hidden";

	this.theToolbar = new widgToolbar(this);
	
	/* Modify DOM objects for editor */
	this.theContainer.id = this.theTextarea.id + "WidgContainer";
	this.theContainer.className = "widgContainer";
	
	this.theIframe.id = this.theTextarea.id + "WidgIframe";
	this.theIframe.className = "widgIframe";
	
	this.theInput.type = "hidden";
	this.theInput.id = this.theTextarea.id;
	this.theInput.name = this.theTextarea.name;
	this.theInput.value = this.theTextarea.value;

	/* An extra input to determine if the submitted data is from the normal textarea or from the widgEditor */
	this.theExtraInput.type = "hidden";	
	this.theExtraInput.id = this.theTextarea.id + "WidgEditor";
	this.theExtraInput.name = this.theTextarea.name + "WidgEditor";
	this.theExtraInput.value = "true";
	
	this.theTextarea.id += "WidgTextarea";
	this.theTextarea.name += "WidgTextarea";
	
	this.theContainer.appendChild(this.theToolbar.theList);
	this.theContainer.appendChild(this.theIframe);
	this.theContainer.appendChild(this.theInput);
	this.theContainer.appendChild(this.theExtraInput);
	this.theContainer.style.visibility = "hidden";

	this.theInput.widgEditorObject = this;
	
	this.theTextarea.parentNode.replaceChild(this.theContainer, this.theTextarea);

	/* Fill editor with old textarea content */
	this.writeDocument(this.theInput.value);
	
	/* Make editor editable */
	this.initEdit();
	
	/* Attach onsubmit to parent form */
	this.modifyFormSubmit();
	
	return true;
}




/* Clean the HTML code of the content area */
widgEditor.prototype.cleanSource = function()
{
	var theHTML = "";
	
	if (this.wysiwyg)
	{
		theHTML = this.theIframe.contentWindow.document.getElementsByTagName("body")[0].innerHTML;
	}
	else
	{
		theHTML = this.theTextarea.value;
	}
	
    /* Change attr="xyz" on attributes inside any img tag and add ... /> */
    //theHTML = theHTML.replace(/<img (.*?)>/g, '<img $1 />');
    theHTML = theHTML.replace(/ width=([\d]+)/g, ' width="$1"');
    theHTML = theHTML.replace(/ height=([\d]+)/g, ' height="$1"');
    theHTML = theHTML.replace(/ class=([\w]+)/g, ' class="$1"');
	
    /* Replace IE's uppercase tags with lowercase */
	theHTML = theHTML.replace(/<[^> ]*/g, function(match){return match.toLowerCase();});

	/* Replace Mozilla's span formatting with semantic tags */
	theHTML = theHTML.replace(/<span\s*style="font-style:\s*italic;">(.*?)<\/span>/g, "<em>$1</em>");
	theHTML = theHTML.replace(/<span\s*style="font-weight:\s*bold;">(.*?)<\/span>/g, "<strong>$1</strong>");
	theHTML = theHTML.replace(/<span\s*style="font-style:\s*italic;\s*font-weight:\s*bold;">(.*?)<\/span>/g, "<em><strong>$1</strong></em>");
	theHTML = theHTML.replace(/<span style="font-weight:\s*bold;\s*font-style:\s*italic;">(.*?)<\/span>/g, "<strong><em>$1</em></strong>");
	
    /* Remove style attribute inside any tag */
	theHTML = theHTML.replace(/ style="[^"]*"/g, "");

	/* Replace improper BRs */
	theHTML = theHTML.replace(/<br>/g, "<br />");
	if (this.wysiwyg)
	{
		this.theIframe.contentWindow.document.getElementsByTagName("body")[0].innerHTML = theHTML;
	}
	else
	{
		this.theTextarea.value = theHTML;
	}
	
	this.theInput.value = theHTML;
	
	return true;
}




/* Turn on document editing */
widgEditor.prototype.initEdit = function()
{
	var self = this;
	
	/* IE's version */
	if (document.contentEditable)
	{
		this.theIframe.contentWindow.document.designMode = "On";
	}
	/* Mozilla's version */
	else if (document.designMode != null)
	{
		try
		{
			this.theIframe.contentWindow.document.designMode = "on";
		}
		catch (e)
		{
			/* setTimeout needed to counteract Mozilla bug whereby you can't immediately change designMode on newly created iframes */
			setTimeout(function(){self.initEdit()}, 250);
			
			return false;
		}
	}
	
	this.theContainer.style.visibility = "visible";
	this.theTextarea.style.visibility = "visible";
	
	return true;	
}




/* Add elements to a paragraph and inserts the paragraph before a given element in the body */
widgEditor.prototype.insertNewParagraph = function(elementArray, succeedingElement)
{
	var theBody = this.theIframe.contentWindow.document.getElementsByTagName("body")[0];
	var theParagraph = this.theIframe.contentWindow.document.createElement("p");
	
	for (var i = 0; i < elementArray.length; i++)
	{
		theParagraph.appendChild(elementArray[i]);
	}
	
	if (typeof(succeedingElement) != "undefined")
	{
		theBody.insertBefore(theParagraph, succeedingElement);
	}
	else
	{
		theBody.appendChild(theParagraph);
	}
	
	return true;
}




/* Add submit listener to parent form */
widgEditor.prototype.modifyFormSubmit = function()
{
	var self = this;
	var theForm = this.theContainer.parentNode;
	var oldOnsubmit = null;
	
	/* Find the parent form element */
	while (theForm.nodeName.toLowerCase() != "form")
	{
		theForm = theForm.parentNode;
	}

	/* Add onsubmit without overwriting existing function calls */
	oldOnsubmit = theForm.onsubmit;

	if (typeof theForm.onsubmit != "function")
	{
		theForm.onsubmit = function()
		{
			return self.updateWidgInput();
		}
	}
	else
	{
		theForm.onsubmit = function()
		{
			self.updateWidgInput();

			return oldOnsubmit();			
		}
	}

	return true;
}




/* Switch between WYSIWYG and HTML source */
widgEditor.prototype.switchMode = function()
{
	this.cleanSource();
	
	/* Switch to HTML source */
	if (this.wysiwyg)
	{
		this.theTextarea.value = this.theInput.value;
		
		/* Remove whitespace at start and end of code */
		this.theTextarea.value = this.theTextarea.value.replace(/^\s*(.*?)\s*$/, "$1");

		this.theContainer.replaceChild(this.theTextarea, this.theIframe);

		this.theToolbar.disable();
		this.wysiwyg = false;
	}
	/* Switch to WYSIWYG */
	else
	{
		this.theContainer.replaceChild(this.theIframe, this.theTextarea);

		this.writeDocument(this.theInput.value);

		this.theToolbar.enable();
		this.initEdit();
		this.wysiwyg = true;
	}
			
	return true;
}




/* Update hidden input to reflect editor contents, for submission */
widgEditor.prototype.updateWidgInput = function(resubmit)
{
	var theHTML = "";
	
	if (this.wysiwyg)
	{
		/* Format HTML with paragraphs */
		if (widgInsertParagraphs)
		{
			var theBody = this.theIframe.contentWindow.document.getElementsByTagName("body")[0];

			/* Remove all text nodes containing just whitespace */
			for (var i = 0; i < theBody.childNodes.length; i++)
			{
				if (theBody.childNodes[i].nodeName.toLowerCase() == "#text" &&
					theBody.childNodes[i].data.search(/^\s*$/) != -1)
				{
					theBody.removeChild(theBody.childNodes[i]);

					i--;
				}
			}

			var removedElements = new Array();

			for (var i = 0; i < theBody.childNodes.length; i++)
			{
				if (theBody.childNodes[i].nodeName.isInlineName())
				{
					removedElements.push(theBody.childNodes[i].cloneNode(true));

					theBody.removeChild(theBody.childNodes[i]);

					i--;
				}
				else if (theBody.childNodes[i].nodeName.toLowerCase() == "br")
				{
					if (i + 1 < theBody.childNodes.length)
					{
						/* If the current break tag is followed by another break tag */
						if (theBody.childNodes[i + 1].nodeName.toLowerCase() == "br")
						{
							/* Remove consecutive break tags */
							while (i < theBody.childNodes.length && theBody.childNodes[i].nodeName.toLowerCase() == "br")
							{
								theBody.removeChild(theBody.childNodes[i]);
							}

							if (removedElements.length > 0)
							{
								this.insertNewParagraph(removedElements, theBody.childNodes[i]);

								removedElements = new Array();
							}
						}
						/* If the break tag appears before a block element */
						else if (!theBody.childNodes[i + 1].nodeName.isInlineName())
						{
							theBody.removeChild(theBody.childNodes[i]);
						}
						else if (removedElements.length > 0)
						{
							removedElements.push(theBody.childNodes[i].cloneNode(true));

							theBody.removeChild(theBody.childNodes[i]);
						}
						else
						{
							theBody.removeChild(theBody.childNodes[i]);
						}

						i--;
					}
					else
					{
						theBody.removeChild(theBody.childNodes[i]);
					}
				}
				else if (removedElements.length > 0)
				{
					this.insertNewParagraph(removedElements, theBody.childNodes[i]);

					removedElements = new Array();
				}
			}

			if (removedElements.length > 0)
			{
				this.insertNewParagraph(removedElements);
			}
		}
	}
	
	this.cleanSource();

	return true;
}




/* Write initial content to editor */
widgEditor.prototype.writeDocument = function(documentContent)
{
	/* HTML template into which the HTML Editor content is inserted */
	var documentTemplate = "\
		<html>\
			<head>\
				<style type=\"text/css\">@import url(\"INSERT:STYLESHEET:END\");</style>\
			</head>\
			<body id=\"iframeBody\">\
				INSERT:CONTENT:END\
			</body>\
		</html>\
	";
	
	/* Insert dynamic variables/content into document */
	documentTemplate = documentTemplate.replace(/INSERT:STYLESHEET:END/, widgStylesheet);
	documentTemplate = documentTemplate.replace(/INSERT:CONTENT:END/, documentContent);
	
	this.theIframe.contentWindow.document.open();
	this.theIframe.contentWindow.document.write(documentTemplate);
	this.theIframe.contentWindow.document.close();
	
	return true;
}




/* Toolbar items */
function widgToolbar(theEditor)
{
	var self = this;
	
	this.widgEditorObject = theEditor;
	
	/* Create toolbar ul element */
	this.theList = document.createElement("ul");
	this.theList.id = this.widgEditorObject.theInput.id + "WidgToolbar";
	this.theList.className = "widgToolbar";
	this.theList.widgToolbarObject = this;

	/* Create toolbar items */
	for (var i = 0; i < widgToolbarItems.length; i++)
	{
		switch (widgToolbarItems[i])
		{
			case "bold":
				this.addButton("widgButtonBold", "Bold", "bold");
				
				break;
				
			case "italic":
				this.addButton("widgButtonItalic", "Italic", "italic");
				
				break;
				
			case "hyperlink":
				this.addButton("widgButtonLink", "Hyperlink", "link");
				
				break;
				
			case "unorderedlist":
				this.addButton("widgButtonUnordered", "Unordered List", "insertunorderedlist");
				
				break;
				
			case "orderedlist":
				this.addButton("widgButtonOrdered", "Ordered List", "insertorderedlist");
				
				break;
				
			case "htmlsource":
				this.addButton("widgButtonHTML", "HTML Source", "html");
				
				break;
				
			case "blockformat":
				this.addSelect("widgSelectBlock", widgSelectBlockOptions, "formatblock");
				
				break;
		}
	}

	return true;
}





/* Add button to toolbar */
widgToolbar.prototype.addButton = function(theID, theLabel, theAction)
{
	var menuItem = document.createElement("li");
	var theLink = document.createElement("a");
	var theText = document.createTextNode(theLabel);
	
	menuItem.className = "widgEditButton";

	theLink.href = "#";
	theLink.title = theLabel;
	theLink.className = theID;
	theLink.action = theAction;
	theLink.onclick = widgToolbarAction;
	theLink.onmouseover = widgToolbarMouseover;

	theLink.appendChild(theText);
	menuItem.appendChild(theLink);
	this.theList.appendChild(menuItem);

	return true;
}




/* Add select box to toolbar. theContentArray is an array of string pairs (option value, option label) */
widgToolbar.prototype.addSelect = function(theID, theContentArray, theAction)
{
	var menuItem = document.createElement("li");
	var theSelect = document.createElement("select");
	
	menuItem.className = "widgEditSelect";
	
	theSelect.id = theID;
	theSelect.name = theID;
	theSelect.action = theAction;
	theSelect.onchange = widgToolbarAction;

	for (var i = 0; i < theContentArray.length; i += 2)
	{
		var theOption = document.createElement("option");
		var theText = document.createTextNode(theContentArray[i + 1]);
		
		theOption.value = theContentArray[i];

		theOption.appendChild(theText);
		theSelect.appendChild(theOption);
	}
	
	menuItem.appendChild(theSelect);
	this.theList.appendChild(menuItem);

	return true;
}




/* Turn off toolbar items */
widgToolbar.prototype.disable = function()
{
	/* Change class to disable buttons using CSS */
	this.theList.className += " widgSource";

	/* Loop through lis */
	for (var i = 0; i < this.theList.childNodes.length; i++)
	{
		var theChild = this.theList.childNodes[i];
		
		if (theChild.nodeName.toLowerCase() == "li" && theChild.className == "widgEditSelect")
		{
			/* Loop through li children to find select */
			for (j = 0; j < theChild.childNodes.length; j++)
			{
				if (theChild.childNodes[j].nodeName.toLowerCase() == "select")
				{
					theChild.childNodes[j].disabled = "disabled";
					
					break;
				}
			}
		}
	}
	
	return true;
}




/* Turn on toolbar items */
widgToolbar.prototype.enable = function()
{
	/* Change class to enable buttons using CSS */
	this.theList.className = this.theList.className.replace(/ widgSource/, "");
	
	/* Loop through lis */
	for (var i = 0; i < this.theList.childNodes.length; i++)
	{
		var theChild = this.theList.childNodes[i];
		
		if (theChild.nodeName.toLowerCase() == "li" && theChild.className == "widgEditSelect")
		{
			/* Loop through li children to find select */
			for (j = 0; j < theChild.childNodes.length; j++)
			{
				if (theChild.childNodes[j].nodeName.toLowerCase() == "select")
				{
					theChild.childNodes[j].disabled = "";
					
					break;
				}
			}
		}
	}
	
	return true;
}





/* Action taken when toolbar item activated */
function widgToolbarAction()
{
	var theToolbar = this.parentNode.parentNode.widgToolbarObject;
	var theWidgEditor = theToolbar.widgEditorObject;
	var theIframe = theWidgEditor.theIframe;
	var theSelection = "";

	/* If somehow a button other than "HTML source" is clicked while viewing HTML source, ignore click */	
	if (!theWidgEditor.wysiwyg && this.action != "html")
	{
		return false;
	}
	
	switch (this.action)
	{
		case "formatblock":
			theIframe.contentWindow.document.execCommand(this.action, false, this.value);
			
			this.value = "";
			
			break;
			
		case "html":
			theWidgEditor.switchMode();
			
			break;
			
		case "link":
			if (theIframe.contentWindow.document.selection)
			{
				theSelection = theIframe.contentWindow.document.selection.createRange().text;
				
				if (theSelection == "")
				{
					alert("Please select the text you wish to hyperlink.");
					
					break;
				}
			}
			else
			{
				theSelection = theIframe.contentWindow.getSelection();
				
				if (theSelection == "")
				{
					alert("Please select the text you wish to hyperlink.");
					
					break;
				}
			}
			
			var theURL = prompt("Enter the URL for this link:", "http://");
			
			if (theURL != null)
			{			
				theIframe.contentWindow.document.execCommand("CreateLink", false, theURL);
			}
			
			break;
		
		default:
			theIframe.contentWindow.document.execCommand(this.action, false, null);
	}
	
	if (theWidgEditor.wysiwyg == true)
	{
		theIframe.contentWindow.focus();
	}
	else
	{
		theWidgEditor.theTextarea.focus();
	}
	
	return false;	
}




/* Turn off browser status display for toolbar items */
function widgToolbarMouseover()
{
	window.status = "";
	
	return true;
}




String.prototype.classExists = function(theClass)
{
	var regString = "(^| )" + theClass + "\W*";
	var regExpression = new RegExp(regString);
	
	if (regExpression.test(this))
	{
		return true;
	}
	
	return false;
}




String.prototype.isInlineName = function()
{
	var theName = this.toLowerCase();
	
	if (theName == "#text" || theName == "em" || theName == "strong" || theName == "span" || theName == "a")
	{
		return true;
	}
	
	return false;
}