Index: /CKEditor/branches/tests/editor/_assets/sample.css
===================================================================
--- /CKEditor/branches/tests/editor/_assets/sample.css	(revision 4090)
+++ /CKEditor/branches/tests/editor/_assets/sample.css	(revision 4090)
@@ -0,0 +1,4 @@
+﻿.sample
+{
+	position: absolute;
+}
Index: /CKEditor/branches/tests/editor/_assets/sample.js
===================================================================
--- /CKEditor/branches/tests/editor/_assets/sample.js	(revision 4090)
+++ /CKEditor/branches/tests/editor/_assets/sample.js	(revision 4090)
@@ -0,0 +1,1 @@
+﻿var testVar = 'Test!';
Index: /CKEditor/branches/tests/editor/_assets/sample.txt
===================================================================
--- /CKEditor/branches/tests/editor/_assets/sample.txt	(revision 4090)
+++ /CKEditor/branches/tests/editor/_assets/sample.txt	(revision 4090)
@@ -0,0 +1,1 @@
+Sample Text
Index: /CKEditor/branches/tests/editor/_assets/sample.xml
===================================================================
--- /CKEditor/branches/tests/editor/_assets/sample.xml	(revision 4090)
+++ /CKEditor/branches/tests/editor/_assets/sample.xml	(revision 4090)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<list>
+	<item id="test1" />
+	<item id="test2" />
+</list>
Index: /CKEditor/branches/tests/editor/_assets/sample_xml.txt
===================================================================
--- /CKEditor/branches/tests/editor/_assets/sample_xml.txt	(revision 4090)
+++ /CKEditor/branches/tests/editor/_assets/sample_xml.txt	(revision 4090)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<list>
+	<item id="test1" />
+	<item id="test2" />
+</list>
Index: /CKEditor/branches/tests/editor/plugins/domiterator/domiterator.html
===================================================================
--- /CKEditor/branches/tests/editor/plugins/domiterator/domiterator.html	(revision 4090)
+++ /CKEditor/branches/tests/editor/plugins/domiterator/domiterator.html	(revision 4090)
@@ -0,0 +1,233 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Plugin: domiterator</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+CKEDITOR.plugins.load( [ 'htmldataprocessor', 'htmlwriter', 'domiterator'] );
+
+
+	</script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+/**
+ * IE always returning CRLF for linefeed, so remove it when retrieve pre-formated text from text area.
+ * @param {Object} id
+ */
+function getTextAreaValue( id )
+{
+	return CKEDITOR.document.getById( id ).getValue().replace(/\r/gi,'');
+}
+
+CKEDITOR.test.addTestCase( (function()
+{
+
+	// Local references.
+	var assert = CKEDITOR.test.assert,
+		arrayAssert = YAHOO.util.ArrayAssert;
+
+	var doc = new CKEDITOR.dom.document( document );
+
+	// In these tests, we may "reset" the writer rules to avoid it formatting
+	// the output, making the assertion easier to the done. We don't need to
+	// test formatting features here, so this is ok.
+	var getDataProcessor = function()
+	{
+		var dataProcessor = new CKEDITOR.htmlDataProcessor();
+		dataProcessor.writer._.rules = [];
+		return dataProcessor;
+	};
+
+	/**
+	 * IE always returning CRLF for line-feed, so remove it when retrieving
+	 * pre-formated text from text area.
+	 */
+	function getTextAreaValue( id )
+	{
+		return CKEDITOR.document.getById( id ).getValue().replace( /\r/gi, '' );
+	}
+
+	function assumeElementContentAreSame( container, textareaId )
+	{
+		if( typeof container == 'string' )
+			container = doc.getById( container );
+		//Assume result document content
+		var html = getDataProcessor().toDataFormat( container.getHtml() );
+		assert.areSame( getTextAreaValue( textareaId ) , html );
+	}
+
+	function assumeElementAreSame( element, textareaId )
+	{
+		if( typeof element == 'string' )
+			element = doc.getById( element );
+		//Assume result document content
+		var html = getDataProcessor().toDataFormat( element.getOuterHtml() );
+		assert.areSame( getTextAreaValue( textareaId ) , html );
+	}
+
+	/**
+	 *
+	 * @param {String|CKEDITOR.dom.range} containerId|range Either the id of html container which contents are treated as range, or a exisiting range object.
+	 * @param {Object} iteratorOption
+	 * @param {Array} expectedTagList block elements tagName list in iteration orders.
+	 */
+	function assumeIterationSameAs( containerIdOrRange, iteratorOption, expectedTagList )
+	{
+		var range;
+		if( typeof containerIdOrRange == 'string' )
+		{
+			range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( doc.getById( containerIdOrRange ) );
+		}
+		else
+			range = containerIdOrRange;
+
+		var iter = range.createIterator();
+		CKEDITOR.tools.extend( iter, iteratorOption, true );
+		var blockList = [], block;
+		while (( block = iter.getNextParagraph() ) )
+		{
+			blockList.push( block.getName() );
+		}
+		arrayAssert.itemsAreEqual( expectedTagList, blockList );
+	}
+
+	return {
+
+		/**
+		 * Test iterating over table cells.
+		 */
+		test_iterator_table_cells : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( 'iterTarget2a' ), CKEDITOR.POSITION_AFTER_START );
+			range.setEndAt( doc.getById( 'iterTarget2b' ), CKEDITOR.POSITION_BEFORE_END );
+			assumeIterationSameAs( range, null, [ 'th', 'p', 'td' ]);
+			assumeElementContentAreSame( 'iterContainer2', 'iterResult2' );
+		},
+
+		/**
+		 * Test iterating over list items.
+		 */
+		test_iterator_listItems : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( 'iterTarget3a' ), CKEDITOR.POSITION_AFTER_START );
+			range.setEndAt( doc.getById( 'iterTarget3b' ), CKEDITOR.POSITION_BEFORE_END );
+			assumeIterationSameAs( range, null, [ 'li', 'p', 'li' ,'p', 'li' ]);
+			assumeElementContentAreSame( 'iterContainer3', 'iterResult3' );
+		},
+
+		/**
+		 * Test iterating over pseudo block.
+		 */
+		test_iterator_pseudoBlock : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( 'iterTarget4a' ), CKEDITOR.POSITION_AFTER_START );
+			range.setEndAt( doc.getById( 'iterTarget4b' ), CKEDITOR.POSITION_BEFORE_END );
+			assumeIterationSameAs( range, null, [ 'p', 'p' ] );
+			assumeElementContentAreSame( 'iterContainer4', 'iterResult4' );
+		},
+
+		/**
+		 * Test collapsed range before paraghraph end.
+		 */
+		test_iterator_collapsed_before_paragraph : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( 'iterTarget7' ), CKEDITOR.POSITION_BEFORE_END );
+			assumeIterationSameAs( range, null, [ 'p' ]);
+			assumeElementContentAreSame( 'iterContainer7', 'iterResult7' );
+		},
+
+		/**
+		 * Test range collapsed  inside paraghraph.
+		 */
+		test_iterator_collapsed_inside_paragraph : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( 'para8' ).getFirst(), 3 );
+			assumeIterationSameAs( range, null, [ 'p' ]);
+			assumeElementContentAreSame( 'iterContainer8', 'iterResult8' );
+		},
+
+		/**
+		 * Test fix paragraph-less content.
+		 */
+		test_iterator_establish_paragraph: function()
+		{
+			var range = new CKEDITOR.dom.range(doc);
+			range.setStartAt(doc.getById('iterTarget9'), CKEDITOR.POSITION_AFTER_START);
+			range.setEndAt(doc.getById('iterTarget9'), CKEDITOR.POSITION_BEFORE_END);
+			assumeIterationSameAs(range, null, ['p']);
+			assumeElementContentAreSame( 'iterContainer9', 'iterResult9');
+		},
+
+		/**
+		 * Test iterating over more than one paragraphs. (#3352)
+		 */
+		test_iterator_multiple_paragraphs: function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt(doc.getById( 'iterTarget10a' ), CKEDITOR.POSITION_AFTER_START );
+			range.setEndAt(doc.getById( 'iterTarget10b' ), CKEDITOR.POSITION_BEFORE_END );
+			assumeIterationSameAs( range, null, [ 'p', 'p' ] );
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+
+
+<div id="iterContainer2"><table summary="summary">
+	<caption>caption</caption>
+	<tr>
+		<th id="iterTarget2a">
+			head1</th>
+	</tr>
+	<tr>
+		<td><p>cell1</p></td>
+	</tr>
+	<tr>
+		<td id="iterTarget2b">cell2</td>
+	</tr>
+</table></div>
+<textarea id="iterResult2"><table summary="summary"><caption>caption</caption><tbody><tr><th id="iterTarget2a">head1</th></tr><tr><td><p>cell1</p></td></tr><tr><td id="iterTarget2b">cell2</td></tr></tbody></table></textarea>
+
+<div id="iterContainer3"><ul>
+	<li id="iterTarget3a">item1</li>
+	<li><p>item2</p></li>
+	<li>
+		<ul><li>item3</li></ul>
+		<ul><li><p>item5</p></li></ul>
+	</li>
+	<li id="iterTarget3b">item5</li>
+	</ul></div>
+<textarea id="iterResult3"><ul><li id="iterTarget3a">item1</li><li><p>item2</p></li><li><ul><li>item3</li></ul><ul><li><p>item5</p></li></ul></li><li id="iterTarget3b">item5</li></ul></textarea>
+
+<div id="iterContainer4">
+	<div id="iterTarget4b"><p id="iterTarget4a">paragraph</p>text</div>
+</div>
+<textarea id="iterResult4"><div id="iterTarget4b"><p id="iterTarget4a">paragraph</p><p>text</p></div></textarea>
+
+<div id="iterContainer7"><p id="iterTarget7">paragraph</p></div>
+<textarea id="iterResult7"><p id="iterTarget7">paragraph</p></textarea>
+
+<div id="iterContainer8"><p id="para8">paragraph</p></div>
+<textarea id="iterResult8"><p id="para8">paragraph</p></textarea>
+
+<form id="iterContainer9"><span id="iterTarget9">non-paragraph</span></form>
+<textarea id="iterResult9"><p><span id="iterTarget9">non-paragraph</span></p></textarea>
+
+<div id="iterContainer10"><p id="iterTarget10a">para1</p><p id="iterTarget10b">para2</p></div>
+
+</body>
+</html>
Index: /CKEditor/branches/tests/editor/plugins/htmldataprocessor/htmldataprocessor.html
===================================================================
--- /CKEditor/branches/tests/editor/plugins/htmldataprocessor/htmldataprocessor.html	(revision 4090)
+++ /CKEditor/branches/tests/editor/plugins/htmldataprocessor/htmldataprocessor.html	(revision 4090)
@@ -0,0 +1,384 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Plugin: htmldataprocessor</title>
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+// Load the required plugins and launch the runner.
+CKEDITOR.test.runner.defer = true;
+CKEDITOR.plugins.load( 'htmldataprocessor' , function()
+{
+ CKEDITOR.test.runner.run();
+} );
+
+var testCase;
+
+CKEDITOR.test.addTestCase( testCase = (function()
+{
+	// Local references.
+	var assert = CKEDITOR.test.assert;
+	var doc = new CKEDITOR.dom.document( document );
+
+	// In these tests, we may "reset" the writer rules to avoid it formatting
+	// the output, making the assertion easier to the done. We don't need to
+	// test formatting features here, so this is ok.
+	var getDataProcessor = function()
+	{
+		var dataProcessor = new CKEDITOR.htmlDataProcessor();
+		dataProcessor.writer._.rules = [];
+		return dataProcessor;
+	};
+
+	/**
+	 * IE always returning CRLF for linefeed, so remove it when retrieve pre-formated text from text area.
+	 * @param {Object} id
+	 */
+	function getTextAreaValue( id )
+	{
+		return CKEDITOR.document.getById( id ).getValue().replace(/\r/gi,'');
+	}
+
+	// These tests go far beyond the strict htmlDataProcessor code testing. We
+	// are actually testing the entire parsing system here. The combination of
+	// htmlParser and htmlWriter inside htmlDataProcessor is useful in this
+	// sense.
+
+	return {
+		test_toDataFormat_1a : function()
+		{
+			var element = new CKEDITOR.dom.element.createFromHtml( '<div><p>Test</p></div>' );
+
+			assert.areSame( '<p>Test</p>', getDataProcessor().toDataFormat( element.getHtml() ) );
+		},
+
+		test_toDataFormat_1b : function()
+		{
+			var element = new CKEDITOR.dom.element.createFromHtml( '<div><x:x>Test</x:x></div>' );
+
+			// IE adds the XML namespace tag.
+			if ( CKEDITOR.env.ie )
+				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.getHtml() ) );
+		},
+
+		test_toDataFormat_2a : function()
+		{
+			var element = new CKEDITOR.dom.element.createFromHtml( '<div><br /><p>Test</p></div>' );
+
+			assert.areSame( '<br /><p>Test</p>', getDataProcessor().toDataFormat( element.getHtml() ) );
+		},
+
+		test_toDataFormat_2b : function()
+		{
+			var element = new CKEDITOR.dom.element.createFromHtml( '<div><x:x></x:x><p>Test</p></div>' );
+
+			// 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.getHtml() ) );
+			else
+				assert.areSame( '<x:x></x:x><p>Test</p>', getDataProcessor().toDataFormat( element.getHtml() ) );
+		},
+
+		test_toDataFormat_3 : function()
+		{
+			assert.areSame( '<div><x:x><p>Test</p></x:x></div>', getDataProcessor().toDataFormat( '<div><x:x><p>Test</p></div>' ) );
+		},
+
+		test_toDataFormat_ticket_2774 : function()
+		{
+			var element = new CKEDITOR.dom.element.createFromHtml( '<div><P class=MsoNormal><B><I><SPAN lang=EN-US><o:p>Test</o:p></SPAN></I></B></P></div>' );
+
+			// 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.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.getHtml() ) );
+		},
+
+		test_toDataFormat_ticket_3036_1 : function()
+		{
+			assert.areSame( '<input autocomplete="off" checked="checked" type="checkbox" />',
+				getDataProcessor().toDataFormat( '<INPUT type="checkbox" CHECKED  autocomplete=off>' ) );
+		},
+
+		test_toDataFormat_ticket_3036_2 : function()
+		{
+			assert.areSame( '<input autocomplete="off" type="checkbox" unknown="" />',
+				getDataProcessor().toDataFormat( '<INPUT type="checkbox" UNKNOWN  autocomplete=off>' ) );
+		},
+
+		test_toDataFormat_ticket_2886_1 : function()
+		{
+			var editor = CKEDITOR.instances.editor1;
+			var test = this;
+			var isReady = !!editor.dataProcessor;
+
+			if ( !isReady )
+			{
+				editor.on( 'instanceReady', function()
+				{
+					isReady = true;
+				} );
+			}
+
+			this.wait( function()
+				{
+					if ( !isReady )
+					{
+						test.wait( arguments.callee, 100 );
+						return;
+					}
+
+					assert.areSame( '<p>\n\t&nbsp;</p>\n',
+						editor.dataProcessor.toDataFormat( '<p></p>' ) );
+				}, 100 );
+		},
+
+		test_toDataFormat_ticket_2886_2 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var source = '<p>Some text<br><br><br></p>';
+			if ( CKEDITOR.env.ie )
+				source = '<p>Some text<br><br></p>';
+			assert.areSame( '<p>\n\tSome text<br />\n\t<br />\n\t&nbsp;</p>\n',
+				dataProcessor.toDataFormat( source ) );
+		},
+
+		test_toDataFormat_ticket_2886_3 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			assert.areSame( '<p>\n\tSome text<br />\n\t<br />\n\t<br />\n\tSome more text</p>\n',
+				dataProcessor.toDataFormat( '<p>Some text<br><br><br>Some more text</p>' ) );
+		},
+
+		test_toDataFormat_ticket_2886_4 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			assert.areSame( '<p>\n\tSome text<br />\n\t<br />\n\t&nbsp;</p>\n',
+				dataProcessor.toDataFormat( '<p>Some text<br><br>&nbsp;</p>' ) );
+		},
+
+		test_toDataFormat_ticket_2886_5 : function()
+		{
+			if ( CKEDITOR.env.ie )
+				return;
+
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			assert.areSame( '<p>\n\t&nbsp;</p>\n',
+				dataProcessor.toDataFormat( '<p><br></p>' ) );
+		},
+
+		test_toDataFormat_ticket_2886_6 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var source = '<p><br><br></p>';
+			if ( CKEDITOR.env.ie )
+				source = '<p><br></p>';
+
+			assert.areSame( '<p>\n\t<br />\n\t&nbsp;</p>\n',
+				dataProcessor.toDataFormat( source ) );
+		},
+
+		test_toHtml_ticket_2886_1 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var expected = '<p><br /></p>';
+			if ( CKEDITOR.env.ie )
+				expected = '<p>\xa0</p>';
+			assert.areSame( expected, dataProcessor.toHtml( '<p></p>' ) );
+		},
+
+		test_toHtml_ticket_2886_2 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var expected = '<p>Some text<br />Some other text</p>';
+			assert.areSame( expected, dataProcessor.toHtml( '<p>Some text<br>Some other text</p>' ) );
+		},
+
+		test_toHtml_ticket_2886_3 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var expected = '<p>Some text<br /><br /></p>';
+			if ( CKEDITOR.env.ie )
+				expected = '<p>Some text<br />\xa0</p>';
+			assert.areSame( expected, dataProcessor.toHtml( '<p>Some text<br>&nbsp;</p>' ) );
+		},
+
+		test_toHtml_ticket_2886_4 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var expected = '<p>Some text</p>';
+			assert.areSame( expected, dataProcessor.toHtml( '<p>Some text<br></p>' ));
+		},
+
+		test_ticket_3407 : function()
+		{
+			var editor = CKEDITOR.instances.editor1,
+				dataProcessor = editor.dataProcessor,
+				config = editor.config;
+
+			config.protectedSource.push( /<\?[\s\S]*?\?>/g );   // PHP Code
+			config.protectedSource.push( /<%[\s\S]*?%>/g );   // ASP Code
+			config.protectedSource.push( /(<asp:[^\>]+>[\s|\S]*?<\/asp:[^\>]+>)|(<asp:[^\>]+\/>)/gi );   // ASP.Net Code
+			dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
+			var html = getTextAreaValue( '_TEXTAREA1' );
+			var protectedHtml = dataProcessor.toHtml( html );
+			assert.areSame( html , dataProcessor.toDataFormat( protectedHtml ) );
+		},
+
+		test_ticket_3591 : function()
+		{
+			var editor = CKEDITOR.instances.editor1,
+				dataProcessor = editor.dataProcessor;
+
+			dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
+			var html = getTextAreaValue( '_TEXTAREA_3591' );
+			var protectedHtml = dataProcessor.toHtml( html );
+
+			assert.areSame( getTextAreaValue( CKEDITOR.env.ie ? '_TEXTAREA_3591_protected_ie' : '_TEXTAREA_3591_protected' ),
+				protectedHtml );
+			assert.areSame( getTextAreaValue( '_TEXTAREA_3591' ),
+				dataProcessor.toDataFormat( protectedHtml ) );
+		},
+
+		test_ticket_3591_2 : function()
+		{
+			var editor = CKEDITOR.instances.editor1,
+				dataProcessor = editor.dataProcessor;
+
+			dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
+			dataProcessor.writer.sortAttributes = true;
+
+			var html = getTextAreaValue( '_TEXTAREA_3591_2' );
+			var protectedHtml = dataProcessor.toHtml( html );
+
+			assert.areSame( getTextAreaValue( '_TEXTAREA_3591_2' ),
+				dataProcessor.toDataFormat( protectedHtml ) );
+		},
+
+		test_ticket_3869_1 : function()
+		{
+			var editor = CKEDITOR.instances.editor1,
+				dataProcessor = editor.dataProcessor;
+
+			dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
+			var html = getTextAreaValue( '_TEXTAREA_3869_1' );
+			var protectedHtml = dataProcessor.toHtml( html );
+
+			assert.areSame( html , dataProcessor.toDataFormat( protectedHtml ) );
+		},
+
+		test_ticket_3869_2 : function()
+		{
+			var editor = CKEDITOR.instances.editor1,
+				dataProcessor = editor.dataProcessor,
+				config = editor.config;
+
+			config.protectedSource.push( /<\?[\s\S]*?\?>/g );   // PHP Code
+			config.protectedSource.push( /<%[\s\S]*?%>/g );   // ASP Code
+			config.protectedSource.push( /(<asp:[^\>]+>[\s|\S]*?<\/asp:[^\>]+>)|(<asp:[^\>]+\/>)/gi );   // ASP.Net Code
+			config.protectedSource.push(/<gallery[\s\S]*?<\/gallery>/gi );	// custom protected source
+			config.protectedSource.push(/<options[\s\S]*?<\/options>/gi );
+			dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
+			var html = getTextAreaValue( '_TEXTAREA_3869_2' );
+			var protectedHtml = dataProcessor.toHtml( html );
+
+			assert.areSame( html , dataProcessor.toDataFormat( protectedHtml ) );
+		},
+
+		/**
+		 * Test empty value attributes.
+		 */
+		test_ticket_3884 : function()
+		{
+			var editor = CKEDITOR.instances.editor1,
+				dataProcessor = editor.dataProcessor;
+			dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
+			dataProcessor.writer.sortAttributes = true;
+
+			assert.areSame( '<p><a href="" name="">emptylink</a></p>',
+				dataProcessor.toDataFormat( dataProcessor.toHtml( '<p><a href="" name="">emptylink</a></p>' ) ) );
+		},
+
+		test_innerHtmlComments_ticket_3801 : function()
+		{
+			var editor = CKEDITOR.instances.editor1,
+				dataProcessor = editor.dataProcessor;
+
+			dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
+
+			for ( var i = 1; i <= 7; i++ )
+			{
+				var html = getTextAreaValue( '_TEXTAREA_3801_' + i );
+				var protectedHtml = dataProcessor.toHtml( html );
+
+				assert.areSame( getTextAreaValue( '_TEXTAREA_3801_' + i ),
+					dataProcessor.toDataFormat( protectedHtml ) );
+			}
+		},
+
+		name : document.title
+	};
+})() );
+
+//window.onload = testCase.test_ticket_3884;
+	//]]>
+	</script>
+</head>
+<body>
+	<textarea id="editor1" class="ckeditor" cols="80" rows="10"></textarea>
+	<textarea id="_TEXTAREA1"><script type="text/javascript">alert('>');</script><table><tbody><tr><!--- IE doesn't handle this comment ---><!--[if gte IE 6 ]>
+	<![if lt IE 8 ]>
+	gIE conditional comments
+<![endif]>
+<![endif]--><td><%Response.Write(now())%></td><td><asp:control_name id="some_id" runat="server"/></td><td><?php
+include ("head.html"); ?></td></tr></tbody></table><noscript>Your browser doesn't support JavaScript</noscript></textarea>
+	<textarea id="_TEXTAREA_3591"><object><param /><param /><embed></embed></object></textarea>
+	<textarea id="_TEXTAREA_3591_protected"><cke:object><cke:param></cke:param><cke:param></cke:param><cke:embed></cke:embed></cke:object></textarea>
+	<textarea id="_TEXTAREA_3591_protected_ie"><?xml:namespace prefix="cke" /><cke:object><cke:param></cke:param><cke:param></cke:param><cke:embed></cke:embed></cke:object></textarea>
+ 	<textarea id="_TEXTAREA_3801_1"><!-- foo --><p>bar</p></textarea>
+ 	<textarea id="_TEXTAREA_3801_2"><p><!-- foo -->bar</p></textarea>
+ 	<textarea id="_TEXTAREA_3801_3"><!-- foo --><p><!-- foo -->bar</p></textarea>
+ 	<textarea id="_TEXTAREA_3801_4"><p>bar</p><!-- foo --></textarea>
+ 	<textarea id="_TEXTAREA_3801_5">bar<!-- foo --></textarea>
+ 	<textarea id="_TEXTAREA_3801_6"><!-- foo -->bar</textarea>
+ 	<textarea id="_TEXTAREA_3801_7"><p><!-- foo -->bar</p><p><!-- foo -->bar</p></textarea>
+	<textarea id="_TEXTAREA_3869_1">#3869<script language="Javascript" type="text/javascript">
+	<!--
+		alert('-->');
+	//-->
+	</script></textarea>
+	<textarea id="_TEXTAREA_3869_2">#3869&lt;gallery&gt;
+	file.jpg
+	&lt;options name="test1" value="value1"&gt;&lt;/options&gt;
+	&lt;options name="test2" value="value2"&gt;&lt;/options&gt;
+	&lt;/gallery&gt;&lt;?
+		echo '&lt;script type="text/javascript"&gt;';
+		echo '&lt;!--alert("--&gt;");//--&gt;';
+		echo '&lt;/script&gt;';
+	?&gt;&lt;!--&lt;%Response.Write(now())%&gt;//--&gt;invalid&lt;?
+		echo 'bbb&lt;script&gt;
+		&lt;!--
+		ccc();&lt;!-- &lt;/script&gt;&lt;!-- &lt;/script&gt;
+		//--&gt;&lt;/script&gt;
+		&lt;script&gt;&lt;!--
+		&lt;!--
+		ffff();
+		//--&gt;&lt;/script&gt;
+		ddd';
+	?&gt;html</textarea>
+	<textarea id="_TEXTAREA_3591_2"><object classid="clsid"><param name="movie" value="movie.swf" /><embed src="movie.swf" type="application/x-shockwave-flash"></embed></object></textarea>
+</body>
+</html>
Index: /CKEditor/branches/tests/editor/plugins/link/link.html
===================================================================
--- /CKEditor/branches/tests/editor/plugins/link/link.html	(revision 4090)
+++ /CKEditor/branches/tests/editor/plugins/link/link.html	(revision 4090)
@@ -0,0 +1,121 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Plugin: link</title>
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+/**
+ * Load the editor and wait for fully interactable.
+ * @param {Object} elementId
+ * @parma {Object} mode
+ * @param {Object} config
+ * @param {Object} callback Continuation with {@param editor}.
+ * @param {Object} context
+ */
+function prepareEditor( elementId, mode, config, callback, context )
+{
+	CKEDITOR.on( 'instanceReady',
+		function( evt )
+		{
+			var isMe = mode == CKEDITOR.ELEMENT_MODE_REPLACE ?
+				evt.editor.name == elementId
+				: evt.editor.element.$ ==
+					document.getElementById( elementId );
+			if ( isMe )
+			{
+				var editor = evt.editor;
+				// Force result data unformatted.
+				editor.dataProcessor.writer._.rules = {};
+				// Force remove tail br.
+				editor.dataProcessor.htmlFilter.addRules( {
+					elements : {
+						'br' : function( br ){
+							var parent = br.parent,
+								length = parent.children.length,
+								lastChild = parent.children[ length - 1 ];
+							if( lastChild == br )
+								return false;
+						}
+					}
+				} );
+				callback.call( context, editor );
+			}
+		}, this );
+
+	mode = mode || CKEDITOR.ELEMENT_MODE_REPLACE;
+	switch( mode )
+	{
+		case CKEDITOR.ELEMENT_MODE_REPLACE :
+			CKEDITOR.replace( elementId, config );
+			break;
+		case CKEDITOR.ELEMENT_MODE_APPENDTO :
+			CKEDITOR.appendTo( elementId, config );
+			break;
+	}
+}
+
+/**
+ * IE always returning CRLF for line-feed, so remove it when retrieving
+ * pre-formated text from text area.
+ */
+function getTextAreaValue( id )
+{
+	return CKEDITOR.document.getById( id ).getValue().replace( /\r/gi, '' );
+}
+
+CKEDITOR.test.addTestCase( ( function()
+	{
+
+		// Local references.
+		var assert = CKEDITOR.test.assert,
+			doc = CKEDITOR.document,
+			action = YAHOO.util.UserAction,
+			selector = YAHOO.util.Selector;
+
+		return	{
+
+			/**
+			 *  Test remove numbered list with 'enterMode = BR'.
+			 */
+			test_create_link : function()
+			{
+				prepareEditor( 'test_create_link_editor', null,
+					null,
+					function( editor )
+					{
+						this.resume( function()
+						{
+				            editor.focus();
+				            editor.execCommand( 'link' );
+							// waiting for dialog to open.
+				            this.wait( function()
+							 {
+							 	var dialog = editor._.storedDialogs[ 'link' ];
+							 	var urlField = dialog.getContentElement( 'info', 'url' );
+							 	urlField.setValue( 'svn.fckeditor.net' );
+
+							 	dialog.fire( 'ok' );
+							 	dialog.hide();
+
+							 	var result = editor.getData();
+							 	assert.areEqual(
+								 '<p><a href="http://svn.fckeditor.net">http://svn.fckeditor.net</a></p>',
+								 result, 'Created link element doesn\'t match.' );
+
+							 }, 1000 );
+						} );
+					}, this );
+					this.wait();
+			},
+
+			name :document.title
+		};
+	} )() );
+	//]]>
+	</script>
+</head>
+<body>
+<textarea id="test_create_link_editor"></textarea>
+</body>
+</html>
Index: /CKEditor/branches/tests/editor/plugins/list/list.html
===================================================================
--- /CKEditor/branches/tests/editor/plugins/list/list.html	(revision 4090)
+++ /CKEditor/branches/tests/editor/plugins/list/list.html	(revision 4090)
@@ -0,0 +1,215 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Plugin: list</title>
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+/**
+ * Load the editor and wait for fully interactable.
+ * @param {Object} elementId
+ * @parma {Object} mode
+ * @param {Object} config
+ * @param {Object} callback Continuation with {@param editor}.
+ * @param {Object} context
+ */
+function prepareEditor( elementId, mode, config, callback, context )
+{
+	CKEDITOR.on( 'instanceReady',
+		function( evt )
+		{
+			var isMe = mode == CKEDITOR.ELEMENT_MODE_REPLACE ?
+				evt.editor.name == elementId
+				: evt.editor.element.$ ==
+					document.getElementById( elementId );
+			if ( isMe )
+			{
+				var editor = evt.editor;
+				// Force result data unformatted.
+				editor.dataProcessor.writer._.rules = {};
+				callback.call( context, editor );
+			}
+		}, this );
+
+	mode = mode || CKEDITOR.ELEMENT_MODE_REPLACE;
+	switch( mode )
+	{
+		case CKEDITOR.ELEMENT_MODE_REPLACE :
+			CKEDITOR.replace( elementId, config );
+			break;
+		case CKEDITOR.ELEMENT_MODE_APPENDTO :
+			CKEDITOR.appendTo( elementId, config );
+			break;
+	}
+}
+
+/**
+ * IE always returning CRLF for line-feed, so remove it when retrieving
+ * pre-formated text from text area.
+ */
+function getTextAreaValue( id )
+{
+	return CKEDITOR.document.getById( id ).getValue().replace( /\r/gi, '' );
+}
+
+CKEDITOR.test.addTestCase( ( function()
+	{
+
+		// Local references.
+		var assert = CKEDITOR.test.assert,
+			doc = CKEDITOR.document,
+			action = YAHOO.util.UserAction,
+			selector = YAHOO.util.Selector;
+
+		/**
+		 * Set the range with the start/end position specified by the locator, which in form of bookmark2.
+		 * @param {Object} range
+		 * @param {Array} startPosition range start path including offset
+		 * @param {Array|Boolean} endPositoin range end path including offset or is collapsed
+		 */
+		function setRange( range, startPosition, endPositoin )
+		{
+			var bm = {
+				end : null,
+				start : null,
+				is2: true,
+				startOffset : 0,
+				endoffset : 0
+			};
+			bm.start = startPosition.slice( 0, startPosition.length - 1 );
+			bm.startOffset = startPosition[ startPosition.length -1];
+			if( endPositoin === true )
+			{
+				bm.end = bm.start.slice();
+				bm.endOffset = bm.startOffset;
+			}
+			else
+			{
+				bm.end = endPositoin.slice( 0, endPositoin.length - 1 );
+				bm.endOffset = endPositoin[ endPositoin.length -1 ];
+			}
+			range.moveToBookmark( bm );
+		}
+
+		return	{
+
+			/**
+			 *  Test remove numbered list with 'enterMode = BR'.
+			 */
+			test_ticket_3151 : function()
+			{
+				prepareEditor( 'test_ticket_3151_editor', null,
+					{ enterMode : CKEDITOR.ENTER_BR },
+					function( editor )
+					{
+						this.resume( function()
+						{
+							editor.focus();
+
+							var doc = editor.document,
+								range = new CKEDITOR.dom.range( doc );
+
+							setRange( range, [ 1, 0, 0, 0, 0 ], true );
+							var sel = editor.getSelection();
+							sel.selectRanges( [ range ] );
+
+							// Waiting for 'comand state' effected.
+							this.wait( function(){
+								// Remove list.
+								editor.execCommand( 'numberedlist' );
+								assert.areSame( getTextAreaValue( 'test_ticket_3151_result' ),
+									editor.getData(),
+									'Remove list result not correct.' );
+							}, 1000 );
+
+						} );
+					}, this );
+					this.wait();
+			},
+
+			/**
+			 *  Test merge newlist with previous list.
+			 */
+			test_ticket_3820 : function()
+			{
+				prepareEditor( 'test_ticket_3820_editor', null,
+					{ enterMode : CKEDITOR.ENTER_BR },
+					function( editor )
+					{
+						this.resume( function()
+						{
+							editor.focus();
+
+							var doc = editor.document,
+								range = new CKEDITOR.dom.range( doc );
+							setRange( range, [ 1, 1, 0 ], true );
+							var sel = editor.getSelection();
+							sel.selectRanges( [ range ] );
+
+							// Waiting for 'comand state' effected.
+							this.wait( function(){
+								// Remove list.
+								editor.execCommand( 'bulletedlist' );
+								assert.areSame( getTextAreaValue( 'test_ticket_3820_result' ),
+									editor.getData(),
+									'Merge list result not correct.' );
+							}, 1000 );
+
+						} );
+					}, this );
+					this.wait();
+			},
+
+			/**
+			 *  Test remove list first list item not merging with previous text node.
+			 */
+			test_ticket_3836 : function()
+			{
+				prepareEditor( 'test_ticket_3836_editor', null,
+					{ enterMode : CKEDITOR.ENTER_BR },
+					function( editor )
+					{
+						this.resume( function()
+						{
+							this.wait( function ()
+							{
+								editor.focus();
+
+								var doc = editor.document,
+									range = new CKEDITOR.dom.range( doc );
+								setRange( range, [ 1, 1, 0, 0 ], [ 1, 1, 1, 1 ] );
+								var sel = editor.getSelection();
+								sel.selectRanges( [ range ] );
+
+								// Waiting for 'comand state' effected.
+								this.wait( function(){
+									// Remove list.
+									editor.execCommand( 'bulletedlist' );
+									assert.areSame( getTextAreaValue( 'test_ticket_3836_result' ),
+										editor.getData(),
+										'Remove list result not correct.' );
+								}, 1000 );
+							}, 1000 );
+
+						} );
+					}, this );
+					this.wait();
+			},
+
+			name :document.title
+		};
+	} )() );
+	//]]>
+	</script>
+</head>
+<body>￘
+<textarea id="test_ticket_3151_editor"><ol><li>text</li></ol></textarea>
+<textarea id="test_ticket_3151_result">text</textarea>
+<textarea id="test_ticket_3820_editor"><ul><li>bullet line 1</li><li>bullet line 2</li></ul><p>Second line</p></textarea>
+<textarea id="test_ticket_3820_result"><ul><li>bullet line 1</li><li>bullet line 2</li><li>Second line</li></ul></textarea>
+<textarea id="test_ticket_3773_editor"><ol><li>line1</li><li>line2</li></ol></textarea>
+<textarea id="test_ticket_3773_result">line1<br />line2</textarea>
+<textarea id="test_ticket_3836_editor">line1<ul><li>item1</li><li>item2</li></ul>line2</textarea>
+<textarea id="test_ticket_3836_result">line1<br />item1<br />item2<br />line2</textarea>
+</body>
+</html>
Index: /CKEditor/branches/tests/editor/plugins/selection/selection.html
===================================================================
--- /CKEditor/branches/tests/editor/plugins/selection/selection.html	(revision 4090)
+++ /CKEditor/branches/tests/editor/plugins/selection/selection.html	(revision 4090)
@@ -0,0 +1,47 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Plugin: selection</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+//<![CDATA[
+CKEDITOR.plugins.load( [ 'selection' ] );
+//]]>
+	</script>
+	<script type="text/javascript">
+//<![CDATA[
+CKEDITOR.test.addTestCase( (function()
+{
+	var assert = CKEDITOR.test.assert;
+	var doc = new CKEDITOR.dom.document( document );
+
+	return {
+		test_selection_ie : function()
+		{
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			var $range = document.body.createTextRange();
+			$range.findText( 'This' );
+			$range.select();
+
+			var range = doc.getSelection().getRanges()[ 0 ];
+			range.select();
+
+			var $range2 = document.selection.createRange();
+			assert.areSame( 0, $range.compareEndPoints( 'StartToStart', $range2 ), 'StartToStart' );
+			assert.areSame( 0, $range.compareEndPoints( 'EndToEnd', $range2 ), 'EndToEnd' );
+		},
+
+		name : document.title
+	};
+})());
+//]]>
+	</script>
+</head>
+<body>
+	<p>
+		This is some <strong>sample text</strong>. You are using <a href="http://www.fckeditor.net/">CKEditor</a>.</p>
+</body>
+</html>
Index: /CKEditor/branches/tests/editor/plugins/styles/styles.html
===================================================================
--- /CKEditor/branches/tests/editor/plugins/styles/styles.html	(revision 4090)
+++ /CKEditor/branches/tests/editor/plugins/styles/styles.html	(revision 4090)
@@ -0,0 +1,595 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Plugin: styles</title>
+	<link rel="stylesheet" type="text/css" href="../../test.css" />
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+
+CKEDITOR.plugins.load( [ 'styles', 'domiterator', 'htmldataprocessor' ] );
+
+	</script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+var testCase;
+
+CKEDITOR.test.addTestCase( testCase = (function()
+{
+	// Local references.
+	var assert			= CKEDITOR.test.assert;
+	var getInnerHtml	= CKEDITOR.test.getInnerHtml;
+
+	function getInnerHtmlParsed( element )
+	{
+		var dataProcessor = new CKEDITOR.htmlDataProcessor();
+		dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
+		return dataProcessor.toDataFormat( getInnerHtml( element, false ) );
+	}
+
+	var doc = new CKEDITOR.dom.document( document );
+
+	return {
+		test_inline1 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this is some sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ).getFirst(), 5 );
+			range.setEnd( doc.getById( '_P1' ).getFirst(), 7 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( 'this <b>is</b> some sample text', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline2 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <b>is some </b>sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ).getChild( 1 ), 0 );
+			range.setEnd( doc.getById( '_P1' ), 2 );
+
+			var style = new CKEDITOR.style( { element : 'i' } );
+			style.applyToRange( range );
+
+			assert.areSame( 'this <i><b>is some </b></i>sample text', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline3 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <b>is some </b>sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ).getChild( 1 ), 0 );
+			range.setEnd( doc.getById( '_P1' ).getChild( 1 ).getFirst(), 2 );
+
+			var style = new CKEDITOR.style( { element : 'i' } );
+			style.applyToRange( range );
+
+			assert.areSame( 'this <b><i>is</i> some </b>sample text', getInnerHtml( '_P1' ) );
+		},
+
+		// Inline - Remove inner duplicates.
+		test_inline4 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <b>is some </b>sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ), 3 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<b>this is some sample text</b>', getInnerHtml( '_P1' ) );
+		},
+
+		// Inline - Merge with next.
+		test_inline5 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <b>is some </b>sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ), 1 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<b>this is some </b>sample text', getInnerHtml( '_P1' ) );
+		},
+
+		// Inline - Merge with previous.
+		test_inline6 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <b>is some </b>sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 2 );
+			range.setEnd( doc.getById( '_P1' ).getChild( 2 ), 6 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( 'this <b>is some sample</b> text', getInnerHtml( '_P1' ) );
+		},
+
+		// Inline - Merge several with next.
+		test_inline7 : function()
+		{
+			doc.getById( '_P1' ).setHtml( '<i><u>this </u></i><b><i><u>is</u> some</i> sample</b> text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ), 1 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<b><i><u>this is</u> some</i> sample</b> text', getInnerHtml( '_P1' ) );
+		},
+
+		// Inline - Merge several with previous.
+		test_inline8 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <b>is <i>some <u>sample</u></i></b><i><u> text</u></i>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 2 );
+			range.setEnd( doc.getById( '_P1' ), 3 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( 'this <b>is <i>some <u>sample text</u></i></b>', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline9 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <i>is some </i>sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ).getChild( 1 ), 0 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<b>this </b><i>is some </i>sample text', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline10 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this is some sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ), 1 );
+
+			var style = new CKEDITOR.style(
+				{
+					element : 'b',
+					attributes :
+						{
+							lang : 'it',
+							title : 'test'
+						},
+					styles :
+						{
+							'font-size' : '10pt',
+							'text-decoration' : 'line-through'
+						}
+				} );
+			style.applyToRange( range );
+
+			assert.areSame( '<b lang="it" style="font-size:10pt;text-decoration:line-through;" title="test">this is some sample text</b>', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline11 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <b lang="it" class="sample">is</b> <b lang="it" style="font-size: 10pt; text-decoration: line-through;" title="test">some sample</b> <b>t</b>ext' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ), 7 );
+
+			var style = new CKEDITOR.style(
+				{
+					element : 'b',
+					attributes :
+						{
+							lang : 'it',
+							title : 'test'
+						},
+					styles :
+						{
+							'font-size' : '10pt',
+							'text-decoration' : 'line-through'
+						}
+				} );
+			style.applyToRange( range );
+
+			assert.areSame( '<b lang="it" style="font-size: 10pt; text-decoration: line-through;" title="test">this <b class="sample">is</b> some sample text</b>', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline11 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <span class="a">is</span> some <span class="b">sample</span> text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ), 5 );
+
+			var style = new CKEDITOR.style( { element : 'span', attributes : { 'class' : 'b' } } );
+			style.applyToRange( range );
+
+			assert.areSame( '<span class="b">this <span class="a">is</span> some sample text</span>', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline12 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <span style="font-size:12pt; font-weight:600">is</span> some <span style="font-size:10px;">sample</span> text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ), 5 );
+
+			var style = new CKEDITOR.style( { element : 'span', styles : { 'font-size' : '1.5em' } } );
+			style.applyToRange( range );
+
+			assert.areSame( '<span style="font-size:1.5em;">this <span style="font-weight:600;">is</span> some sample text</span>', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline13 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this <b>is some sample</b> text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ).getChild( 1 ).getFirst(), 3 );
+			range.setEnd( doc.getById( '_P1' ), 3 );
+
+			var style = new CKEDITOR.style( { element : 'i' } );
+			style.applyToRange( range );
+
+			assert.areSame( 'this <b>is <i>some sample</i></b><i> text</i>', getInnerHtml( '_P1' ) );
+		},
+
+		test_inline14 : function()
+		{
+			var para = doc.getById( '_P1' );
+
+			para.setHtml( 'this is some sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( para.getFirst(), 0 );
+			range.setEnd( para.getFirst(), 7 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<b>this is</b> some sample text', getInnerHtml( '_P1' ), 'First range' );
+
+			para.setHtml( para.getHtml() );
+
+			range = new CKEDITOR.dom.range( doc );
+			range.setStart( para.getFirst().getFirst(), 5 );
+			range.setEnd( para.getChild( 1 ), 5 );
+
+			style.applyToRange( range );
+
+			assert.areSame( '<b>this is some</b> sample text', getInnerHtml( '_P1' ), 'Second range' );
+		},
+
+		test_inline15 : function()
+		{
+			var para = doc.getById( '_P1' );
+
+			para.setHtml( 'this is some sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( para.getFirst(), 0 );
+			range.setEnd( para.getFirst(), 7 );
+
+			var style = new CKEDITOR.style( { element : 'span', styles : { 'font-family' : '#(family)' } }, { family : 'Arial,Helvetica,sans-serif' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<span style="font-family:arial,helvetica,sans-serif;">this is</span> some sample text', getInnerHtml( '_P1' ), 'First range' );
+
+			para.setHtml( para.getHtml() );
+
+			range = new CKEDITOR.dom.range( doc );
+			range.setStart( para.getFirst().getFirst(), 5 );
+			range.setEnd( para.getChild( 1 ), 5 );
+
+			style = new CKEDITOR.style( { element : 'span', styles : { 'font-family' : '#(family)' } }, { family : 'Georgia,serif' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<span style="font-family:arial,helvetica,sans-serif;">this <span style="font-family:georgia,serif;">is</span></span><span style="font-family:georgia,serif;"> some</span> sample text', getInnerHtml( '_P1' ), 'Second range' );
+		},
+
+		test_inline16 : function()
+		{
+			var para = doc.getById( '_P1' );
+
+			para.setHtml( '<b lang="pt" style="font-size:11pt;color:red;">this is some sample text</b>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( para.getFirst().getFirst(), 4 );
+			range.setEnd( para.getFirst(), 10 );
+
+			var style = new CKEDITOR.style( { element : 'b', styles : { color : 'red', 'font-weight' : '700' } } );
+			style.applyToRange( range );
+
+			assert.areSame( '<b lang="pt" style="color:red;font-size:11pt;">this<b style="font-weight:700;"> is some sample text</b></b>', getInnerHtml( '_P1' ), 'First range' );
+		},
+
+		test_inline_nobreak1 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'this is <a href="http://example.com/">some sample</a> text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 0 );
+			range.setEnd( doc.getById( '_P1' ).getChild( 1 ).getFirst(), 4 );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<b>this is </b><a href="http://example.com/"><b>some</b> sample</a> text', getInnerHtml( '_P1' ) );
+		},
+
+		test_ticket_2040 : function()
+		{
+			doc.getById( '_P1' ).setHtml( 'This is some <strong>sample text<\/strong>. You are using <a href="http://www.fckeditor.net/">ckeditor<\/a>.' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_P1' ), 1 );
+			range.setEnd( doc.getById( '_P1' ).getChild( 1 ).getFirst(), 6 );
+
+			var style = new CKEDITOR.style( { element : 'i' } );
+			style.applyToRange( range );
+
+			assert.areSame( 'this is some <strong><i>sample</i> text<\/strong>. you are using <a href="http://www.fckeditor.net/">ckeditor<\/a>.', getInnerHtml( '_P1' ) );
+		},
+
+		test_checkElementRemovable1 : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<b>Test</b>', doc );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+
+			assert.isTrue( style.checkElementRemovable( element ) );
+		},
+
+		test_checkElementRemovable2 : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<b>Test</b>', doc );
+
+			var style = new CKEDITOR.style( { element : 'i' } );
+
+			assert.isFalse( style.checkElementRemovable( element ) );
+		},
+
+		test_checkElementRemovable3 : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<b>Test</b>', doc );
+
+			var style = new CKEDITOR.style( { element : 'b', attributes : { lang : 'pt' } } );
+
+			assert.isTrue( style.checkElementRemovable( element ) );
+		},
+
+		test_checkElementRemovable4 : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<b>Test</b>', doc );
+
+			var style = new CKEDITOR.style( { element : 'b', attributes : { lang : 'pt' } } );
+
+			assert.isFalse( style.checkElementRemovable( element, true ) );
+		},
+
+		test_checkElementRemovable5 : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<span lang="pt" style="color : #fff">Test</span>', doc );
+
+			var style = new CKEDITOR.style( { element : 'span', attributes : { lang : 'pt' }, style : { color : '#ffffff' } } );
+
+			assert.isTrue( style.checkElementRemovable( element, true ) );
+		},
+
+		test_checkElementRemovable6 : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<span lang="pt" style="color : #fff">Test</span>', doc );
+
+			var style = new CKEDITOR.style( { element : 'span', attributes : { lang : 'pt' }, style : { color : '#fffff0' } } );
+
+			assert.isTrue( style.checkElementRemovable( element, true ) );
+		},
+
+		test_checkElementRemovable7 : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<span lang="pt" style="color : #fff">Test</span>', doc );
+
+			var style = new CKEDITOR.style( { element : 'span', attributes : { lang : 'fr' }, style : { color : '#ffffff' } } );
+
+			assert.isFalse( style.checkElementRemovable( element, true ) );
+		},
+
+		test_checkElementRemovable8 : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<span lang="pt" style="font-size: 10px">Test</span>', doc );
+
+			var style = new CKEDITOR.style( { element : 'span', attributes : { lang : 'pt' , style : 'font-size:10px;' } } );
+
+			assert.isTrue( style.checkElementRemovable( element, true ) );
+		},
+
+		test_ticket_3091 : function()
+		{
+			var element = doc.getById( '_P1' );
+			element.setHtml( 'outter<table><tr><td>text</td></tr></table>outter' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( element );
+
+			var styleDef =
+			{
+				element		: 'span',
+				styles		: { 'font-family' : '#(family)' },
+				overrides	: [ { element : 'font', attributes : { 'face' : null } } ]
+			};
+
+			var style = new CKEDITOR.style( styleDef, { 'family' : 'Arial,Helvetica,sans-serif' } );
+			style.applyToRange( range );
+
+			style = new CKEDITOR.style( styleDef, { 'family' : 'Comic Sans MS,cursive' } );
+			style.applyToRange( range );
+
+			style = new CKEDITOR.style( styleDef, { 'family' : 'Courier New,Courier,monospace' } );
+			style.applyToRange( range );
+
+			assert.areSame( '<span style="font-family:couriernew,courier,monospace;">outter</span><table><tbody><tr><td><span style="font-family:couriernew,courier,monospace;">text</span></td></tr></tbody></table><span style="font-family:couriernew,courier,monospace;">outter</span>', getInnerHtml( element ) );
+		},
+
+		test_ticket_3091_2 : function()
+		{
+			var element = doc.getById( '_P1' );
+			element.setHtml( 'outter<p>text</p>outter' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( element );
+
+			var style = new CKEDITOR.style( { element : 'i', attributes : { title : 'x' } } );
+			style.applyToRange( range );
+
+			assert.areSame( '<i title="x">outter</i><p><i title="x">text</i></p><i title="x">outter</i>', getInnerHtml( element ), 'First step failed' );
+
+			style = new CKEDITOR.style( { element : 'i', attributes : { title : 'y' } } );
+			style.applyToRange( range );
+
+			assert.areSame( '<i title="y">outter</i><p><i title="y">text</i></p><i title="y">outter</i>', getInnerHtml( element ), 'Second step failed' );
+
+			style = new CKEDITOR.style( { element : 'i', attributes : { title : 'z' } } );
+			style.applyToRange( range );
+
+			assert.areSame( '<i title="z">outter</i><p><i title="z">text</i></p><i title="z">outter</i>', getInnerHtml( element ), 'Third step failed' );
+		},
+
+		// TC based on the state of the second step in the above test, before it got fixed.
+		test_ticket_3091_3 : function()
+		{
+			var element = doc.getById( '_P1' );
+			element.setHtml( '<p><i title="y">text</i><i title="x"></i></p><i title="y">outter</i><i title="x"></i>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( element );
+
+			var style = new CKEDITOR.style( { element : 'i', attributes : { title : 'z' } } );
+			style.applyToRange( range );
+
+			assert.areSame( '<p><i title="z">text</i></p><i title="z">outter</i>', getInnerHtml( element ) );
+		},
+
+		// Remove inline style when range collapsed at element boundaries,
+		// move out of the removing-style element, with inner style copied.
+		test_ticket_3309 : function()
+		{
+			var element = doc.getById( '_P1' );
+			element.setHtml( 'this is some <b><i id="_i1">styles</i></b> text' );
+
+			// This is some <b><i>styles^</i></b> text
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( '_i1' ), CKEDITOR.POSITION_BEFORE_END );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.removeFromRange( range );
+
+			assert.areSame( 'this is some <b><i id="_i1">styles</i></b><i></i> text', getInnerHtml( element ) );
+		},
+
+		// No inner style preserved, simply move out of the removing-style element.
+		test_ticket_3309_2 : function()
+		{
+			var element = doc.getById( '_P1' );
+			element.setHtml( 'this is some <b id="_b1">styles</b> text' );
+
+			// This is some <b>styles^</b> text
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( '_b1' ), CKEDITOR.POSITION_BEFORE_END );
+
+			var style = new CKEDITOR.style( { element : 'b' } );
+			style.removeFromRange( range );
+			// This is some <b>styles</b>^ text
+			assert.areSame( doc.getById( '_b1' ).getParent().$, range.startContainer.$ );
+			assert.areSame( 2, range.startOffset );
+			assert.areSame( 'this is some <b id="_b1">styles</b> text', getInnerHtml( element ) );
+		},
+
+		// With style overrides.
+		test_ticket_3309_3 : function()
+		{
+			var element = doc.getById( '_P1' );
+			element.setHtml( 'text <strong><bold><span><b><i id="_i1">styles</i></b></span></bold></strong>' );
+
+			// text <strong><bold><span><b><i id="_i1">^styles</i></b></span></bold></strong>
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( '_i1' ), CKEDITOR.POSITION_AFTER_START );
+
+			var style = new CKEDITOR.style( { element : 'b' , overrides : [ 'strong', 'bold' ] } );
+			style.removeFromRange( range );
+
+			// text <span><i>^</i></span><bold><span><b><i>styles</i></b></span></bold>
+			assert.areSame( 'text <span><i></i></span><strong><bold><span><b><i id="_i1">styles</i></b></span></bold></strong>', getInnerHtml( element ) );
+		},
+
+		// Test convert multiple paragraphs to one <pre>.
+		test_ticket_3188 : function()
+		{
+			var element = doc.getById( '_P1' );
+			element.setHtml( '<p id="_P2">\nparagraph1<br /><br />para\t\ngraph2</p><p id="_P3">\nparagraph3\n</p>' );
+
+			// <p id="_P2">[paragraph1</p><p id="_P3">paragraph2]</p>
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( '_P2' ), CKEDITOR.POSITION_AFTER_START );
+			range.setEndAt( doc.getById( '_P3' ), CKEDITOR.POSITION_BEFORE_END );
+
+			var style = new CKEDITOR.style( { element : 'pre' } );
+			style.applyToRange( range );
+
+			var result = getInnerHtmlParsed( element );
+			assert.areSame( '<pre>paragraph1\n\npara graph2\n\nparagraph3</pre>', result );
+		},
+
+		// Test convert one <pre> to multiple paragraphs.
+		test_ticket_3188_2 : function()
+		{
+			var element = doc.getById( '_P1' );
+			element.setHtml( '<pre>\n\tparagraph1\t\tparagraph1\nparagraph2\n\t\n\tpara   graph3\n</pre>' );
+
+			//<pre>[\n\tparagraph1\t\tparagraph1\nparagraph2\n\t\n\tpara   graph3\n]</pre>
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( doc.getById( '_P1' ).getFirst() );
+			var style = new CKEDITOR.style( { element : 'p' } );
+			style.applyToRange( range );
+
+			var result = getInnerHtmlParsed( element );
+
+			if ( CKEDITOR.env.webkit || CKEDITOR.env.opera )
+				result = result.replace( /\xa0/g, '&nbsp;' );
+
+			assert.areSame( '<p>&nbsp;paragraph1&nbsp; paragraph1<br />paragraph2</p><p>&nbsp;para&nbsp;&nbsp; graph3</p>',
+					 result );
+		},
+		name : document.title
+	};
+})() );
+//window.onload = testCase.test_ticket_3188;
+	//]]>
+	</script>
+</head>
+<body>
+	<div id="_P1"></div>
+</body>
+</html>
Index: /CKEditor/branches/tests/index.html
===================================================================
--- /CKEditor/branches/tests/index.html	(revision 4089)
+++ /CKEditor/branches/tests/index.html	(revision 4090)
@@ -8,6 +8,6 @@
 <h1>Test Runner Links</h1>
 <ul>
-	<li><a href="cktester/forge.html?profile=../profile.js&cells=editor">All tests of CKEditor</a></li>
-	<li><a href="cktester/forge.html?profile=../profile.js&cells=tt">All ticket tests of CKEditor</a></li>
+	<li><a href="cktester/fort.html?profile=../profile.js&cells=editor">All tests of CKEditor</a></li>
+	<li><a href="cktester/fort.html?profile=../profile.js&cells=tt">All ticket tests of CKEditor</a></li>
 </ul>
 </body>
