Index: /CKEditor/trunk/_source/core/editor.js
===================================================================
--- /CKEditor/trunk/_source/core/editor.js	(revision 3171)
+++ /CKEditor/trunk/_source/core/editor.js	(revision 3172)
@@ -189,5 +189,5 @@
 					{
 						// Initialize all plugins that have the "beforeInit" and "init" methods defined.
-						var methods = [ 'beforeInit', 'init' ];
+						var methods = [ 'beforeInit', 'init', 'afterInit' ];
 						for ( var m = 0 ; m < methods.length ; m++ )
 						{
Index: /CKEditor/trunk/_source/core/htmlparser/basicwriter.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/basicwriter.js	(revision 3172)
+++ /CKEditor/trunk/_source/core/htmlparser/basicwriter.js	(revision 3172)
@@ -0,0 +1,140 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.htmlParser.basicWriter = CKEDITOR.tools.createClass(
+{
+	$ : function()
+	{
+		this._ =
+		{
+			output : []
+		}
+	},
+
+	proto :
+	{
+		/**
+		 * 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 )
+		{
+			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 )
+		{
+			if ( isSelfClose )
+				this._.output.push( ' />' );
+			else
+				this._.output.push( '>' );
+		},
+
+		/**
+		 * 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 )
+		{
+			this._.output.push( '</', tagName, '>' );
+		},
+
+		/**
+		 * Writes text.
+		 * @param {String} text The text value
+		 * @example
+		 * // Writes "Hello Word".
+		 * writer.text( 'Hello Word' );
+		 */
+		text : function( 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 )
+		{
+			this._.output.push( '<!--', comment, '-->' );
+		},
+
+		/**
+		 * Writes any kind of data to the ouput.
+		 * @example
+		 * writer.write( 'This is an &lt;b&gt;example&lt;/b&gt;.' );
+		 */
+		write : function( data )
+		{
+			this._.output.push( data );
+		},
+
+		/**
+		 * 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;
+		}
+	}
+});
Index: /CKEditor/trunk/_source/core/htmlparser/comment.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/comment.js	(revision 3171)
+++ /CKEditor/trunk/_source/core/htmlparser/comment.js	(revision 3172)
@@ -39,7 +39,12 @@
 	 * @example
 	 */
-	writeHtml : function( writer )
+	writeHtml : function( writer, filter )
 	{
-		writer.comment( this.value );
+		var comment = this.value;
+
+		if ( filter && !( comment = filter.onComment( comment ) ) )
+			return;
+
+		writer.comment( comment );
 	}
 };
Index: /CKEditor/trunk/_source/core/htmlparser/element.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/element.js	(revision 3171)
+++ /CKEditor/trunk/_source/core/htmlparser/element.js	(revision 3172)
@@ -14,15 +14,4 @@
 CKEDITOR.htmlParser.element = function( name, attributes )
 {
-	if ( attributes._cke_saved_src )
-		attributes.src = attributes._cke_saved_src;
-
-	if ( attributes._cke_saved_href )
-		attributes.href = attributes._cke_saved_href;
-
-	// IE outputs style attribute in capital letters. We should convert them
-	// back to lower case.
-	if ( CKEDITOR.env.ie && attributes.style )
-		attributes.style = attributes.style.toLowerCase();
-		
 	/**
 	 * The element name.
@@ -72,11 +61,4 @@
 	};
 
-	var ckeAttrRegex = /^_cke/,
-		ckeNamespaceRegex = /^cke:/,
-		ckeStyleWidthRegex = /width\s*:\s*(\d+)/i,
-		ckeStyleHeightRegex = /height\s*:\s*(\d+)/i,
-		ckeClassRegex = /(?:^|\s+)cke_[^\s]*/g,
-		ckePrivateAttrRegex = /^_cke_pa_/;
-
 	CKEDITOR.htmlParser.element.prototype =
 	{
@@ -114,50 +96,7 @@
 		 * @example
 		 */
-		writeHtml : function( writer )
+		writeHtml : function( writer, filter )
 		{
 			var attributes = this.attributes;
-
-			// The "_cke_realelement" attribute indicates that the current
-			// element is a placeholder for another element.
-			if ( attributes._cke_realelement )
-			{
-				var realFragment = new CKEDITOR.htmlParser.fragment.fromHtml( decodeURIComponent( attributes._cke_realelement ) );
-
-				// If _cke_resizable is set, and the fake element contains inline CSS width
-				// and height; then sync the width and height to the real element.
-				if ( attributes._cke_resizable && ( 'style' in attributes ) )
-				{
-					var match = ckeStyleWidthRegex.exec( attributes.style ),
-						width = match ? match[1] : null;
-					match = ckeStyleHeightRegex.exec( attributes.style );
-					var height = match ? match[1] : null;
-					
-					var targetElement = realFragment.children[ 0 ];
-					if ( targetElement && ( width != null || height != null ) )
-					{
-						targetElement.attributes.width = width;
-						targetElement.attributes.height = height;
-
-						// Special case for #2916: If there's an EMBED inside an OBJECT, we need
-						// to set the EMBED's dimensions as well.
-						if ( targetElement.name == 'cke:object' )
-						{
-							for ( var i = 0 ; i < targetElement.children.length ; i++ )
-							{
-								var child = targetElement.children[i];
-								if ( child.name == 'cke:embed' )
-								{
-									child.attributes.width = width;
-									child.attributes.height = height;
-									break;
-								}
-							}
-						}
-					}
-				}
-
-				realFragment.writeHtml( writer );
-				return;
-			}
 
 			// The "_cke_replacedata" indicates that this element is replacing
@@ -170,50 +109,73 @@
 
 			// Ignore cke: prefixes when writing HTML.
-			var writeName = this.name.replace( ckeNamespaceRegex, '' );
+			var element = this,
+				writeName = element.name,
+				a, value;
+
+			if ( filter )
+			{
+				while ( true )
+				{
+					if ( !( writeName = filter.onElementName( writeName ) ) )
+						return;
+
+					element.name = writeName;
+
+					if ( !( element = filter.onElement( element ) ) )
+						return;
+
+					if ( element.name == writeName )
+						break;
+
+					writeName = element.name;
+				}
+
+				// The element may have been changed, so update the local
+				// references.
+				attributes = element.attributes;
+			}
 
 			// Open element tag.
-			writer.openTag( writeName, this.attributes );
+			writer.openTag( writeName, attributes );
 
-			// Copy all attributes to an array.
-			var attribsArray = [];
-			for ( var a in attributes )
+			if ( writer.sortAttributes )
 			{
-				var value = attributes[ a ];
+				// Copy all attributes to an array.
+				var attribsArray = [];
+				for ( a in attributes )
+				{
+					value = attributes[ a ];
 
-				// If the attribute name is _cke_pa_*, strip away the _cke_pa part.
-				a = a.replace( ckePrivateAttrRegex, '' );
+					if ( filter && ( !( a = filter.onAttributeName( a ) ) || ( value = filter.onAttribute( element, a, value ) ) === false ) )
+						continue;
 
-				// Ignore all attributes starting with "_cke".
-				if ( ckeAttrRegex.test( a ) )
-					continue;
-
-				// Ignore all cke_* CSS classes.
-				if ( a.toLowerCase() == 'class' )
-				{
-					value = CKEDITOR.tools.ltrim( value.replace( ckeClassRegex, '' ) );
-					if ( value == '' )
-						continue;
+					attribsArray.push( [ a, value ] );
 				}
 
-				attribsArray.push( [ a, value ] );
+				// 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] );
+				}
 			}
-
-			// Sort the attributes by name.
-			attribsArray.sort( sortAttribs );
-
-			// Send the attributes.
-			for ( var i = 0, len = attribsArray.length ; i < len ; i++ )
+			else
 			{
-				var attrib = attribsArray[ i ];
-				writer.attribute( attrib[0], attrib[1] );
+				for ( a in attributes )
+				{
+					writer.attribute( a, attributes[ a ] );
+				}
 			}
 
 			// Close the tag.
-			writer.openTagClose( writeName, this.isEmpty );
+			writer.openTagClose( writeName, element.isEmpty );
 
-			if ( !this.isEmpty )
+			if ( !element.isEmpty )
 			{
 				// Send children.
-				CKEDITOR.htmlParser.fragment.prototype.writeHtml.apply( this, arguments );
+				CKEDITOR.htmlParser.fragment.prototype.writeHtml.apply( element, arguments );
 
 				// Close the element.
Index: /CKEditor/trunk/_source/core/htmlparser/filter.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/filter.js	(revision 3172)
+++ /CKEditor/trunk/_source/core/htmlparser/filter.js	(revision 3172)
@@ -0,0 +1,231 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+	CKEDITOR.htmlParser.filter = CKEDITOR.tools.createClass(
+	{
+		$ : function( rules )
+		{
+			this._ =
+			{
+				elementNames : [],
+				attributeNames : [],
+				elements : { $length : 0 },
+				attributes : { $length : 0 }
+			};
+
+			if ( rules )
+				this.addRules( rules, 10 );
+		},
+
+		proto :
+		{
+			addRules : function( rules, priority )
+			{
+				if ( typeof priority != 'number' )
+					priority = 10;
+
+				// Add the elementNames.
+				addItemsToList( this._.elementNames, rules.elementNames, priority );
+
+				// Add the attributeNames.
+				addItemsToList( this._.attributeNames, rules.attributeNames, priority );
+
+				// Add the elements.
+				addNamedItems( this._.elements, rules.elements, priority );
+
+				// Add the attributes.
+				addNamedItems( this._.attributes, rules.attributes, priority );
+
+				// Add the text.
+				this._.text = transformNamedItem( this._.text, rules.text, priority ) || this._.text;
+
+				// Add the comment.
+				this._.comment = transformNamedItem( this._.comment, rules.comment, priority ) || this._.comment;
+			},
+
+			onElementName : function( name )
+			{
+				return filterName( name, this._.elementNames );
+			},
+
+			onAttributeName : function( name )
+			{
+				return filterName( name, this._.attributeNames );
+			},
+
+			onText : function( text )
+			{
+				var textFilter = this._.text;
+				return textFilter ? textFilter.filter( text ) : text;
+			},
+
+			onComment : function( commentText )
+			{
+				var textFilter = this._.comment;
+				return textFilter ? textFilter.filter( commentText ) : commentText;
+			},
+
+			onElement : function( element )
+			{
+				// We must apply filters set to the specific element name as
+				// well as those set to the generic $ name. So, add both to an
+				// array and process them in a small loop.
+				var filters = [ this._.elements[ element.name ], this._.elements.$ ],
+					filter, ret;
+
+				for ( var i = 0 ; i < 2 ; i++ )
+				{
+					filter = filters[ i ];
+					if ( filter )
+					{
+						ret = filter.filter( element, this );
+
+						if ( ret === false )
+							return null;
+
+						if ( ret && ret != element )
+							return this.onElement( ret );;
+					}
+				}
+
+				return element;
+			},
+
+			onAttribute : function( element, name, value )
+			{
+				var filter = this._.attributes[ name ];
+
+				if ( filter )
+				{
+					var ret = filter.filter( value, element, this );
+
+					if ( ret === false )
+						return false;
+
+					if ( typeof ret != 'undefined' )
+						return ret;
+				}
+
+				return value;
+			}
+		}
+	});
+
+	function filterName( name, filters )
+	{
+		for ( var i = 0 ; name && i < filters.length ; i++ )
+		{
+			var filter = filters[ i ];
+			name = name.replace( filter[ 0 ], filter[ 1 ] );
+		}
+		return name;
+	}
+
+	function addItemsToList( list, items, priority )
+	{
+		var i, j,
+			listLength = list.length,
+			itemsLength = items && items.length;
+
+		if ( itemsLength )
+		{
+			// Find the index to insert the items at.
+			for ( i = 0 ; i < listLength && list[ i ].pri < priority ; i++ )
+			{}
+
+			// Add all new items to the list at the specific index.
+			for ( j = itemsLength - 1 ; j >= 0 ; j-- )
+			{
+				var item = items[ j ];
+				item.pri = priority;
+				list.splice( i, 0, item );
+			}
+		}
+	}
+
+	function addNamedItems( hashTable, items, priority )
+	{
+		if ( items )
+		{
+			for ( var name in items )
+			{
+				var current = hashTable[ name ];
+
+				hashTable[ name ] =
+					transformNamedItem(
+						current,
+						items[ name ],
+						priority );
+
+				if ( !current )
+					hashTable.$length++;
+			}
+		}
+	}
+
+	function transformNamedItem( current, item, priority )
+	{
+		if ( item )
+		{
+			item.pri = priority;
+
+			if ( current )
+			{
+				// If the current item is not an Array, transform it.
+				if ( !current.splice )
+				{
+					if ( current.pri > priority )
+						current = [ item, current ];
+					else
+						current = [ current, item ];
+
+					current.filter = callItems;
+				}
+				else
+					addItemsToList( current, item, priority );
+
+				return current;
+			}
+			else
+			{
+				item.filter = item;
+				return item;
+			}
+		}
+	}
+
+	function callItems( currentEntry )
+	{
+		var isObject = ( typeof currentEntry == 'object' );
+
+		for ( var i = 0 ; i < this.length ; i++ )
+		{
+			var item = this[ i ],
+				ret = item.apply( window, arguments );
+
+			if ( typeof ret != 'undefined' )
+			{
+				if ( ret === false )
+					return false;
+
+				if ( isObject && ret != currentEntry )
+					return ret;
+			}
+		}
+	}
+})();
+
+// "entities" plugin
+/*
+{
+	text : function( text )
+	{
+		// TODO : Process entities.
+		return text.toUpperCase();
+	}
+};
+*/
Index: /CKEditor/trunk/_source/core/htmlparser/fragment.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/fragment.js	(revision 3171)
+++ /CKEditor/trunk/_source/core/htmlparser/fragment.js	(revision 3172)
@@ -91,8 +91,4 @@
 		parser.onTagOpen = function( tagName, attributes, selfClosing )
 		{
-			// If the tag name is ?xml:namespace, ignore.
-			if ( tagName == '?xml:namespace' )
-				return;
-
 			var element = new CKEDITOR.htmlParser.element( tagName, attributes );
 
@@ -279,8 +275,8 @@
 		 * alert( writer.getHtml() );  "&lt;p&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/p&gt;"
 		 */
-		writeHtml : function( writer )
+		writeHtml : function( writer, filter )
 		{
 			for ( var i = 0, len = this.children.length ; i < len ; i++ )
-				this.children[i].writeHtml( writer );
+				this.children[i].writeHtml( writer, filter );
 		}
 	};
Index: /CKEditor/trunk/_source/core/htmlparser/text.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/text.js	(revision 3171)
+++ /CKEditor/trunk/_source/core/htmlparser/text.js	(revision 3172)
@@ -43,7 +43,12 @@
 		 * @example
 		 */
-		writeHtml : function( writer )
+		writeHtml : function( writer, filter )
 		{
-			writer.text( this.value );
+			var text = this.value;
+
+			if ( filter && !( text = filter.onText( text ) ) )
+				return;
+
+			writer.text( text );
 		}
 	};
Index: /CKEditor/trunk/_source/core/loader.js
===================================================================
--- /CKEditor/trunk/_source/core/loader.js	(revision 3171)
+++ /CKEditor/trunk/_source/core/loader.js	(revision 3172)
@@ -26,5 +26,5 @@
 			'core/_bootstrap'		: [ 'core/config', 'core/ckeditor', 'core/plugins', 'core/scriptloader', 'core/tools', /* The following are entries that we want to force loading at the end to avoid dependence recursion */ 'core/dom/elementpath', 'core/dom/text', 'core/dom/range' ],
 			'core/ajax'				: [ 'core/xml' ],
-			'core/ckeditor'			: [ 'core/ckeditor_basic', 'core/dom', 'core/dtd', 'core/dom/document', 'core/dom/element', 'core/editor', 'core/event', 'core/htmlparser', 'core/htmlparser/element', 'core/htmlparser/fragment', 'core/tools' ],
+			'core/ckeditor'			: [ 'core/ckeditor_basic', 'core/dom', 'core/dtd', 'core/dom/document', 'core/dom/element', 'core/editor', 'core/event', 'core/htmlparser', 'core/htmlparser/element', 'core/htmlparser/fragment', 'core/htmlparser/filter', 'core/htmlparser/basicwriter', 'core/tools' ],
 			'core/ckeditor_base'	: [],
 			'core/ckeditor_basic'	: [ 'core/editor_basic', 'core/env', 'core/event' ],
@@ -55,4 +55,6 @@
 			'core/htmlparser/fragment'	: [ 'core/htmlparser', 'core/htmlparser/comment', 'core/htmlparser/text' ],
 			'core/htmlparser/text'		: [ 'core/htmlparser' ],
+			'core/htmlparser/filter'	: [ 'core/htmlparser' ],
+			'core/htmlparser/basicwriter': [ 'core/htmlparser' ],
 			'core/imagecacher'		: [ 'core/dom/element' ],
 			'core/lang'				: [],
Index: /CKEditor/trunk/_source/plugins/fakeobjects/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/fakeobjects/plugin.js	(revision 3171)
+++ /CKEditor/trunk/_source/plugins/fakeobjects/plugin.js	(revision 3172)
@@ -4,5 +4,60 @@
 */
 
-CKEDITOR.plugins.add( 'fakeobjects' );
+(function()
+{
+	var htmlFilterRules =
+	{
+		elements :
+		{
+			$ : function( element, filter )
+			{
+				var realHtml = element.attributes._cke_realelement;
+					realFragment = realHtml && new CKEDITOR.htmlParser.fragment.fromHtml( decodeURIComponent( realHtml ), filter ),
+					realElement = realFragment && realFragment.children[ 0 ];
+
+				if ( realElement )
+				{
+					// If we have width/height in the element, we must move it into
+					// the real element.
+
+					var style = element.attributes.style;
+					
+					if ( style )
+					{
+						// Get the width from the style.
+						var match = /(?:$|\s)width\s*:\s*(\d+)/.exec( style ),
+							width = match && match[1];
+
+						// Get the height from the style.
+						match = /(?:$|\s)height\s*:\s*(\d+)/.exec( style );
+						var height = match && match[1];
+
+						if ( width )
+							realElement.attributes.width = width;
+						
+						if ( height )
+							realElement.attributes.height = height;
+					}
+				}
+				
+				return realElement;
+			}
+		}
+	};
+
+	CKEDITOR.plugins.add( 'fakeobjects', 
+	{
+		requires : [ 'htmlwriter' ],
+
+		afterInit : function( editor )
+		{
+			var dataProcessor = editor.dataProcessor,
+				htmlFilter = dataProcessor && dataProcessor.htmlFilter;
+
+			if ( htmlFilter )
+				htmlFilter.addRules( htmlFilterRules );
+		}
+	});
+})();
 
 CKEDITOR.editor.prototype.createFakeElement = function( realElement, className, realElementType, isResizable )
@@ -22,4 +77,28 @@
 };
 
+CKEDITOR.editor.prototype.createFakeParserElement = function( realElement, className, realElementType, isResizable )
+{
+	var writer = new CKEDITOR.htmlParser.basicWriter();
+	
+	realElement.writeHtml( writer );
+	
+	var html = writer.getHtml();
+	
+	var attributes = 
+	{
+		'class' : className,
+		src : CKEDITOR.getUrl( 'images/spacer.gif' ),
+		_cke_realelement : encodeURIComponent( html )
+	};
+
+	if ( realElementType )
+		attributes._cke_real_element_type = realElementType;
+
+	if ( isResizable )
+		attributes._cke_resizable = isResizable;
+
+	return new CKEDITOR.htmlParser.element( 'img', attributes );
+};
+
 CKEDITOR.editor.prototype.restoreRealElement = function( fakeElement )
 {
Index: /CKEditor/trunk/_source/plugins/flash/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/flash/plugin.js	(revision 3171)
+++ /CKEditor/trunk/_source/plugins/flash/plugin.js	(revision 3172)
@@ -4,99 +4,140 @@
 */
 
-CKEDITOR.plugins.add( 'flash',
+(function()
 {
-	init : function( editor )
+	var flashFilenameRegex = /\.swf(?:$|\?)/i,
+		numberRegex = /^\d+(?:\.\d+)?$/;
+
+	function cssifyLength( length )
 	{
-		var flash = CKEDITOR.plugins.flash,
-			flashFilenameRegex = /\.swf(?:$|\?)/i,
-			numberRegex = /^\d+(?:\.\d+)?$/;
+		if ( numberRegex.test( length ) )
+			return length + 'px';
+		return length;
+	}
 	
-		function cssifyLength( length )
+	function isFlashEmbed( element )
+	{
+		var attributes = element.attributes;
+
+		return ( attributes.type != 'application/x-shockwave-flash' || !flashFilenameRegex.test( attributes.src || '' ) );
+	}
+
+	function createFakeElement( editor, realElement )
+	{
+		var fakeElement = editor.createFakeParserElement( realElement, 'cke_flash', 'flash', true ),
+			fakeStyle = fakeElement.attributes.style || '';
+
+		var width = realElement.attributes.width,
+			height = realElement.attributes.height;
+
+		if ( typeof width != 'undefined' )
+			fakeStyle = fakeElement.attributes.style = fakeStyle + 'width:' + cssifyLength( width ) + ';';
+
+		if ( typeof height != 'undefined' )
+			fakeStyle = fakeElement.attributes.style = fakeStyle + 'height:' + cssifyLength( height ) + ';';
+
+		return fakeElement;
+	}
+
+	CKEDITOR.plugins.add( 'flash',
+	{
+		init : function( editor )
 		{
-			if ( numberRegex.test( length ) )
-				return length + 'px';
-			return length;
-		}
+			editor.addCommand( 'flash', new CKEDITOR.dialogCommand( 'flash' ) );
+			editor.ui.addButton( 'Flash',
+				{
+					label : editor.lang.common.flash,
+					command : 'flash'
+				});
+			CKEDITOR.dialog.add( 'flash', this.path + 'dialogs/flash.js' );
 
-		editor.addCommand( 'flash', new CKEDITOR.dialogCommand( 'flash' ) );
-		editor.ui.addButton( 'Flash',
+			editor.addCss(
+				'img.cke_flash' +
+				'{' +
+					'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/flashlogo.gif' ) + ');' +
+					'background-position: center center;' +
+					'background-repeat: no-repeat;' +
+					'border: 1px solid #a9a9a9;' +
+					'width: 80px;' +
+					'height: 80px;' +
+				'}'
+				);
+
+			// If the "menu" plugin is loaded, register the menu items.
+			if ( editor.addMenuItems )
 			{
-				label : editor.lang.common.flash,
-				command : 'flash'
-			});
-		CKEDITOR.dialog.add( 'flash', this.path + 'dialogs/flash.js' );
+				editor.addMenuItems(
+					{
+						flash :
+						{
+							label : editor.lang.flash.properties,
+							command : 'flash',
+							group : 'flash'
+						}
+					});
+			}
 
-		editor.addCss(
-			'img.cke_flash' +
-			'{' +
-				'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/flashlogo.gif' ) + ');' +
-				'background-position: center center;' +
-				'background-repeat: no-repeat;' +
-				'border: 1px solid #a9a9a9;' +
-				'width: 80px;' +
-				'height: 80px;' +
-			'}' 
-			);
+			// If the "contextmenu" plugin is loaded, register the listeners.
+			if ( editor.contextMenu )
+			{
+				editor.contextMenu.addListener( function( element, selection )
+					{
+						if ( element && element.is( 'img' ) && element.getAttribute( '_cke_real_element_type' ) == 'flash' )
+							return { flash : CKEDITOR.TRISTATE_OFF };
+					});
+			}
+		},
 
-		editor.on( 'contentDom', function()
+		afterInit : function( editor )
+		{
+			var dataProcessor = editor.dataProcessor,
+				dataFilter = dataProcessor && dataProcessor.dataFilter;
+
+			if ( dataFilter )
 			{
-				var rawObjectNodes = editor.document.$.getElementsByTagName( CKEDITOR.env.ie ? 'object' : 'cke:object' );
-				for ( var i = rawObjectNodes.length - 1, objectNode ; i >= 0 ; i-- )
-				{
-					objectNode = new CKEDITOR.dom.element( rawObjectNodes[ i ] );
-					if ( String( objectNode.getAttribute( 'classid' ) ).toLowerCase() != 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' )
-						continue;
+				dataFilter.addRules(
+					{
+						elements :
+						{
+							'cke:object' : function( element )
+							{
+								var attributes = element.attributes,
+									classId = attributes.classid && String( attributes.classid ).toLowerCase();
 
-					var fakeElement = editor.createFakeElement( objectNode, 'cke_flash', 'flash', true );
-					if ( objectNode.getAttribute( 'width' ) != null )
-						fakeElement.setStyle( 'width', cssifyLength( objectNode.getAttribute( 'width' ) ) );
-					if ( objectNode.getAttribute( 'height' ) != null )
-						fakeElement.setStyle( 'height', cssifyLength( objectNode.getAttribute( 'height' ) ) );
-					fakeElement.replace( objectNode );
-				}
+								if ( !classId )
+								{
+									// Look for the inner <embed>
+									for ( var i = 0 ; i < element.children.length ; i++ )
+									{
+										if ( element.children[ i ].name == 'embed' )
+										{
+											if ( !isFlashEmbed( element.children[ i ] ) )
+												return;
+											
+											return createFakeElement( editor, element );								
+										}
+									}
+									return;
+								}
 
-				var rawEmbedNodes = editor.document.$.getElementsByTagName( CKEDITOR.env.ie ? 'embed' : 'cke:embed' );
-				for ( var i = rawEmbedNodes.length - 1, embedNode ; i >= 0 ; i-- )
-				{
-					embedNode = new CKEDITOR.dom.element( rawEmbedNodes[ i ] );
-					if ( embedNode.getAttribute( 'type' ) != 'application/x-shockwave-flash'
-						&& !flashFilenameRegex.test( embedNode.getAttribute( 'src' ) ) )
-						continue;
-					var fakeElement = editor.createFakeElement( embedNode, 'cke_flash', 'flash', true );
-					if ( embedNode.getAttribute( 'width' ) != null )
-						fakeElement.setStyle( 'width', cssifyLength( embedNode.getAttribute( 'width' ) ) );
-					if ( embedNode.getAttribute( 'height' ) != null )
-						fakeElement.setStyle( 'height', cssifyLength( embedNode.getAttribute( 'height' ) ) );
-					fakeElement.replace( embedNode );
-				}
-			} );
+								return createFakeElement( editor, element );
+							},
 
-		// If the "menu" plugin is loaded, register the menu items.
-		if ( editor.addMenuItems )
-		{
-			editor.addMenuItems(
-				{
-					flash :
-					{
-						label : editor.lang.flash.properties,
-						command : 'flash',
-						group : 'flash'
-					}
-				});
-		}
+							'cke:embed' : function( element )
+							{
+								if ( !isFlashEmbed( element ) )
+									return;
 
-		// If the "contextmenu" plugin is loaded, register the listeners.
-		if ( editor.contextMenu )
-		{
-			editor.contextMenu.addListener( function( element, selection )
-				{
-					if ( element && element.is( 'img' ) && element.getAttribute( '_cke_real_element_type' ) == 'flash' )
-						return { flash : CKEDITOR.TRISTATE_OFF };
-				});
-		}
-	},
+								return createFakeElement( editor, element );
+							}
+						}
+					},
+					5);
+			}
+		},
 
-	requires : [ 'fakeobjects' ]
-} );
+		requires : [ 'fakeobjects' ]
+	});
+})();
 
 CKEDITOR.tools.extend( CKEDITOR.config,
Index: /CKEditor/trunk/_source/plugins/htmldataprocessor/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/htmldataprocessor/plugin.js	(revision 3171)
+++ /CKEditor/trunk/_source/plugins/htmldataprocessor/plugin.js	(revision 3172)
@@ -4,41 +4,163 @@
 */
 
-CKEDITOR.plugins.add( 'htmldataprocessor',
+(function()
 {
-	requires : [ 'htmlwriter' ],
+	var defaultDataFilterRules =
+	{
+		elementNames :
+		[
+			// Elements that cause problems in wysiwyg mode.
+			[ /^(object|embed|param)$/, 'cke:$1' ]
+		],
 
-	init : function( editor, pluginPath )
+		attributeNames :
+		[
+			// Event attributes (onXYZ) must not be directly set. They can become
+			// active in the editing area (IE|WebKit).
+			[ /^on/, '_cke_pa_on' ]
+		]
+	};
+
+	var defaultHtmlFilterRules =
+		{
+			elementNames :
+			[
+				// Remove the "cke:" namespace prefix.
+				[ /^cke:/, '' ],
+
+				// Ignore <?xml:namespace> tags.
+				[ /^\?xml:namespace$/, '' ]
+			],
+
+			attributeNames :
+			[
+				// Attributes saved for changes and protected attributes.
+				[ /^_cke_(saved|pa)_/, '' ],
+
+				// All "_cke" attributes are to be ignored.
+				[ /^_cke.*/, '' ]
+			],
+
+			elements :
+			{
+				embed : function( element )
+				{
+					var parent = element.parent;
+
+					// If the <embed> is child of a <object>, copy the width
+					// and height attributes from it.
+					if ( parent && parent.name == 'object' )
+					{
+						element.attributes.width = parent.attributes.width;
+						element.attributes.height = parent.attributes.height;
+					}
+				},
+
+				img : function( element )
+				{
+					var attribs = element.attributes;
+
+					if ( attribs._cke_saved_src )
+						delete attribs.src;
+				},
+
+				a : function( element )
+				{
+					var attribs = element.attributes;
+
+					if ( attribs._cke_saved_href )
+						delete attribs.href;
+				}
+			},
+
+			attributes :
+			{
+				'class' : function( value, element )
+				{
+					// Remove all class names starting with "cke_".
+					return CKEDITOR.tools.ltrim( value.replace( /(?:^|\s+)cke_[^\s]*/g, '' ) ) || false;
+				}
+			}
+		};
+
+	if ( CKEDITOR.env.ie )
 	{
-		var dataProcessor = editor.dataProcessor = new CKEDITOR.htmlDataProcessor();
-		
-		dataProcessor.writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand;
+		// IE outputs style attribute in capital letters. We should convert
+		// them back to lower case.
+		defaultHtmlFilterRules.attributes.style = function( value, element )
+		{
+			return value.toLowerCase();
+		}
 	}
-});
 
-CKEDITOR.htmlDataProcessor = function()
-{
-	this.writer = new CKEDITOR.htmlWriter();
-};
+	var protectUrlTagRegex = /<(?:a|area|img).*?\s((?:href|src)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+)))/gi;
 
-CKEDITOR.htmlDataProcessor.prototype =
-{
-	toHtml : function( data )
+	function protectUrls( html )
 	{
-		// The source data is already HTML, so just return it as is.
-		return data;
-	},
+		return html.replace( protectUrlTagRegex, '$& _cke_saved_$1' );
+	};
 
-	toDataFormat : function( element )
+	CKEDITOR.plugins.add( 'htmldataprocessor',
 	{
-		var writer = this.writer,
-			fragment = CKEDITOR.htmlParser.fragment.fromHtml( element.getHtml() );
+		requires : [ 'htmlwriter' ],
 
-		writer.reset();
+		init : function( editor, pluginPath )
+		{
+			var dataProcessor = editor.dataProcessor = new CKEDITOR.htmlDataProcessor();
 
-		fragment.writeHtml( writer );
+			dataProcessor.writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand;
 
-		return writer.getHtml( true );
-	}
-};
+			dataProcessor.dataFilter.addRules( defaultDataFilterRules );
+			dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules );
+		}
+	});
+
+	CKEDITOR.htmlDataProcessor = function()
+	{
+		this.writer = new CKEDITOR.htmlWriter();
+		this.dataFilter = new CKEDITOR.htmlParser.filter();
+		this.htmlFilter = new CKEDITOR.htmlParser.filter();
+	};
+
+	CKEDITOR.htmlDataProcessor.prototype =
+	{
+		toHtml : function( data )
+		{
+			// The source data is already HTML, but we need to clean
+			// it up and apply the filter.
+
+			// Before anything, we must protect the URL attributes as the
+			// browser may changing them when setting the innerHTML later in
+			// the code.
+			data = protectUrls( data );
+
+			// Call the browser to help us fixing a possibly invalid HTML
+			// structure.
+			var div = document.createElement( 'div' );
+			div.innerHTML = data;
+
+			// Now use our parser to make further fixes to the structure, as
+			// well as apply the filter.
+			var fragment = CKEDITOR.htmlParser.fragment.fromHtml( div.innerHTML ),
+				writer = new CKEDITOR.htmlParser.basicWriter();
+
+			fragment.writeHtml( writer, this.dataFilter );
+
+			return writer.getHtml( true );
+		},
+
+		toDataFormat : function( html )
+		{
+			var writer = this.writer,
+				fragment = CKEDITOR.htmlParser.fragment.fromHtml( html );
+
+			writer.reset();
+
+			fragment.writeHtml( writer, this.htmlFilter );
+
+			return writer.getHtml( true );
+		}
+	};
+})();
 
 CKEDITOR.config.forceSimpleAmpersand = false;
Index: /CKEditor/trunk/_source/plugins/htmlwriter/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/htmlwriter/plugin.js	(revision 3171)
+++ /CKEditor/trunk/_source/plugins/htmlwriter/plugin.js	(revision 3172)
@@ -3,4 +3,6 @@
 For licensing, see LICENSE.html or http://ckeditor.com/license
 */
+
+CKEDITOR.plugins.add( 'htmlwriter' );
 
 /**
@@ -16,302 +18,268 @@
  * alert( writer.getHtml() );  "&lt;p class="MyClass"&gt;Hello&lt;/p&gt;"
  */
-CKEDITOR.htmlWriter = function()
+CKEDITOR.htmlWriter = CKEDITOR.tools.createClass(
 {
-	/**
-	 * 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.forceSimpleAmpersand = false;
-
-	this._ =
+	base : CKEDITOR.htmlParser.basicWriter,
+
+	$ : function()
 	{
-		output : [],
-		indent : false,
-		indentation : '',
-		rules : {}
-	};
-
-	var dtd = CKEDITOR.dtd;
-
-	for ( var e in CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent ) )
+		// Call the base contructor.
+		this.base();
+
+		/**
+		 * 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.forceSimpleAmpersand = false;
+
+		this.sortAttributes = true;
+
+		this._.indent = false;
+		this._.indentation = '';
+		this._.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 : !dtd[ e ][ '#' ],
+					breakAfterClose : true
+				});
+		}
+
+		this.setRules( 'br',
+			{
+				breakAfterOpen : true
+			});
+	},
+
+	proto :
 	{
-		this.setRules( e,
-			{
-				indent : true,
-				breakBeforeOpen : true,
-				breakAfterOpen : true,
-				breakBeforeClose : !dtd[ e ][ '#' ],
-				breakAfterClose : true
-			});
+		/**
+		 * 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 )
+		{
+			if ( this.forceSimpleAmpersand )
+				attValue = attValue.replace( /&amp;/, '&' );
+
+			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
+		 * indentation steps.
+		 * @example
+		 * // Writes "\t" (e.g.).
+		 * writer.indentation();
+		 */
+		indentation : function()
+		{
+			this._.output.push( this._.indentation );
+			this._.indent = false;
+		},
+
+		/**
+		 * 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;
+		}
 	}
-
-	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 )
-	{
-		if ( this.forceSimpleAmpersand )
-			attValue = attValue.replace( /&amp;/, '&' );
-
-		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
-	 * indentation steps.
-	 * @example
-	 * // Writes "\t" (e.g.).
-	 * writer.indentation();
-	 */
-	indentation : function()
-	{
-		this._.output.push( this._.indentation );
-		this._.indent = false;
-	},
-
-	/**
-	 * Writes any kind of data to the ouput.
-	 * @example
-	 * writer.write( 'This is an &lt;b&gt;example&lt;/b&gt;.' );
-	 */
-	write : function( data )
-	{
-		this._.output.push( data );
-	},
-
-	/**
-	 * 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/trunk/_source/plugins/link/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/link/plugin.js	(revision 3171)
+++ /CKEditor/trunk/_source/plugins/link/plugin.js	(revision 3172)
@@ -66,21 +66,4 @@
 			} );
 
-		// Register a contentDom handler for displaying placeholders after mode change.
-		editor.on( 'contentDom', function()
-			{
-				var rawAnchors = editor.document.$.anchors;
-				for ( var i = rawAnchors.length - 1, anchor ; i >= 0 ; i-- )
-				{
-					anchor = new CKEDITOR.dom.element( rawAnchors[ i ] );
-
-					// IE BUG: When an <a> tag doesn't have href, IE would return empty string
-					// instead of null on getAttribute.
-					if ( !anchor.getAttribute( 'href' ) )
-						editor.createFakeElement( anchor, 'cke_anchor', 'anchor' ).replace( anchor );
-					else
-						anchor.addClass( 'cke_anchor' );
-				}
-			});
-
 		// If the "menu" plugin is loaded, register the menu items.
 		if ( editor.addMenuItems )
@@ -138,4 +121,28 @@
 	},
 
+	afterInit : function( editor )
+	{
+		// Register a filter to displaying placeholders after mode change.
+
+		var dataProcessor = editor.dataProcessor,
+			dataFilter = dataProcessor && dataProcessor.dataFilter;
+
+		if ( dataFilter )
+		{
+			dataFilter.addRules(
+				{
+					elements :
+					{
+						a : function( element )
+						{
+							var attributes = element.attributes;
+							if ( attributes.name && !attributes.href )
+								return editor.createFakeParserElement( element, 'cke_anchor', 'anchor' );
+						}
+					}
+				});
+		}
+	},
+
 	requires : [ 'fakeobjects' ]
 } );
Index: /CKEditor/trunk/_source/plugins/pagebreak/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/pagebreak/plugin.js	(revision 3171)
+++ /CKEditor/trunk/_source/plugins/pagebreak/plugin.js	(revision 3172)
@@ -39,20 +39,33 @@
 
 			'}' );
-		
-		// Listen for the "contentDom" event, so the document can be fixed to
-		// display the placeholders.
-		editor.on( 'contentDom', function()
-			{
-				var divs = editor.document.getBody().getElementsByTag( 'div' );
-				for ( var div, i = 0, length = divs.count() ; i < length ; i++ )
+	},
+	
+	afterInit : function( editor )
+	{
+		// Register a filter to displaying placeholders after mode change.
+				
+		var dataProcessor = editor.dataProcessor,
+			dataFilter = dataProcessor && dataProcessor.dataFilter;
+
+		if ( dataFilter )
+		{
+			dataFilter.addRules(
 				{
-					div = divs.getItem( i );
-					if ( div.getStyle( 'page-break-after' ) == 'always' && !/[^\s\u00A0]/.test( div.getText() ) )
+					elements :
 					{
-						editor.createFakeElement( div, 'cke_pagebreak', 'div' ).replace( div );
+						div : function( element )
+						{
+							var style = element.attributes.style,
+								child = style && element.children.length == 1 && element.children[ 0 ],
+								childStyle = child && ( child.name == 'span' ) && child.attributes.style;
+
+							if ( childStyle && /page-break-after\s*:\s*always/i.test( style ) && /display\s*:\s*none/i.test( childStyle ) )
+								return editor.createFakeParserElement( element, 'cke_pagebreak', 'div' );
+						}
 					}
-				}
-			});
+				});
+		}
 	},
+
 	requires : [ 'fakeobjects' ]
 });
Index: /CKEditor/trunk/_source/plugins/wysiwygarea/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/wysiwygarea/plugin.js	(revision 3171)
+++ /CKEditor/trunk/_source/plugins/wysiwygarea/plugin.js	(revision 3172)
@@ -11,61 +11,14 @@
 (function()
 {
-	// Matches all self-closing tags that are not defined as empty elements in
-	// the DTD (like &lt;span/&gt;).
-	var invalidSelfCloseTagsRegex = /(<(?!br|hr|base|meta|link|param|img|area|input|col)([a-zA-Z0-9:]+)[^>]*)\/>/gi;
-
-	// #### protectEvents - START
-
-	// Matches all tags that have event attributes (onXYZ).
-	var tagsWithEventRegex = /<[^\>]+ on\w+\s*=[\s\S]+?\>/g;
-
-	// Matches all event attributes.
-	var eventAttributesRegex = /\s(on\w+)(?=\s*=\s*?('|")[\s\S]*?\2)/g;
-
-	// Matches the protected attribute prefix.
-	var protectedEventsRegex = /_cke_pa_/g;
-
-	var protectEvents = function( html )
-	{
-		return html.replace( tagsWithEventRegex, protectEvents_ReplaceTags );
-	};
-
-	var protectEvents_ReplaceTags = function( tagMatch )
-	{
-		// Appends the "_cke_pa_" prefix to the event name.
-		return tagMatch.replace( eventAttributesRegex, ' _cke_pa_$1' );
-	};
-
-	var protectEventsRestore = function( html )
-	{
-		return html.replace( protectedEventsRegex, '' ) ;
-	};
-
-	// #### protectEvents - END
-
-	// #### protectAttributes - START
-	
-	// TODO: Clean and simplify these regexes.
-	var protectUrlTagRegex = /<(?:a|area|img)(?=\s).*?\s(?:href|src)=((?:(?:\s*)("|').*?\2)|(?:[^"'][^ >]+))/gi,
-		protectUrlAttributeRegex = /\s(href|src)(\s*=\s*?('|")[\s\S]*?\3)/gi;
-	
-	var protectUrls = function( html )
-	{
-		return html.replace( protectUrlTagRegex, protectUrls_ReplaceTags );
-	};
-
-	var protectUrls_ReplaceTags = function( tagMatch )
-	{
-		return tagMatch.replace( protectUrlAttributeRegex, '$& _cke_saved_$1$2');
-	};
-
-	// #### protectAttributes - END
-
-	var onInsertHtml = function( evt )
+	function onInsertHtml( evt )
 	{
 		if ( this.mode == 'wysiwyg' )
 		{
-			var $doc = this.document.$;
+			var $doc = this.document.$,
+				data = evt.data;
 			var data = protectHtml( evt.data );
+
+			if ( editor.dataProcessor )
+				data = editor.dataProcessor.toHtml( data );
 
 			if ( CKEDITOR.env.ie )
@@ -74,31 +27,7 @@
 				$doc.execCommand( 'inserthtml', false, data );
 		}
-	};
-
-	// ### protectCkeTags - START
-	var protectCkeTagRegex = /(<\/?)(object|embed|param)/gi
-	var protectCkeTags = function( html )
-	{
-		return html.replace( protectCkeTagRegex, '$1cke:$2' );
-	};
-	// ### protectCkeTags - END
-	
-	function protectHtml( html )
-	{
-		// Prevent event attributes (like "onclick") to
-		// execute while editing.
-		if ( CKEDITOR.env.ie || CKEDITOR.env.webkit )
-			html = protectEvents( html );
-
-		// Protect src or href attributes.
-		html = protectUrls( html );
-
-		// Protect cke prefixed tags.
-		html = protectCkeTags( html );
-
-		return html;
 	}
 
-	var onInsertElement = function( evt )
+	function onInsertElement( evt )
 	{
 		if ( this.mode == 'wysiwyg' )
@@ -141,5 +70,5 @@
 			selection.selectRanges( [ range ] );
 		}
-	};
+	}
 
 	CKEDITOR.plugins.add( 'wysiwygarea',
@@ -343,11 +272,4 @@
 									data = editor.dataProcessor.toHtml( data );
 
-								// Fix for invalid self-closing tags (see #152).
-								// TODO: Check if this fix is really needed as
-								// soon as we have the XHTML generator.
-								if ( CKEDITOR.env.ie )
-									data = data.replace( invalidSelfCloseTagsRegex, '$1></$2>' );
-
-								data = protectHtml( data );
 								data =
 									editor.config.docType +
@@ -395,13 +317,8 @@
 							getData : function()
 							{
-								var data = iframe.$.contentWindow.document.body;
+								var data = iframe.$.contentWindow.document.body.innerHTML;
 
 								if ( editor.dataProcessor )
-									data = editor.dataProcessor.toDataFormat( new CKEDITOR.dom.element( data ) );
-								else
-									data = data.innerHTML;
-
-								// Restore protected attributes.
-								data = protectEventsRestore( data );
+									data = editor.dataProcessor.toDataFormat( data );
 
 								return data;
Index: /CKEditor/trunk/_source/tests/plugins/htmldataprocessor/htmldataprocessor.html
===================================================================
--- /CKEditor/trunk/_source/tests/plugins/htmldataprocessor/htmldataprocessor.html	(revision 3171)
+++ /CKEditor/trunk/_source/tests/plugins/htmldataprocessor/htmldataprocessor.html	(revision 3172)
@@ -41,5 +41,5 @@
 			var element = new CKEDITOR.dom.element.createFromHtml( '<div><p>Test</p></div>' );
 
-			assert.areSame( '<p>Test</p>', getDataProcessor().toDataFormat( element ) );
+			assert.areSame( '<p>Test</p>', getDataProcessor().toDataFormat( element.getHtml() ) );
 		},
 
@@ -50,7 +50,7 @@
 			// IE adds the XML namespace tag.
 			if ( CKEDITOR.env.ie )
-				assert.areSame( '<?xml:namespace prefix="x" /><x:x>Test</x:x>', getDataProcessor().toDataFormat( element ) );
+				assert.areSame( '<?xml:namespace prefix="x" /><x:x>Test</x:x>', getDataProcessor().toDataFormat( element.getHtml() ) );
 			else
-				assert.areSame( '<x:x>Test</x:x>', getDataProcessor().toDataFormat( element ) );
+				assert.areSame( '<x:x>Test</x:x>', getDataProcessor().toDataFormat( element.getHtml() ) );
 		},
 
@@ -59,5 +59,5 @@
 			var element = new CKEDITOR.dom.element.createFromHtml( '<div><br /><p>Test</p></div>' );
 
-			assert.areSame( '<br /><p>Test</p>', getDataProcessor().toDataFormat( element ) );
+			assert.areSame( '<br /><p>Test</p>', getDataProcessor().toDataFormat( element.getHtml() ) );
 		},
 
@@ -68,7 +68,7 @@
 			// IE adds the XML namespace tag.
 			if ( CKEDITOR.env.ie )
-				assert.areSame( '<?xml:namespace prefix="x" /><x:x></x:x><p>Test</p>', getDataProcessor().toDataFormat( element ) );
+				assert.areSame( '<?xml:namespace prefix="x" /><x:x></x:x><p>Test</p>', getDataProcessor().toDataFormat( element.getHtml() ) );
 			else
-				assert.areSame( '<x:x></x:x><p>Test</p>', getDataProcessor().toDataFormat( element ) );
+				assert.areSame( '<x:x></x:x><p>Test</p>', getDataProcessor().toDataFormat( element.getHtml() ) );
 		},
 
@@ -77,5 +77,5 @@
 			var element = new CKEDITOR.dom.element.createFromHtml( '<div><x:x><p>Test</p></div>' );
 
-			assert.areSame( '<x:x><p>Test</p></x:x>', getDataProcessor().toDataFormat( element ) );
+			assert.areSame( '<x:x><p>Test</p></x:x>', getDataProcessor().toDataFormat( element.getHtml() ) );
 		},
 
@@ -86,7 +86,7 @@
 			// IE adds the XML namespace tag.
 			if ( CKEDITOR.env.ie )
-				assert.areSame( '<p class="MsoNormal"><b><i><span lang="EN-US"><?xml:namespace prefix="o" /><o:p>Test</o:p></span></i></b></p>', getDataProcessor().toDataFormat( element ) );
+				assert.areSame( '<p class="MsoNormal"><b><i><span lang="EN-US"><?xml:namespace prefix="o" /><o:p>Test</o:p></span></i></b></p>', getDataProcessor().toDataFormat( element.getHtml() ) );
 			else
-				assert.areSame( '<p class="MsoNormal"><b><i><span lang="EN-US"><o:p>Test</o:p></span></i></b></p>', getDataProcessor().toDataFormat( element ) );
+				assert.areSame( '<p class="MsoNormal"><b><i><span lang="EN-US"><o:p>Test</o:p></span></i></b></p>', getDataProcessor().toDataFormat( element.getHtml() ) );
 		},
 
Index: /CKEditor/trunk/ckeditor.pack
===================================================================
--- /CKEditor/trunk/ckeditor.pack	(revision 3171)
+++ /CKEditor/trunk/ckeditor.pack	(revision 3172)
@@ -110,4 +110,6 @@
 					'_source/core/htmlparser/fragment.js',
 					'_source/core/htmlparser/element.js',
+					'_source/core/htmlparser/filter.js',
+					'_source/core/htmlparser/basicwriter.js',
 					'_source/core/ckeditor.js',
 					'_source/core/dom/elementpath.js',
