----- file: /js/ubbeditor.js -----

[code]
var UBBEditor = function (field_id)
{
	// UBB Editor
	this.__init(field_id);
};

UBBEditor.prototype = 
{
	/*
		Properties
	*/
	'id' : '',
	'resource' : null,

	/*
		Init function to detect resource
	*/
	'__init' : function(f_id)
	{
		if(document.getElementById)
		{
			try
			{
				this.id = f_id;
				this.resource = document.getElementById(f_id);
			}
			catch(e0)
			{
				this.resource = null;
			}
		}

		if(this.resource == null)
		{
			alert('The UBBEditor is not supported by your browser.');
		}
	},

	/*
		Utils
	*/
	// Replace string within a string
	'__replace' : function(needle, replace_with, haystack)
	{
		if(haystack.length >= needle.length)
		{
			for(var i = 0; i <= (haystack.length - needle.length); i++)
			{
				if(haystack.substr(i, needle.length) == needle)
				{
					haystack = haystack.substr(0, i) + replace_with + haystack.substr(i + needle.length);
					i = i + replace_with.length;
				}
			}
		}

		return haystack;
	},

	// Replace string within a string
	'__addslashes' : function(haystack)
	{
		haystack = this.__replace('\\', '\\\\', haystack);
		haystack = this.__replace('"', '\\"', haystack);
		haystack = this.__replace('\'', '\\\'', haystack);
		return haystack;
	},

	// Get range of the selection. Will return an array with start index and length.
	'__getRange' : function()
	{
		var start = 0;
		var length = 0;

		if(this.resource != null)
		{
			// Set default range (cursor after last character in textarea)
			start = this.resource.value.length;
			length = 0;

			if(document.selection)
			{
				// Set focus on textarea (to ignore selection outside the textarea)
				this.resource.focus();

				// The current selection 
				var range = document.selection.createRange();

				// Store original text
				var original_text = range.text;

				// Insert ubb marker to find range
				// var marker = '[ubb_marker_' + (Math.round(Math.rand() * 90000) + 10000) + ']';
				var marker = '[ubb_marker_345381223746467363670497069367]';
				range.text = marker;

				// See if marker is found within the textarea
				var index = this.resource.value.indexOf(marker);

				if(index >= 0) // Text selected within textarea
				{
					start = index;
					length = original_text.length;

					// Restore original text in textarea
					this.resource.value = this.__replace(marker, original_text, this.resource.value);
				}
				else // No text selected within textarea
				{
					// No selection found. Use default range.
				}
			}
			else if((this.resource.selectionStart >= 0) && (this.resource.selectionEnd >= 0))
			{
				start = this.resource.selectionStart;
				length = this.resource.selectionEnd - start;
			}
		}

		return new Array(start, length);
	},

	'__getText' : function(f_start, f_length)
	{
		var text = '';

		if(this.resource != null)
		{
			if(this.__getText.arguments.length < 1)
			{
				text = this.resource.value;
			}
			else if(this.__getText.arguments.length < 2)
			{
				text = this.resource.value.substr(f_start);
			}
			else
			{
				text = this.resource.value.substr(f_start, f_length);
			}
		}

		return text;
	},

	'__setText' : function(f_string, f_start, f_length)
	{
		if(this.resource != null)
		{
			if(this.__setText.arguments.length < 1)
			{
				this.resource.value = '';
			}
			else if(this.__setText.arguments.length < 2)
			{
				this.resource.value = f_string;
			}
			else if(this.__setText.arguments.length < 3)
			{
				this.resource.value = this.resource.value.substr(0, f_start) + f_string;
			}
			else
			{
				this.resource.value = this.resource.value.substr(0, f_start) + f_string + this.resource.value.substr(f_start + f_length);
			}
		}
	},

	/*
		UBB functions
		Note: Some functions require arguments. If not provided, PROMPT() is used to gather missing info.
	*/

	// Clear textarea
	'clear' : function()
	{
		if(this.resource != null)
		{
			this.resource.value = '';
		}
		else
		{
			alert('Invald resource for UBB Editor.');
		}
	},

	// Add: [align]..[/align]
	'setAlign' : function(f_align)
	{
		var range = this.__getRange();

		if(this.setAlign.arguments.length < 1)
		{
			f_align = prompt('How should we align this text?\nEg: left, center, right, justify', 'left');
		}

		if(f_align == null)
		{
			// Ignore
		}
		else if(f_align == '')
		{
			// Ignore
		}
		else
		{
			this.__setText('[align="' + this.__addslashes(f_align) + '"]' + this.__getText(range[0], range[1]) + '[/align]', range[0], range[1]);
		}
	},

	// Add: [b]..[/b]
	'setBold' : function()
	{
		var range = this.__getRange();
		this.__setText('[b]' + this.__getText(range[0], range[1]) + '[/b]', range[0], range[1]);
	},

	// Add: [code="f_type"]..[/code]
	'setCode' : function(f_type)
	{
		var range = this.__getRange();

		if(this.setCode.arguments.length < 1)
		{
			f_type = prompt('How should we treat this code?\nEg: PHP, SQL, HTML, JS', '');
		}

		if(f_type == null)
		{
			// Ignore
		}
		else if (f_type == '')
		{
			this.__setText('[code]' + this.__getText(range[0], range[1]) + '[/code]', range[0], range[1]);
		}
		else
		{
			this.__setText('[code="' + this.__addslashes(f_type) + '"]' + this.__getText(range[0], range[1]) + '[/code]', range[0], range[1]);
		}
	},

	// Add: [color="f_color"]..[/color]
	'setColor' : function(f_color)
	{
		var range = this.__getRange();

		if(this.setColor.arguments.length == 0)
		{
			f_color = prompt('What color should we use?\nEg: #000000, rgb(0, 0, 0), black', '#000000');
		}

		if(f_color == null)
		{
			// Ignore
		}
		else if(f_color == '')
		{
			// Ignore
		}
		else
		{
			this.__setText('[color="' + this.__addslashes(f_color) + '"]' + this.__getText(range[0], range[1]) + '[/color]', range[0], range[1]);
		}
	},

	// Add: [image="f_url"]..[/image]
	'setImage' : function(f_url)
	{
		var range = this.__getRange();

		if(this.setImage.arguments.length < 1)
		{
			f_url = prompt('What image should we add?\nEg: http://www.domain.tld/folder/image.jpg', 'http://');
		}

		if(f_url == null)
		{
			// Ignore
		}
		else if(f_url == '')
		{
			// Ignore
		}
		else
		{
			this.__setText('[image="' + this.__addslashes(f_url) + '"]' + this.__getText(range[0], range[1]) + '[/image]', range[0], range[1]);
		}
	},
	
	// Add: [i]..[/i]
	'setItalic' : function()
	{
		var range = this.__getRange();
		this.__setText('[i]' + this.__getText(range[0], range[1]) + '[/i]', range[0], range[1]);
	},

	// Add: [quote="f_name"]..[/quote]
	'setQuote' : function(f_name)
	{
		var range = this.__getRange();

		if(this.setQuote.arguments.length == 0)
		{
			f_name = prompt('Who\'s quote are you using?', '');
		}

		if(f_name == null)
		{
			// Ignore
		}
		else if(f_name == '')
		{
			this.__setText('[quote]' + this.__getText(range[0], range[1]) + '[/quote]', range[0], range[1]);
		}
		else
		{
			this.__setText('[quote="' + this.__addslashes(f_name) + '"]' + this.__getText(range[0], range[1]) + '[/quote]', range[0], range[1]);
		}
	},

	// Add: [s]..[/s]
	'setStrike' : function()
	{
		var range = this.__getRange();
		this.__setText('[s]' + this.__getText(range[0], range[1]) + '[/s]', range[0], range[1]);
	},

	// Add: [sub]..[/sub]
	'setSubscript' : function()
	{
		var range = this.__getRange();
		this.__setText('[sub]' + this.__getText(range[0], range[1]) + '[/sub]', range[0], range[1]);
	},

	// Add: [sup]..[/sup]
	'setSuperscript' : function()
	{
		var range = this.__getRange();
		this.__setText('[sup]' + this.__getText(range[0], range[1]) + '[/sup]', range[0], range[1]);
	},

	// Add replace selected text with given text
	'setText' : function(f_string)
	{
		var range = this.__getRange();

		if(this.setText.arguments.length < 1)
		{
			f_string = prompt('What text should use to overwrite the selection?\nEg: hello world', '');
		}

		if(f_string == null)
		{
			// Ignore
		}
		else if(f_string == '')
		{
			// Ignore
		}
		else
		{
			this.__setText(f_string, range[0], range[1]);
		}
	},

	// Add: [u]..[/u]
	'setUnderline' : function()
	{
		var range = this.__getRange();
		this.__setText('[u]' + this.__getText(range[0], range[1]) + '[/u]', range[0], range[1]);
	},

	// Add: [url="f_url"]..[/url]
	'setUrl' : function(f_url)
	{
		var range = this.__getRange();

		if(this.setUrl.arguments.length == 0)
		{
			f_url = prompt('What url should we use?\nEg: http://www.domain.tld', 'http://');
		}

		if(f_url == null)
		{
			// Ignore
		}
		else if(f_url == '')
		{
			// Ignore
		}
		else
		{
			if(range[1] > 0)
			{
				this.__setText('[url="' + this.__addslashes(f_url) + '"]' + this.__getText(range[0], range[1]) + '[/url]', range[0], range[1]);
			}
			else
			{
				this.__setText('[url="' + this.__addslashes(f_url) + '"]' + f_url + '[/url]', range[0], range[1]);
			}
		}
	}
};
[/code]

----- end of file -----


----- file: /index.php -----

[code]
<?php

	// Escape HTML characters from a string
	function escapeHtml($string, $ascii_save = false)
	{
		if($ascii_save)
		{
			$string = htmlentities($string);

			// Catch not escaped characters that have HTML-friendly alternatives
			$string = str_replace(chr(128), "&euro;", $string);
			$string = str_replace(chr(137), "&permil;", $string);
			$string = str_replace(chr(153), "&trade;", $string);

			// Catch all other not escaped characters.
			for($i = 128; $i < 160; $i++) // for($i = 128; $i < 256; $i++)
			{
				$string = str_replace(chr($i), "&#" . $i . ";", $string);
			}
		}
		else
		{
			$string = str_replace("&", "&amp;", $string);
			$string = str_replace("<", "&lt;", $string);
			$string = str_replace(">", "&gt;", $string);
			$string = str_replace("\"", "&quot;", $string);
		}

		return $string;
	}

	// Change plain string to HTML code.
	function stringToHtml($string)
	{
		// HTML syntax
		$string = escapeHtml($string, true);

		// Fix new lines
		$string = str_replace("\r\n", "<br>", $string);
		$string = str_replace("\n", "<br>", $string);
		$string = str_replace("\r", "<br>", $string);

		// Fix Tabs
		$string = str_replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;", $string);

		return $string;
	}

	// Parse UBB code to HTML
	function ubbToHtml($string)
	{
		$result = '';

		// Stack with opened tags.
		$open_tags = array();
		$block = false;

		// Slice string into UBB blocks
		$matches = __splitUbb($string);

		for($i = 0; $i < sizeof($matches); $i++)
		{
			if(strcasecmp($match = $matches[$i], "") !== 0)
			{
				if($block) // Block opened (don't process ubb within blocks)
				{
					if(strcasecmp($match, '[/' . $block["tag"] . ']') === 0) // Closing block?
					{
						$result .= '</' . $block["tag"] . '>';

						// Remove tag from stack
						array_pop($open_tags);

						// Close block
						$block = false;
					}
					else
					{
						$result .= stringToHtml($match);
					}
				}
				else
				{
					if((strcasecmp(substr($match, 0, 2), '[/') === 0) && (strcasecmp(substr($match, -1), ']') === 0)) // Closing tag? [/tag]
					{
						$tag = strtolower(substr($match, 2, -1));

						// Auto close unclosed tags
						if((sizeof($open_tags) > 0) && in_array($tag, $open_tags))
						{
							$tag_found = false;

							while($tag_found === false)
							{
								$last_tag = array_pop($open_tags);

								if(strcasecmp($last_tag, $tag) === 0)
								{
									$tag_found = true;
								}

								if(strcasecmp($last_tag, 'align') === 0)
								{
									$result .= '</div>';
								}
								elseif(strcasecmp($last_tag, 'b') === 0)
								{
									$result .= '</b>';
								}
								elseif(strcasecmp($last_tag, 'color') === 0)
								{
									$result .= '</span>';
								}
								elseif(strcasecmp($last_tag, 'i') === 0)
								{
									$result .= '</i>';
								}
								elseif(strcasecmp($last_tag, 'image') === 0)
								{
									// $result .= '</img>';
								}
								elseif(strcasecmp($last_tag, 'quote') === 0)
								{
									$result .= '</blockquote>';
								}
								elseif(strcasecmp($last_tag, 's') === 0)
								{
									$result .= '</strike>';
								}
								elseif(strcasecmp($last_tag, 'sub') === 0)
								{
									$result .= '</sub>';
								}
								elseif(strcasecmp($last_tag, 'sup') === 0)
								{
									$result .= '</sup>';
								}
								elseif(strcasecmp($last_tag, 'u') === 0)
								{
									$result .= '</u>';
								}
								elseif(strcasecmp($last_tag, 'url') === 0)
								{
									$result .= '</a>';
								}
							}
						}
						else
						{
							// Ignore closing tag
						}
					}
					elseif((strcasecmp(substr($match, 0, 1), '[') === 0) && (strcasecmp(substr($match, -1), ']') === 0)) // Opening tag? [tag="arg"]
					{
						if(($index = strpos($match, '=')) !== false) 
						{
							$tag = strtolower(substr($match, 1, $index - 1));
							$arg = (substr($match, $index + 2, -2));

							// Remove escape character in $arg
							$arg = str_replace('\\\\', '\\', $arg);
							$arg = str_replace('\"', '"', $arg);
						}
						else
						{
							$tag = strtolower(substr($match, 1, -1));
							$arg = '';
						}

						// Add tag to stack
						$open_tags[] = $tag;

						if(strcasecmp($tag, 'align') === 0)
						{
							$arg = strtolower($arg);
							
							if(in_array($arg, array('left', 'center', 'right', 'justify')) === false)
							{
								$arg = 'left';
							}

							$result .= '<div style="text-align: ' . escapeHtml($arg) . ';">';
						}
						elseif(strcasecmp($tag, 'b') === 0)
						{
							$result .= '<b>';
						}
						elseif(strcasecmp($tag, 'code') === 0)
						{
							$result .= '<code' . ((strlen($arg) > 0) ? ' class="' . escapeHtml(strtolower($arg)) . '"' : '') . '>';

							// Open block
							$block = array("tag" => $tag, "arg" => $arg);
						}
						elseif(strcasecmp($tag, 'color') === 0)
						{
							if(strlen($arg) > 1)
							{
								$result .= '<span style="color: ' . escapeHtml(strtoupper($arg)) . ';">';
							}
							else
							{
								$result .= '<span>';
							}
						}
						elseif(strcasecmp($tag, 'i') === 0)
						{
							$result .= '<i>';
						}
						elseif(strcasecmp($tag, 'image') === 0)
						{
							if(strcasecmp($matches[$i + 1], '[/image]') === 0) // Next match is closing tag
							{
								$result .= '<img alt="' . escapeHtml($arg) . '" border="0" src="' . escapeHtml($arg) . '">';
							}
							else // // Lookup image alt_text & title
							{
								$title = '';

								while((($i + 1) < sizeof($matches)) && (strcasecmp($matches[$i + 1], '[/image]') !== 0))
								{
									$title .= $matches[$i + 1];
									$i++;
								}

								$result .= '<img alt="' . escapeHtml($title) . '" border="0" src="' . escapeHtml($arg) . '" title="' . escapeHtml($title) . '">';
							}
						}
						elseif(strcasecmp($tag, 'quote') === 0)
						{
							$result .= '<bockquote>';

							if(strlen($arg) > 0)
							{
								$result .= '<em>' . escapeHtml($arg) . '</em><br>';
							}
						}
						elseif(strcasecmp($tag, 's') === 0)
						{
							$result .= '<strike>';
						}
						elseif(strcasecmp($tag, 'sub') === 0)
						{
							$result .= '<sub>';
						}
						elseif(strcasecmp($tag, 'sup') === 0)
						{
							$result .= '<sup>';
						}
						elseif(strcasecmp($tag, 'u') === 0)
						{
							$result .= '<u>';
						}
						elseif(strcasecmp($tag, 'url') === 0)
						{
							$result .= '<a href="' . escapeHtml($arg) . '" target="_blank">';
						}
						else
						{
							// Ignore tag
							$result .= stringToHtml($match);
						}
					}
					else // Text
					{
						$result .= stringToHtml($match);
					}
				}
			}
		}

		// Close unclosed tags
		while(sizeof($open_tags) > 0)
		{
			$tag = array_pop($open_tags);

			if(strcasecmp($last_tag, 'align') === 0)
			{
				$result .= '</div>';
			}
			elseif(strcasecmp($tag, 'b') === 0)
			{
				$result .= '</b>';
			}
			elseif(strcasecmp($tag, 'code') === 0)
			{
				$result .= '</code>';
			}
			elseif(strcasecmp($tag, 'color') === 0)
			{
				$result .= '</span>';
			}
			elseif(strcasecmp($tag, 'i') === 0)
			{
				$result .= '</i>';
			}
			elseif(strcasecmp($tag, 'image') === 0)
			{
				// $result .= '</img>';
			}
			elseif(strcasecmp($tag, 'quote') === 0)
			{
				$result .= '</blockquote>';
			}
			elseif(strcasecmp($tag, 's') === 0)
			{
				$result .= '</strike>';
			}
			elseif(strcasecmp($tag, 'sub') === 0)
			{
				$result .= '</sub>';
			}
			elseif(strcasecmp($tag, 'sup') === 0)
			{
				$result .= '</sup>';
			}
			elseif(strcasecmp($tag, 'u') === 0)
			{
				$result .= '</u>';
			}
			elseif(strcasecmp($tag, 'url') === 0)
			{
				$result .= '</a>';
			}
		}

		return $result;
	}

	// Split on all available UBB tag
	function __splitUbb($string)
	{
		$aResult = array();

		$bEscaped = false;
		$bTagged = false;
		$bQuoted = false;
		$sBuffer = '';

		$iLength = strlen($string);
		for($i = 0; $i < $iLength; $i++)
		{
			$char = $string[$i];

			if((strcmp($char, '"') === 0) && ($bEscaped === false) && $bTagged) // Quotes have special meaning
			{
				if($bQuoted)
				{
					$bQuoted = false; // Set quoted flag
				}
				else
				{
					$bQuoted = true; // Set quoted flag
				}

				$sBuffer .= $char; // Add quote to buffer
			}
			elseif((strcmp($char, '[') === 0) && ($bQuoted === false)) // A new tag is starting
			{
				if(strlen($sBuffer) > 0)
				{
					$aResult[] = $sBuffer; // Add buffer to result
					$sBuffer = ''; // Clear buffer
				}

				$sBuffer .= $char; // Add opening bracket to buffer
				$bTagged = true; // Set tag flag
			}
			elseif((strcmp($char, ']') === 0) && ($bQuoted === false) && $bTagged) // A tag is closing
			{
				$sBuffer .= $char; // Add closing bracket to buffer
				$aResult[] = $sBuffer; // Add buffer to result
				$sBuffer = ''; // Clear buffer

				$bTagged = false; // Set tag flag
			}
			else
			{
				// Catch escape character (backslash)
				if((strcmp($char, "\\") === 0) && $bQuoted)
				{
					$bEscaped = ($bEscaped === false);
				}
				else
				{
					$bEscaped = false;
				}

				$sBuffer .= $char;
			}
		}

		if(strlen($sBuffer) > 0)
		{
			$aResult[] = $sBuffer;
		}

		return $aResult;
	}

?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
	<head>
		<title>UBB Editor</title>
		<script type="text/javascript" src="js/ubbeditor.js"></script>
		<link href="css/default.css" rel="stylesheet">
	</head>
	<body>

			<h1>ubbToHtml() output:</h1>

<!--UBB_OUTPUT-->
<?php

	if(isset($_POST["editor_content"]))
	{
		$code = stripslashes($_POST["editor_content"]);	
	}
	else
	{
		$code = 'Hello world!';
	}

	echo ubbToHtml($code) . '';

?>

<!--/UBB_OUTPUT-->

		<hr>
		<h1>UBB Editor</h1>

		<div class="ubb_menu">
			<table border="0" cellpadding="0" cellspacing="0">
				<tr>
					<td><a href="javascript: my_ubb_editor.clear();" title="new"><img alt="new" border="0" src="images/new.gif"></a></td>
					<td><img border="0" src="images/seperator.gif"></td>
					<td><a href="javascript: my_ubb_editor.setBold();" title="bold"><img alt="[b]" border="0" src="images/bold.gif"></a></td>
					<td><a href="javascript: my_ubb_editor.setUnderline();" title="underline"><img alt="[u]" border="0" src="images/underline.gif"></a></td>
					<td><a href="javascript: my_ubb_editor.setItalic();" title="italic"><img alt="[i]" border="0" src="images/italic.gif"></a></td>
					<td><a href="javascript: my_ubb_editor.setStrike();" title="strike"><img alt="[s]" border="0" src="images/strike.gif"></a></td>
					<td><img border="0" src="images/seperator.gif"></td>
					<td><a href="javascript: my_ubb_editor.setColor();" title="font color"><img alt="[color]" border="0" src="images/fontcolor.gif"></a></td>
					<td><img border="0" src="images/seperator.gif"></td>
					<td><a href="javascript: my_ubb_editor.setAlign('left');" title="align left"><img alt="[align]" border="0" src="images/alignleft.gif"></a></td>
					<td><a href="javascript: my_ubb_editor.setAlign('center');" title="align center"><img alt="[align]" border="0" src="images/aligncenter.gif"></a></td>
					<td><a href="javascript: my_ubb_editor.setAlign('right');" title="align right"><img alt="[align]" border="0" src="images/alignright.gif"></a></td>
					<td><a href="javascript: my_ubb_editor.setAlign('justify');" title="align justify"><img alt="[align]" border="0" src="images/alignjustify.gif"></a></td>
					<td><img border="0" src="images/seperator.gif"></td>
					<td><a href="javascript: my_ubb_editor.setSubscript();" title="sub"><img alt="[sub]" border="0" src="images/subscript.gif"></a></td>
					<td><a href="javascript: my_ubb_editor.setSuperscript();" title="sup"><img alt="[sup]" border="0" src="images/superscript.gif"></a></td>
					<td><img border="0" src="images/seperator.gif"></td>
					<td><a href="javascript: my_ubb_editor.setImage();" title="image"><img alt="[image]" border="0" src="images/image.gif"></a></td>
					<td><a href="javascript: my_ubb_editor.setUrl();" title="url"><img alt="[url]" border="0" src="images/link.gif"></a></td>
					<td><img border="0" src="images/seperator.gif"></td>
					<td><a href="javascript: my_ubb_editor.setCode();" title="code"><img alt="[code]" border="0" src="images/code.gif"></a></td>
					<td><img border="0" src="images/seperator.gif"></td>
					<td>
						<!-- IMAGES -->
						<select class="select" onchange="javascript: my_ubb_editor.setImage(this.value);">
							<option value="">---------- images ----------</option>
							<option value="http://www.domain.tld/001.jpg">plaatje 1</option>
							<option value="http://www.domain.tld/002.jpg">plaatje 2</option>
							<option value="http://www.domain.tld/003.jpg">plaatje 3</option>
						</select>
					</td>
					<td>
						<!-- LINKS -->
						<select class="select" onchange="javascript: my_ubb_editor.setUrl(this.value);">
							<option value="">---------- links ----------</option>
							<option value="http://www.link1.tld/">link 1</option>
							<option value="http://www.link2.tld/">link 2</option>
							<option value="http://www.link3.tld/">link 3</option>
						</select>
					</td>
				</tr>
			</table>
		</div>

		<form action="" method="post">
			<textarea id="editor_content" name="editor_content" style="width: 800; height: 300px;"><?php echo escapeHtml($code); ?></textarea>
			<br>
			<input name="submit" type="submit" value="submit">
		</form>

		<script type="text/javascript">

			// Init editor
			var my_ubb_editor = new UBBEditor('editor_content');

		</script>
	</body>
</html>
[/code]

----- end of file -----