Index: /CKEditor/branches/prototype/_dev/docs_build/docs_build.bat
===================================================================
--- /CKEditor/branches/prototype/_dev/docs_build/docs_build.bat	(revision 2333)
+++ /CKEditor/branches/prototype/_dev/docs_build/docs_build.bat	(revision 2334)
@@ -29,5 +29,5 @@
 java -jar ../_thirdparty/jsdoc-toolkit/jsrun.jar ../_thirdparty/jsdoc-toolkit/app/run.js -c=docs_build.conf
 
-php ../fixlineends/fixlineends.php --eolstripwhite --eofnewline --eofstripwhite --nohidden --nosystem ../../_docs/api/
+:: php ../fixlineends/fixlineends.php --eolstripwhite --eofnewline --eofstripwhite --nohidden --nosystem ../../_docs/api/
 
 ECHO Finished!
Index: /CKEditor/branches/prototype/_dev/docs_build/docs_build.conf
===================================================================
--- /CKEditor/branches/prototype/_dev/docs_build/docs_build.conf	(revision 2333)
+++ /CKEditor/branches/prototype/_dev/docs_build/docs_build.conf	(revision 2334)
@@ -17,4 +17,5 @@
 		'../../_source/core/',
 		'../../_source/plugins/',
+		'../../_source/dtd/xhtml1-transitional.js',
 		'../../_source/lang/en.js'
 	],
Index: /CKEditor/branches/prototype/_dev/packager/fckpackager.php
===================================================================
--- /CKEditor/branches/prototype/_dev/packager/fckpackager.php	(revision 2333)
+++ /CKEditor/branches/prototype/_dev/packager/fckpackager.php	(revision 2334)
@@ -234,5 +234,5 @@
 			{
 				$varsMatch = $varsMatches[1][$i];
-				
+
 				// Removed all (...), [...] and {...} blocks from the var
 				// statement to avoid problems with commas inside them.
@@ -240,7 +240,7 @@
 				$varsMatch = preg_replace( '/(\[(?:(?>[^\[\]]*)|(?1))*\])+/', '', $varsMatch ) ;
 				$varsMatch = preg_replace( '/({(?:(?>[^{}]*)|(?1))*})+/', '', $varsMatch ) ;
-				
+
 				$numVarNameMatches = preg_match_all( '/(?:^|,)\s*([^\s=,]+)/', $varsMatch, $varNameMatches ) ;
-				
+
 				for ( $j = 0 ; $j < $numVarNameMatches ; $j++ )
 				{
@@ -260,5 +260,11 @@
 		{
 			if ( strlen( $var) > 1 )
-				$source = preg_replace( '/(?<!\w|\d|\.)' . preg_quote( $var ) . '(?!\w|\d)/', $this->_GetVarName(), $source ) ;
+			{
+				$varName = $this->_GetVarName();
+				$source = preg_replace( '/(?<!\w|\d|\.)' . preg_quote( $var ) . '(?!\w|\d|:)/', $varName, $source ) ;
+
+				// The above regex exclude names placed before ":", but it is not true for ternary operators.
+				$source = preg_replace( '/(?<=\?)' . preg_quote( $var ) . '(?=:)/', $varName, $source ) ;
+			}
 		}
 
@@ -331,5 +337,5 @@
 				'/\/\/.*$/m',
 				'', $script ) ;
-		
+
 		// Remove spaces before the ";" at the end of the lines
 		$script = preg_replace(
@@ -431,5 +437,5 @@
 
 		$processed = $GLOBALS['funcProcessor']->Process() ;
-		
+
 		$processed = substr_replace( $processed, '', 0, 8 ) ;
 
@@ -438,5 +444,5 @@
 		if ( $hasfuncProcessor != TRUE )
 			unset( $GLOBALS['funcProcessor'] ) ;
-		
+
 		return 'function'. $processed ;
 	}
Index: /CKEditor/branches/prototype/_source/core/config.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/config.js	(revision 2333)
+++ /CKEditor/branches/prototype/_source/core/config.js	(revision 2334)
@@ -136,5 +136,5 @@
 	 * config.plugins = 'editingblock,toolbar,wysiwygarea';
 	 */
-	plugins : 'basicstyles,button,editingblock,elementspath,htmldataprocessor,selection,sourcearea,toolbar,wysiwygarea',
+	plugins : 'basicstyles,button,editingblock,elementspath,htmldataprocessor,htmlwriter,selection,sourcearea,toolbar,wysiwygarea',
 
 	/**
Index: /CKEditor/branches/prototype/_source/core/dom/node.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/node.js	(revision 2333)
+++ /CKEditor/branches/prototype/_source/core/dom/node.js	(revision 2334)
@@ -42,8 +42,8 @@
 		switch ( domNode.nodeType )
 		{
-			case 1 :	// ELEMENT_NODE
+			case CKEDITOR.NODE_ELEMENT :
 				return new CKEDITOR.dom.element( domNode );
 
-			case 3 :	// TEXT_NODE
+			case CKEDITOR.NODE_TEXT :
 				return new CKEDITOR.dom.text( domNode );
 		}
@@ -63,4 +63,25 @@
 
 CKEDITOR.dom.node.prototype = new CKEDITOR.dom.domObject();
+
+/**
+ * Element node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_ELEMENT	= 1;
+
+/**
+ * Text node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_TEXT		= 3;
+
+/**
+ * Comment node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_COMMENT	= 8;
 
 CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype,
Index: /CKEditor/branches/prototype/_source/core/htmlparser.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/htmlparser.js	(revision 2334)
+++ /CKEditor/branches/prototype/_source/core/htmlparser.js	(revision 2334)
@@ -0,0 +1,174 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+/**
+ * HTML text parser.
+ * @constructor
+ * @example
+ */
+CKEDITOR.htmlParser = function()
+{};
+
+(function()
+{
+	var htmlPartsRegex	= /<(?:(?:\/([^>]+)>)|(?:!--(.*?)-->)|(?:([^\s>]+)\s*((?:(?:[^"'>]+)|(?:"[^"]*")|(?:'[^']*'))*)\/?>))/g,
+		attribsRegex	= /([\w:]+)(?:=(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))?/g,
+		emptyAttribs	= {checked:1,compact:1,declare:1,defer:1,disabled:1,ismap:1,multiple:1,nohref:1,noresize:1,noshade:1,nowrap:1,readonly:1,selected:1};
+
+	CKEDITOR.htmlParser.prototype =
+	{
+		/**
+		 * Function to be fired when a tag opener is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} tagName The tag name. The name is guarantted to be
+		 *		lowercased.
+		 * @param {Object} attributes An object containing all tag attributes. Each
+		 *		property in this object represent and attribute name and its
+		 *		value is the attribute value.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * parser.onTagOpener = function( tagName, attributes )
+		 *     {
+		 *         alert( tagName );  // e.g. "b"
+		 *     });
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		onTagOpen	: function() {},
+
+		/**
+		 * Function to be fired when a tag closer is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} tagName The tag name. The name is guarantted to be
+		 *		lowercased.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * parser.onTagClose = function( tagName )
+		 *     {
+		 *         alert( tagName );  // e.g. "b"
+		 *     });
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		onTagClose	: function() {},
+
+		/**
+		 * Function to be fired when text is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} text The text found.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * parser.onText = function( text )
+		 *     {
+		 *         alert( text );  // e.g. "Hello"
+		 *     });
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		onText		: function() {},
+
+		/**
+		 * Function to be fired when a commend is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} comment The comment text.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * parser.onText = function( comment )
+		 *     {
+		 *         alert( comment );  // e.g. " Example "
+		 *     });
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		onComment	: function() {},
+
+		/**
+		 * Parses text, looking for HTML tokens, like tag openers or closers,
+		 * or comments. This function fires the onTagOpen, onTagClose, onText
+		 * and onComment function during its execution.
+		 * @param {String} html The HTML to be parsed.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * // The onTagOpen, onTagClose, onText and onComment should be overriden
+		 * // at this point.
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		parse : function( html )
+		{
+			var parts,
+				tagName,
+				nextIndex = 0;
+
+			while ( ( parts = htmlPartsRegex.exec( html ) ) )
+			{
+				var tagIndex = parts.index;
+				if ( tagIndex > nextIndex )
+					this.onText( html.substring( nextIndex, tagIndex ) );
+
+				nextIndex = htmlPartsRegex.lastIndex;
+
+				/*
+				 "parts" is an array with the following items:
+					0 : The entire match (not used)
+					1 : Group filled with the tag name for closing tags.
+					2 : Group filled with the comment text.
+					3 : Group filled with the tag name for opening tags.
+					4 : Group filled with the attributes part of opening tags.
+				 */
+
+				// Closing tag
+				if ( ( tagName = parts[ 1 ] ) )
+				{
+					this.onTagClose( tagName.toLowerCase() );
+					continue;
+				}
+
+				// Opening tag
+				if ( ( tagName = parts[ 3 ] ) )
+				{
+					var attribs = {},
+						attribMatch,
+						attribsPart = parts[ 4 ];
+
+					if ( attribsPart )
+					{
+						while ( ( attribMatch = attribsRegex.exec( attribsPart ) ) )
+						{
+							var attName = attribMatch[1].toLowerCase(),
+								attValue = attribMatch[2] || attribMatch[3] || attribMatch[4];
+
+							if ( !attValue && emptyAttribs[ attName ] )
+								attribs[ attName ] = attName;
+							else
+								attribs[ attName ] = attValue;
+						}
+					}
+
+					this.onTagOpen( tagName.toLowerCase(), attribs );
+					continue;
+				}
+
+				// Comment
+				if( ( tagName = parts[ 2 ] ) )
+					this.onComment( tagName );
+			}
+
+			if ( html.length > nextIndex )
+				this.onText( html.substring( nextIndex, html.length ) );
+		}
+	};
+})();
Index: /CKEditor/branches/prototype/_source/core/htmlparser/comment.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/htmlparser/comment.js	(revision 2334)
+++ /CKEditor/branches/prototype/_source/core/htmlparser/comment.js	(revision 2334)
@@ -0,0 +1,61 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+/**
+ * A lightweight representation of an HTML comment.
+ * @constructor
+ * @example
+ */
+CKEDITOR.htmlParser.comment = function( value )
+{
+	/**
+	 * The comment text.
+	 * @type String
+	 * @example
+	 */
+	this.value = value;
+
+	/** @private */
+	this._ =
+	{
+		isBlockLike : false
+	};
+};
+
+CKEDITOR.htmlParser.comment.prototype =
+{
+	/**
+	 * The node type. This is a constant value set to {@link CKEDITOR.NODE_COMMENT}.
+	 * @type Number
+	 * @example
+	 */
+	type : CKEDITOR.NODE_COMMENT,
+
+	/**
+	 * Writes the HTML representation of this comment to a CKEDITOR.htmlWriter.
+	 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
+	 * @example
+	 */
+	writeHtml : function( writer )
+	{
+		writer.comment( this.value );
+	}
+};
Index: /CKEditor/branches/prototype/_source/core/htmlparser/element.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/htmlparser/element.js	(revision 2334)
+++ /CKEditor/branches/prototype/_source/core/htmlparser/element.js	(revision 2334)
@@ -0,0 +1,145 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+/**
+ * A lightweight representation of an HTML element.
+ * @param {String} name The element name.
+ * @param {Object} attributes And object holding all attributes defined for
+ *		this element.
+ * @constructor
+ * @example
+ */
+CKEDITOR.htmlParser.element = function( name, attributes )
+{
+	/**
+	 * The element name.
+	 * @type String
+	 * @example
+	 */
+	this.name = name;
+
+	/**
+	 * Holds the attributes defined for this element.
+	 * @type Object
+	 * @example
+	 */
+	this.attributes = attributes;
+
+	/**
+	 * The nodes that are direct children of this element.
+	 * @type Array
+	 * @example
+	 */
+	this.children = [];
+
+	var dtd			= CKEDITOR.dtd,
+		isBlockLike	= !!( dtd.$block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] ),
+		isEmpty		= !!dtd.$empty[ name ];
+
+	/** @private */
+	this._ =
+	{
+		isBlockLike : isBlockLike,
+		isEmpty : isEmpty,
+		hasInlineStarted : isEmpty || !isBlockLike
+	};
+};
+
+(function()
+{
+	// Used to sort attribute entries in an array, where the first element of
+	// each object is the attribute name.
+	var sortAttribs = function( a, b )
+	{
+		a = a[0];
+		b = b[0];
+		return a < b ? -1 : a > b ? 1 : 0;
+	};
+
+	CKEDITOR.htmlParser.element.prototype =
+	{
+		/**
+		 * The node type. This is a constant value set to {@link CKEDITOR.NODE_ELEMENT}.
+		 * @type Number
+		 * @example
+		 */
+		type : CKEDITOR.NODE_ELEMENT,
+
+		/**
+		 * Adds a node to the element children list.
+		 * @param {Object} node The node to be added. It can be any of of the
+		 *		following types: {@link CKEDITOR.htmlParser.element},
+		 *		{@link CKEDITOR.htmlParser.text} and
+		 *		{@link CKEDITOR.htmlParser.comment}.
+		 * @function
+		 * @example
+		 */
+		add : CKEDITOR.htmlParser.fragment.prototype.add,
+
+		/**
+		 * Clone this element.
+		 * @returns {CKEDITOR.htmlParser.element} The element clone.
+		 * @example
+		 */
+		clone : function()
+		{
+			return new CKEDITOR.htmlParser.element( this.name, this.attributes );
+		},
+
+		/**
+		 * Writes the element HTML to a CKEDITOR.htmlWriter.
+		 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
+		 * @example
+		 */
+		writeHtml : function( writer )
+		{
+			// Open element tag.
+			writer.openTag( this.name, this.attributes );
+
+			// Copy all attributes to an array.
+			var attribsArray = [];
+			for ( var a in this.attributes )
+				attribsArray.push( [ a, this.attributes[ a ] ] );
+
+			// Sort the attributes by name.
+			attribsArray.sort( sortAttribs );
+
+			// Send the attributes.
+			for ( var i = 0, len = attribsArray.length ; i < len ; i++ )
+			{
+				var attrib = attribsArray[ i ];
+				writer.attribute( attrib[0], attrib[1] );
+			}
+
+			// Close the tag.
+			writer.openTagClose( this.name, this._.isEmpty );
+
+			if ( !this._.isEmpty )
+			{
+				// Send children.
+				CKEDITOR.htmlParser.fragment.prototype.writeHtml.apply( this, arguments );
+
+				// Close the element.
+				writer.closeTag( this.name );
+			}
+		}
+	};
+})();
Index: /CKEditor/branches/prototype/_source/core/htmlparser/fragment.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/htmlparser/fragment.js	(revision 2334)
+++ /CKEditor/branches/prototype/_source/core/htmlparser/fragment.js	(revision 2334)
@@ -0,0 +1,294 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+/**
+ * A lightweight representation of an HTML DOM structure.
+ * @constructor
+ * @example
+ */
+CKEDITOR.htmlParser.fragment = function()
+{
+	/**
+	 * The nodes contained in the root of this fragment.
+	 * @type Array
+	 * @example
+	 * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
+	 * alert( fragment.children.length );  "2"
+	 */
+	this.children = [];
+
+	/**
+	 * Get the fragment parent. Should always be null.
+	 * @type Object
+	 * @default null
+	 * @example
+	 */
+	this.parent = null;
+
+	/** @private */
+	this._ =
+	{
+		isBlockLike : true,
+		hasInlineStarted : false
+	};
+};
+
+(function()
+{
+	// Elements which the end tag is marked as optional in the HTML 4.01 DTD
+	// (expect empty elements).
+	var optionalClose = {colgroup:1,dd:1,dt:1,li:1,option:1,p:1,td:1,tfoot:1,th:1,thead:1,tr:1};
+
+	/**
+	 * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
+	 * @param {String} fragmentHtml The HTML to be parsed, filling the fragment.
+	 * @returns CKEDITOR.htmlParser.fragment The fragment created.
+	 * @example
+	 * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
+	 * alert( fragment.children[0].name );  "b"
+	 * alert( fragment.children[1].value );  " Text"
+	 */
+	CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml )
+	{
+		var parser = new CKEDITOR.htmlParser(),
+			html = [],
+			fragment = new CKEDITOR.htmlParser.fragment(),
+			pendingInline = [],
+			currentNode = fragment;
+
+		var checkPending = function( newTagName )
+		{
+			if ( pendingInline.length > 0 )
+			{
+				for ( var i = 0 ; i < pendingInline.length ; i++ )
+				{
+					var pendingElement = pendingInline[ i ],
+						pendingName = pendingElement.name,
+						pendingDtd = CKEDITOR.dtd[ pendingName ],
+						currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ];
+
+					if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] ) )
+					{
+						// Get a clone for the pending element.
+						pendingElement = pendingElement.clone();
+
+						// Add it to the current node and make it the current,
+						// so the new element will be added inside of it.
+						currentNode.add( pendingElement );
+						currentNode = pendingElement;
+
+						// Remove the pending element (back the index by one
+						// to properly process the next entry).
+						pendingInline.splice( i, 1 );
+						i--;
+					}
+				}
+			}
+		};
+
+		parser.onTagOpen = function( tagName, attributes )
+		{
+			var element = new CKEDITOR.htmlParser.element( tagName, attributes );
+
+			// This is a tag to be removed if empty, so do not add it immediately.
+			if ( CKEDITOR.dtd.$removeEmpty[ tagName ] )
+			{
+				pendingInline.push( element );
+				return;
+			}
+
+			var currentName = currentNode.name,
+				currentDtd = ( currentName && CKEDITOR.dtd[ currentName ] ) || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span );
+
+			// If the element cannot be child of the current element.
+			if ( !currentDtd[ tagName ] )
+			{
+				// If this is the fragment node, just ignore this tag and add
+				// its children.
+				if ( !currentName )
+					return;
+
+				var reApply = false;
+
+				// If the element name is the same as the current element name,
+				// then just close the current one an append the new one to the
+				// parent. This situation usually happens with <p>, <li>, <dt> and
+				// <dd>, specially in IE.
+				if ( tagName != currentName )
+				{
+					// If it is optional to close the current element, then
+					// close it at this point and simply add the new
+					// alement after it.
+					if ( !optionalClose[ currentName ] )
+					{
+						// The current element is an inline element, which
+						// cannot hold the new one. Put it in the pending list,
+						// and try adding the new one after it.
+						pendingInline.unshift( currentNode );
+					}
+
+					reApply = true;
+				}
+
+				// In any of the following cases, we'll be adding, or trying to
+				// add it to the parent.
+				currentNode = currentNode.parent;
+
+				if ( reApply )
+				{
+					parser.onTagOpen.apply( this, arguments );
+					return;
+				}
+			}
+
+			checkPending( tagName );
+
+			currentNode.add( element );
+
+			if ( !element._.isEmpty )
+				currentNode = element;
+		};
+
+		parser.onTagClose = function( tagName )
+		{
+			var closingElement = currentNode,
+				index = 0;
+
+			while ( closingElement && closingElement.name != tagName )
+			{
+				// If this is an inline element, add it to the pending list, so
+				// it will continue after the closing tag.
+				if ( !closingElement._.isBlockLike )
+				{
+					pendingInline.unshift( closingElement );
+
+					// Increase the index, so it will not get checked again in
+					// the pending list check that follows.
+					index++;
+				}
+
+				closingElement = closingElement.parent;
+			}
+
+			if ( closingElement )
+				currentNode = closingElement.parent;
+			else if ( pendingInline.length > index )
+			{
+				// If we didn't find any parent to be closed, let's check the
+				// peding list.
+				for ( ; index < pendingInline.length ; index++ )
+				{
+					// If found, just remove it from the list.
+					if ( tagName == pendingInline[ index ].name )
+					{
+						pendingInline.splice( index, 1 );
+
+						// Decrease the index so we continue from the next one.
+						index--;
+					}
+				}
+			}
+		};
+
+		parser.onText = function( text )
+		{
+			if ( !currentNode._.hasInlineStarted )
+			{
+				text = CKEDITOR.tools.ltrim( text );
+
+				if ( text.length === 0 )
+					return;
+			}
+
+			checkPending();
+			currentNode.add( new CKEDITOR.htmlParser.text( text ) );
+		};
+
+		parser.onComment = function( comment )
+		{
+			currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );
+		};
+
+		parser.parse( fragmentHtml );
+
+		return fragment;
+	};
+
+	CKEDITOR.htmlParser.fragment.prototype =
+	{
+		/**
+		 * Adds a node to this fragment.
+		 * @param {Object} node The node to be added. It can be any of of the
+		 *		following types: {@link CKEDITOR.htmlParser.element},
+		 *		{@link CKEDITOR.htmlParser.text} and
+		 *		{@link CKEDITOR.htmlParser.comment}.
+		 * @example
+		 */
+		add : function( node )
+		{
+			var len = this.children.length,
+				previous = len > 0 && this.children[ len - 1 ] || null;
+
+			if ( previous )
+			{
+				// If the block to be appended is following text, trim spaces at
+				// the right of it.
+				if ( node._.isBlockLike && previous.type == CKEDITOR.NODE_TEXT )
+				{
+					previous.value = CKEDITOR.tools.rtrim( previous.value );
+
+					// If we have completely cleared the previous node.
+					if ( previous.value.length === 0 )
+					{
+						// Remove it from the list and add the node again.
+						this.children.pop();
+						this.add( node );
+						return;
+					}
+				}
+
+				previous.next = node;
+			}
+
+			node.previous = previous;
+			node.parent = this;
+
+			this.children.push( node );
+
+			this._.hasInlineStarted = node.type == CKEDITOR.NODE_TEXT || ( node.type == CKEDITOR.NODE_ELEMENT && !node._.isBlockLike );
+		},
+
+		/**
+		 * Writes the fragment HTML to a CKEDITOR.htmlWriter.
+		 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
+		 * @example
+		 * var writer = new CKEDITOR.htmlWriter();
+		 * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '&lt;P&gt;&lt;B&gt;Example' );
+		 * fragment.writeHtml( writer )
+		 * alert( writer.getHtml() );  "&lt;p&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/p&gt;"
+		 */
+		writeHtml : function( writer )
+		{
+			for ( var i = 0, len = this.children.length ; i < len ; i++ )
+				this.children[i].writeHtml( writer );
+		}
+	};
+})();
Index: /CKEditor/branches/prototype/_source/core/htmlparser/text.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/htmlparser/text.js	(revision 2334)
+++ /CKEditor/branches/prototype/_source/core/htmlparser/text.js	(revision 2334)
@@ -0,0 +1,66 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+(function()
+{
+	var spacesRegex = /[\t\r\n ]{2,}|[\t\r\n]/g;
+
+	/**
+	 * A lightweight representation of HTML text.
+	 * @constructor
+	 * @example
+	 */
+ 	CKEDITOR.htmlParser.text = function( value )
+	{
+		/**
+		 * The text value.
+		 * @type String
+		 * @example
+		 */
+		this.value = value.replace( spacesRegex, ' ' );
+
+		/** @private */
+		this._ =
+		{
+			isBlockLike : false
+		};
+	};
+
+	CKEDITOR.htmlParser.text.prototype =
+	{
+		/**
+		 * The node type. This is a constant value set to {@link CKEDITOR.NODE_TEXT}.
+		 * @type Number
+		 * @example
+		 */
+		type : CKEDITOR.NODE_TEXT,
+
+		/**
+		 * Writes the HTML representation of this text to a CKEDITOR.htmlWriter.
+		 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
+		 * @example
+		 */
+		writeHtml : function( writer )
+		{
+			writer.text( this.value );
+		}
+	};
+})();
Index: /CKEditor/branches/prototype/_source/core/loader.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/loader.js	(revision 2333)
+++ /CKEditor/branches/prototype/_source/core/loader.js	(revision 2334)
@@ -38,9 +38,9 @@
 	var scripts =
 	{
-		'lang/en'				: [],	// TODO: Remove me
+		'lang/en'				: [],	// TODO: Remove me. This should be handled by the localization code.
 
 		'core/_bootstrap'		: [ 'core/config', 'core/ckeditor', 'core/plugins', 'core/scriptLoader', 'core/tools', /* The following are entries that we wnat to force loading to at the end to avoid dependence recursion */ 'core/dom/text' ],
 		'core/ajax'				: [ 'core/xml' ],
-		'core/ckeditor'			: [ 'core/ajax', 'core/ckeditor_basic', 'core/dom', 'core/dom/document', 'core/dom/element', 'core/editor', 'core/event', 'core/tools' ],
+		'core/ckeditor'			: [ 'core/ajax', 'core/ckeditor_basic', 'core/dom', 'core/dom/document', 'core/dom/element', 'core/editor', 'core/event', 'core/htmlparser', 'core/htmlparser/element', 'core/htmlparser/fragment', 'core/tools', 'dtd/xhtml1-transitional' ],
 		'core/ckeditor_base'	: [],
 		'core/ckeditor_basic'	: [ 'core/env', 'core/event' ],
@@ -57,4 +57,9 @@
 		'core/env'				: [],
 		'core/event'			: [],
+		'core/htmlparser'		: [],
+		'core/htmlparser/comment'	: [ 'core/htmlparser' ],
+		'core/htmlparser/element'	: [ 'core/htmlparser', 'core/htmlparser/fragment' ],
+		'core/htmlparser/fragment'	: [ 'core/htmlparser', 'core/htmlparser/comment', 'core/htmlparser/text' ],
+		'core/htmlparser/text'		: [ 'core/htmlparser' ],
 		'core/plugins'			: [ 'core/resourceManager' ],
 		'core/resourceManager'	: [ 'core/scriptLoader', 'core/tools' ],
@@ -64,5 +69,7 @@
 		'core/tools'			: [ 'core/env' ],
 		'core/ui'				: [],
-		'core/xml'				: [ 'core/env' ]
+		'core/xml'				: [ 'core/env' ],
+
+		'dtd/xhtml1-transitional' : []
 	};
 
Index: /CKEditor/branches/prototype/_source/core/tools.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/tools.js	(revision 2333)
+++ /CKEditor/branches/prototype/_source/core/tools.js	(revision 2334)
@@ -73,7 +73,9 @@
 	 * already present in the target object <strong>are not</strong> overwritten.
 	 * @param {Object} target The object to be extended.
-	 * @param {Object} source The object from which copy properties.
+	 * @param {Object} source[,souce(n)] The objects from which copy
+	 *		properties. Any number of objects can be passed to this function.
 	 * @param {Boolean} [overwrite] Indicates that properties already present
-	 *		in the target object must be overwritten.
+	 *		in the target object must be overwritten. This must be the last
+	 *		parameter in the function call.
 	 * @returns {Object} the extended object (target).
 	 * @example
@@ -95,11 +97,25 @@
 	 *     alert( p );
 	 */
-	extend : function( target, source, overwrite )
-	{
-		for ( var propertyName in source )
-		{
-			if ( overwrite || target[ propertyName ] == undefined )
-				target[ propertyName ] = source[ propertyName ];
+	extend : function( target )
+	{
+		var argsLength = arguments.length,
+			overwrite = arguments[ argsLength - 1 ];
+
+		if ( typeof overwrite == 'boolean' )
+			argsLength--;
+		else
+			overwrite = false;
+
+		for ( var i = 1 ; i < argsLength ; i++ )
+		{
+			var source = arguments[ i ];
+
+			for ( var propertyName in source )
+			{
+				if ( overwrite || target[ propertyName ] == undefined )
+					target[ propertyName ] = source[ propertyName ];
+			}
 		}
+
 		return target;
 	},
@@ -223,4 +239,58 @@
 			},
 			milliseconds || 0 );
-	}
+	},
+
+	/**
+	 * Remove spaces from the start and the end of a string. The following
+	 * characters are removed: space, tab, line break, line feed.
+	 * @param {String} str The text from which remove the spaces.
+	 * @returns {String} The modified string without the boundary spaces.
+	 * @example
+	 * alert( CKEDITOR.tools.trim( '  example ' );  // "example"
+	 */
+	trim : (function()
+	{
+		// We are not using \s because we don't want "non-breaking spaces" to be caught.
+		var trimRegex = /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g;
+		return function( str )
+		{
+			return str.replace( trimRegex, '' ) ;
+		};
+	})(),
+
+	/**
+	 * Remove spaces from the start (left) of a string. The following
+	 * characters are removed: space, tab, line break, line feed.
+	 * @param {String} str The text from which remove the spaces.
+	 * @returns {String} The modified string excluding the removed spaces.
+	 * @example
+	 * alert( CKEDITOR.tools.ltrim( '  example ' );  // "example "
+	 */
+	ltrim : (function()
+	{
+		// We are not using \s because we don't want "non-breaking spaces" to be caught.
+		var trimRegex = /(?:^[ \t\n\r]+)/g;
+		return function( str )
+		{
+			return str.replace( trimRegex, '' ) ;
+		};
+	})(),
+
+	/**
+	 * Remove spaces from the end (right) of a string. The following
+	 * characters are removed: space, tab, line break, line feed.
+	 * @param {String} str The text from which remove the spaces.
+	 * @returns {String} The modified string excluding the removed spaces.
+	 * @example
+	 * alert( CKEDITOR.tools.ltrim( '  example ' );  // "  example"
+	 */
+	rtrim : (function()
+	{
+		// We are not using \s because we don't want "non-breaking spaces" to be caught.
+		var trimRegex = /(?:[ \t\n\r]+$)/g;
+		return function( str )
+		{
+			return str.replace( trimRegex, '' ) ;
+		};
+	})()
 };
Index: /CKEditor/branches/prototype/_source/dtd/dtd_test.html
===================================================================
--- /CKEditor/branches/prototype/_source/dtd/dtd_test.html	(revision 2334)
+++ /CKEditor/branches/prototype/_source/dtd/dtd_test.html	(revision 2334)
@@ -0,0 +1,81 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>CKEDITOR.dtd Contents</title>
+	<script type="text/javascript">
+	//<![CDATA[
+
+// Define the CKEDITOR namespace.
+var CKEDITOR = {};
+
+	//]]>
+	</script>
+	<script type="text/javascript" src="../core/tools.js"></script>
+	<script type="text/javascript" src="xhtml1-transitional.js"></script>
+</head>
+<body>
+	<h1>
+		CKEDITOR.dtd Contents
+	</h1>
+	<table border="1">
+		<script type="text/javascript">
+		//<![CDATA[
+
+var entries = [];
+
+// Get all entries.
+for ( var p in CKEDITOR.dtd )
+{
+	var children = [];
+	for ( var c in CKEDITOR.dtd[p] )
+	{
+		children.push( c );
+	}
+	children.sort();
+	entries.push( [ p, children.join(', ') ] );
+}
+
+// Sort them alphabetically.
+entries.sort( function( a, b )
+	{
+		a = a[0];
+		b = b[0];
+
+		return a < b ? -1 :
+			a > b ? 1 : 0;
+	});
+
+// Writes the list.
+for ( var i = 0 ; i < entries.length ; i++ )
+{
+	document.write(
+		'<tr><td><b>' + entries[i][0] + '</b></td><td>' +
+		entries[i][1] +
+		'</td></tr>' );
+}
+
+		//]]>
+		</script>
+	</table>
+</body>
+</html>
Index: /CKEditor/branches/prototype/_source/dtd/xhtml1-transitional.js
===================================================================
--- /CKEditor/branches/prototype/_source/dtd/xhtml1-transitional.js	(revision 2334)
+++ /CKEditor/branches/prototype/_source/dtd/xhtml1-transitional.js	(revision 2334)
@@ -0,0 +1,202 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dtd} object, which holds the DTD
+ *		mapping for XHTML 1.0 Transitional. This file was automatically
+ *		generated from the file: xhtml1-transitional.dtd.
+ */
+
+/**
+ * Holds and object representation of the HTML DTD to be used by the editor in
+ * its internal operations.
+ *
+ * Each element in the DTD is represented by a
+ * property in this object. Each property contains the list of elements that
+ * can be contained by the element. Text is represented by the "#" property.
+ *
+ * Several special grouping properties are also available. Their names start
+ * with the "$" character.
+ * @namespace
+ * @example
+ * // Check if "div" can be contained in a "p" element.
+ * alert( !!CKEDITOR.dtd[ 'p' ][ 'div' ] );  "false"
+ * @example
+ * // Check if "p" can be contained in a "div" element.
+ * alert( !!CKEDITOR.dtd[ 'div' ][ 'p' ] );  "true"
+ * @example
+ * // Check if "p" is a block element.
+ * alert( !!CKEDITOR.dtd.$block[ 'p' ] );  "true"
+ */
+CKEDITOR.dtd = (function()
+{
+    var X = CKEDITOR.tools.extend,
+
+		A = {isindex:1,fieldset:1},
+		B = {input:1,button:1,select:1,textarea:1,label:1},
+		C = X({a:1},B),
+		D = X({iframe:1},C),
+		E = {hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1},
+		F = {ins:1,del:1,script:1},
+		G = X({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1},F),
+		H = X({sub:1,img:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1},G),
+		I = X({p:1},H),
+		J = X({iframe:1},H,B),
+		K = {img:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1},
+
+		L = X({a:1},J),
+		M = {tr:1},
+		N = {'#':1},
+		O = X({param:1},K),
+		P = X({form:1},A,D,E,I),
+		Q = {li:1};
+
+    return /** @lends CKEDITOR.dtd */ {
+
+		// The "$" items have been added manually.
+
+		/**
+		 * List of block elements, like "p" or "div".
+		 * @type Object
+		 * @example
+		 */
+		$block : {address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1},
+
+		/**
+		 * List of empty (self-closing) elements, like "br" or "img".
+		 * @type Object
+		 * @example
+		 */
+		$empty : {area:1,base:1,br:1,col:1,hr:1,img:1,input:1,link:1,meta:1,param:1},
+
+		/**
+		 * List of list item elements, like "li" or "dd".
+		 * @type Object
+		 * @example
+		 */
+		$listItem : {dd:1,dt:1,li:1},
+
+		/**
+		 * List of elements that can be ignored if empty, like "b" or "span".
+		 * @type Object
+		 * @example
+		 */
+		$removeEmpty : {abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,dfn:1,em:1,font:1,i:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1},
+
+		/**
+		 * List of elements used inside the "table" element, like "tbody" or "td".
+		 * @type Object
+		 * @example
+		 */
+		$tableContent : {caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1},
+
+        col : {},
+        tr : {td:1,th:1},
+        img : {},
+        colgroup : {col:1},
+        noscript : P,
+        td : P,
+        br : {},
+        th : P,
+        center : P,
+        kbd : L,
+        button : X(I,E),
+        basefont : {},
+        h5 : L,
+        h4 : L,
+        samp : L,
+        h6 : L,
+        ol : Q,
+        h1 : L,
+        h3 : L,
+        option : N,
+        h2 : L,
+        form : X(A,D,E,I),
+        select : {optgroup:1,option:1},
+        font : J,		// Changed from L to J (see (1))
+        ins : P,
+        menu : Q,
+        abbr : L,
+        label : L,
+        table : {thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1},
+        code : L,
+        script : N,
+        tfoot : M,
+        cite : L,
+        li : P,
+        input : {},
+        iframe : P,
+        strong : J,		// Changed from L to J (see (1))
+        textarea : N,
+        noframes : P,
+        big : J,		// Changed from L to J (see (1))
+        small : J,		// Changed from L to J (see (1))
+        span : J,		// Changed from L to J (see (1))
+        hr : {},
+        dt : L,
+        sub : J,		// Changed from L to J (see (1))
+        optgroup : {option:1},
+        param : {},
+        bdo : L,
+        'var' : J,		// Changed from L to J (see (1))
+        div : P,
+        object : O,
+        sup : J,		// Changed from L to J (see (1))
+        dd : P,
+        strike : J,		// Changed from L to J (see (1))
+        area : {},
+        dir : Q,
+        map : X({area:1,form:1,p:1},A,F,E),
+        applet : O,
+        dl : {dt:1,dd:1},
+        del : P,
+        isindex : {},
+        fieldset : X({legend:1},K),
+        thead : M,
+        ul : Q,
+        acronym : L,
+        b : J,			// Changed from L to J (see (1))
+        a : J,
+        blockquote : P,
+        caption : L,
+        i : J,			// Changed from L to J (see (1))
+        u : J,			// Changed from L to J (see (1))
+        tbody : M,
+        s : L,
+        address : X(D,I),
+        tt : J,			// Changed from L to J (see (1))
+        legend : L,
+        q : L,
+        pre : X(G,C),
+        p : L,
+        em : J,			// Changed from L to J (see (1))
+        dfn : L
+    };
+})();
+
+/*
+	Notes:
+	(1) According to the DTD, many elements, like <b> accept <a> elements
+	    inside of them. But, to produce better output results, we have manually
+	    changed the map to avoid breaking the links on pieces, having
+	    "<b>this is a </b><a><b>link</b> test</a>", instead of
+	    "<b>this is a <a>link</a></b><a> test</a>".
+*/
Index: /CKEditor/branches/prototype/_source/plugins/htmldataprocessor/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/htmldataprocessor/plugin.js	(revision 2333)
+++ /CKEditor/branches/prototype/_source/plugins/htmldataprocessor/plugin.js	(revision 2334)
@@ -32,9 +32,17 @@
 			},
 
-			toDataFormat : function( node )
+			toDataFormat : function( element )
 			{
-				// For now, there is no processing of the HTML.
-				return node.getHtml();
-			}
+				var writer = this.writer,
+					fragment = CKEDITOR.htmlParser.fragment.fromHtml( element.getHtml() );
+
+				writer.reset();
+
+				fragment.writeHtml( writer );
+
+				return writer.getHtml( true );
+			},
+
+			writer : new CKEDITOR.htmlWriter()
 		};
 	}
Index: /CKEditor/branches/prototype/_source/plugins/htmlwriter/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/htmlwriter/plugin.js	(revision 2334)
+++ /CKEditor/branches/prototype/_source/plugins/htmlwriter/plugin.js	(revision 2334)
@@ -0,0 +1,318 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+/**
+ * Class used to write HTML data.
+ * @constructor
+ * @example
+ * var writer = new CKEDITOR.htmlWriter();
+ * writer.openTag( 'p' );
+ * writer.attribute( 'class', 'MyClass' );
+ * writer.openTagClose( 'p' );
+ * writer.text( 'Hello' );
+ * writer.closeTag( 'p' );
+ * alert( writer.getHtml() );  "&lt;p class="MyClass"&gt;Hello&lt;/p&gt;"
+ */
+CKEDITOR.htmlWriter = function()
+{
+	/**
+	 * The characters to be used for each identation step.
+	 * @type String
+	 * @default "\t" (tab)
+	 * @example
+	 * // Use two spaces for indentation.
+	 * editorInstance.dataProcessor.writer.indentationChars = '  ';
+	 */
+	this.indentationChars	= '\t';
+
+	/**
+	 * The characters to be used to close "self-closing" elements, like "br" or
+	 * "img".
+	 * @type String
+	 * @default " /&gt;"
+	 * @example
+	 * // Use HTML4 notation for self-closing elements.
+	 * editorInstance.dataProcessor.writer.selfClosingEnd = '>';
+	 */
+	this.selfClosingEnd		= ' />';
+
+	/**
+	 * The characters to be used for line breaks.
+	 * @type String
+	 * @default "\n" (LF)
+	 * @example
+	 * // Use CRLF for line breaks.
+	 * editorInstance.dataProcessor.writer.lineBreakChars = '\r\n';
+	 */
+	this.lineBreakChars		= '\n';
+
+	this._ =
+	{
+		output : [],
+		indent : false,
+		indentation : '',
+		rules : {}
+	};
+
+	var dtd = CKEDITOR.dtd;
+
+	for ( var e in CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent ) )
+	{
+		this.setRules( e,
+			{
+				indent : true,
+				breakBeforeOpen : true,
+				breakAfterOpen : true,
+				breakBeforeClose : true,
+				breakAfterClose : true
+			});
+	}
+
+	this.setRules( 'br',
+		{
+			breakAfterOpen : true
+		});
+};
+
+CKEDITOR.htmlWriter.prototype =
+{
+	/**
+	 * Writes the tag opening part for a opener tag.
+	 * @param {String} tagName The element name for this tag.
+	 * @param {Object} attributes The attributes defined for this tag. The
+	 *		attributes could be used to inspect the tag.
+	 * @example
+	 * // Writes "&lt;p".
+	 * writer.openTag( 'p', { class : 'MyClass', id : 'MyId' } );
+	 */
+	openTag : function( tagName, attributes )
+	{
+		var rules = this._.rules[ tagName ];
+
+		if ( this._.indent )
+			this.indentation();
+		// Do not break if indenting.
+		else if ( rules && rules.breakBeforeOpen )
+		{
+			this.lineBreak();
+			this.indentation();
+		}
+
+		this._.output.push( '<', tagName );
+	},
+
+	/**
+	 * Writes the tag closing part for a opener tag.
+	 * @param {String} tagName The element name for this tag.
+	 * @param {Boolean} isSelfClose Indicates that this is a self-closing tag,
+	 *		like "br" or "img".
+	 * @example
+	 * // Writes "&gt;".
+	 * writer.openTagClose( 'p', false );
+	 * @example
+	 * // Writes " /&gt;".
+	 * writer.openTagClose( 'br', true );
+	 */
+	openTagClose : function( tagName, isSelfClose )
+	{
+		var rules = this._.rules[ tagName ];
+
+		if ( isSelfClose )
+			this._.output.push( this.selfClosingEnd );
+		else
+		{
+			this._.output.push( '>' );
+
+			if ( rules && rules.indent )
+				this._.indentation += this.indentationChars;
+		}
+
+		if ( rules && rules.breakAfterOpen )
+			this.lineBreak();
+	},
+
+	/**
+	 * Writes an attribute. This function should be called after opening the
+	 * tag with {@link #openTagClose}.
+	 * @param {String} attName The attribute name.
+	 * @param {String} attValue The attribute value.
+	 * @example
+	 * // Writes ' class="MyClass"'.
+	 * writer.attribute( 'class', 'MyClass' );
+	 */
+	attribute : function( attName, attValue )
+	{
+		this._.output.push( ' ', attName, '="', attValue, '"' );
+	},
+
+	/**
+	 * Writes a closer tag.
+	 * @param {String} tagName The element name for this tag.
+	 * @example
+	 * // Writes "&lt;/p&gt;".
+	 * writer.closeTag( 'p' );
+	 */
+	closeTag : function( tagName )
+	{
+		var rules = this._.rules[ tagName ];
+
+		if ( rules && rules.indent )
+			this._.indentation = this._.indentation.substr( this.indentationChars.length );
+
+		if ( this._.indent )
+			this.indentation();
+		// Do not break if indenting.
+		else if ( rules && rules.breakBeforeClose )
+		{
+			this.lineBreak();
+			this.indentation();
+		}
+
+		this._.output.push( '</', tagName, '>' );
+
+		if ( rules && rules.breakAfterClose )
+			this.lineBreak();
+	},
+
+	/**
+	 * Writes text.
+	 * @param {String} text The text value
+	 * @example
+	 * // Writes "Hello Word".
+	 * writer.text( 'Hello Word' );
+	 */
+	text : function( text )
+	{
+		if ( this._.indent )
+		{
+			this.indentation();
+			text = CKEDITOR.tools.ltrim( text );
+		}
+
+		this._.output.push( text );
+	},
+
+	/**
+	 * Writes a comment.
+	 * @param {String} comment The comment text.
+	 * @example
+	 * // Writes "&lt;!-- My comment --&gt;".
+	 * writer.comment( ' My comment ' );
+	 */
+	comment : function( comment )
+	{
+		if ( this._.indent )
+			this.indentation();
+
+		this._.output.push( '<!--', comment, '-->' );
+	},
+
+	/**
+	 * Writes a line break. It uses the {@link #lineBreakChars} property for it.
+	 * @example
+	 * // Writes "\n" (e.g.).
+	 * writer.lineBreak();
+	 */
+	lineBreak : function()
+	{
+		if ( this._.output.length > 0 )
+			this._.output.push( this.lineBreakChars );
+		this._.indent = true;
+	},
+
+	/**
+	 * Writes the current indentation chars. It uses the
+	 * {@link #indentationChars} property, repeating it for the current
+	 * intentation steps.
+	 * @example
+	 * // Writes "\t" (e.g.).
+	 * writer.indentation();
+	 */
+	indentation : function()
+	{
+		this._.output.push( this._.indentation );
+		this._.indent = false;
+	},
+
+	/**
+	 * Empties the current output buffer.
+	 * @example
+	 * writer.reset();
+	 */
+	reset : function()
+	{
+		this._.output = [];
+	},
+
+	/**
+	 * Empties the current output buffer.
+	 * @param {Boolean} reset Indicates that the {@link reset} function is to
+	 *		be automatically called after retrieving the HTML.
+	 * @returns {String} The HTML written to the writer so far.
+	 * @example
+	 * var html = writer.getHtml();
+	 */
+	getHtml : function( reset )
+	{
+		var html = this._.output.join( '' );
+
+		if ( reset )
+			this.reset();
+
+		return html;
+	},
+
+	/**
+	 * Sets formatting rules for a give element. The possible rules are:
+	 * <ul>
+	 *	<li><b>indent</b>: indent the element contents.</li>
+	 *	<li><b>breakBeforeOpen</b>: break line before the opener tag for this element.</li>
+	 *	<li><b>breakAfterOpen</b>: break line after the opener tag for this element.</li>
+	 *	<li><b>breakBeforeClose</b>: break line before the closer tag for this element.</li>
+	 *	<li><b>breakAfterClose</b>: break line after the closer tag for this element.</li>
+	 * </ul>
+	 *
+	 * All rules default to "false".
+	 *
+	 * By default, all elements available in the {@link CKEDITOR.dtd.$block),
+	 * {@link CKEDITOR.dtd.$listItem} and {@link CKEDITOR.dtd.$tableContent}
+	 * lists have all the above rules set to "true". Additionaly, the "br"
+	 * element has the "breakAfterOpen" set to "true".
+	 * @param {String} tagName The element name to which set the rules.
+	 * @param {Object} rules An object containing the element rules.
+	 * @example
+	 * // Break line before and after "img" tags.
+	 * writer.setRules( 'img',
+	 *     {
+	 *         breakBeforeOpen : true
+	 *         breakAfterOpen : true
+	 *     });
+	 * @example
+	 * // Reset the rules for the "h1" tag.
+	 * writer.setRules( 'h1', {} );
+	 */
+	setRules : function( tagName, rules )
+	{
+		this._.rules[ tagName ] = rules;
+	}
+};
+
+CKEDITOR.plugins.add( 'htmlwriter', {} );
Index: /CKEditor/branches/prototype/fckpackager.xml
===================================================================
--- /CKEditor/branches/prototype/fckpackager.xml	(revision 2333)
+++ /CKEditor/branches/prototype/fckpackager.xml	(revision 2334)
@@ -45,4 +45,7 @@
 
 	<Constants removeDeclaration="false">
+		<Constant name="CKEDITOR.NODE_ELEMENT" value="1" />
+		<Constant name="CKEDITOR.NODE_TEXT" value="3" />
+		<Constant name="CKEDITOR.NODE_COMMENT" value="8" />
 		<Constant name="CKEDITOR.UI_BUTTON" value="1" />
 		<Constant name="CKEDITOR.SELECTION_NONE" value="1" />
@@ -82,4 +85,10 @@
 		<File path="_source/core/ui.js" />
 		<File path="_source/core/editor.js" />
+		<File path="_source/core/htmlparser.js" />
+		<File path="_source/core/htmlparser/comment.js" />
+		<File path="_source/core/htmlparser/text.js" />
+		<File path="_source/core/htmlparser/fragment.js" />
+		<File path="_source/core/htmlparser/element.js" />
+		<File path="_source/dtd/xhtml1-transitional.js" />
 		<File path="_source/core/ckeditor.js" />
 		<File path="_source/core/dom/text.js" />
@@ -90,4 +99,5 @@
 		<File path="_source/plugins/elementspath/plugin.js" />
 		<File path="_source/plugins/htmldataprocessor/plugin.js" />
+		<File path="_source/plugins/htmlwriter/plugin.js" />
 		<File path="_source/plugins/selection/plugin.js" />
 		<File path="_source/plugins/sourcearea/plugin.js" />
