Index: /CKEditor/trunk/_test/dt/_assets/sample.css
===================================================================
--- /CKEditor/trunk/_test/dt/_assets/sample.css	(revision 4101)
+++ /CKEditor/trunk/_test/dt/_assets/sample.css	(revision 4101)
@@ -0,0 +1,4 @@
+﻿.sample
+{
+	position: absolute;
+}
Index: /CKEditor/trunk/_test/dt/_assets/sample.js
===================================================================
--- /CKEditor/trunk/_test/dt/_assets/sample.js	(revision 4101)
+++ /CKEditor/trunk/_test/dt/_assets/sample.js	(revision 4101)
@@ -0,0 +1,1 @@
+﻿var testVar = 'Test!';
Index: /CKEditor/trunk/_test/dt/_assets/sample.txt
===================================================================
--- /CKEditor/trunk/_test/dt/_assets/sample.txt	(revision 4101)
+++ /CKEditor/trunk/_test/dt/_assets/sample.txt	(revision 4101)
@@ -0,0 +1,1 @@
+Sample Text
Index: /CKEditor/trunk/_test/dt/_assets/sample.xml
===================================================================
--- /CKEditor/trunk/_test/dt/_assets/sample.xml	(revision 4101)
+++ /CKEditor/trunk/_test/dt/_assets/sample.xml	(revision 4101)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<list>
+	<item id="test1" />
+	<item id="test2" />
+</list>
Index: /CKEditor/trunk/_test/dt/_assets/sample_xml.txt
===================================================================
--- /CKEditor/trunk/_test/dt/_assets/sample_xml.txt	(revision 4101)
+++ /CKEditor/trunk/_test/dt/_assets/sample_xml.txt	(revision 4101)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<list>
+	<item id="test1" />
+	<item id="test2" />
+</list>
Index: /CKEditor/trunk/_test/dt/core/_editor/custom_config_1.js
===================================================================
--- /CKEditor/trunk/_test/dt/core/_editor/custom_config_1.js	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/_editor/custom_config_1.js	(revision 4101)
@@ -0,0 +1,10 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.editorConfig = function( config )
+{
+	config.customConfig = '_editor/custom_config_2.js';
+	config.test_custom1 = 'Ok';
+};
Index: /CKEditor/trunk/_test/dt/core/_editor/custom_config_2.js
===================================================================
--- /CKEditor/trunk/_test/dt/core/_editor/custom_config_2.js	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/_editor/custom_config_2.js	(revision 4101)
@@ -0,0 +1,9 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.editorConfig = function( config )
+{
+	config.test_custom2 = 'Ok';
+};
Index: /CKEditor/trunk/_test/dt/core/ajax.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/ajax.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/ajax.html	(revision 4101)
@@ -0,0 +1,161 @@
+<!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>CKEDITOR.ajax</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.loader.load( 'core/ajax' );
+
+	//]]>
+	</script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_load_sync : function()
+		{
+			var data = CKEDITOR.ajax.load( '../_assets/sample.txt' );
+			assert.areSame( 'Sample Text\r\n', data, 'The loaded data doesn\'t match' );
+		},
+
+		test_load_sync_404 : function()
+		{
+			var data = CKEDITOR.ajax.load( '../_assets/404.txt' );
+			assert.isNull( data );
+		},
+
+		test_load_async : function()
+		{
+			var testCase = this;
+			var callback = function( data )
+			{
+				testCase.resume( function()
+					{
+						assert.areSame( 'Sample Text\r\n', data, 'The loaded data doesn\'t match' );
+					} );
+			};
+
+			CKEDITOR.ajax.load( '../_assets/sample.txt', callback );
+			this.wait();
+		},
+
+		test_load_async_404 : function()
+		{
+			var testCase = this;
+			var callback = function( data )
+			{
+				testCase.resume( function()
+					{
+						assert.isNull( data );
+					} );
+			};
+
+			CKEDITOR.ajax.load( '../_assets/404.txt', callback );
+			this.wait();
+		},
+
+		test_loadXml_sync : function()
+		{
+			var data = CKEDITOR.ajax.loadXml( '../_assets/sample.xml' );
+			assert.isInstanceOf( CKEDITOR.xml, data );
+			assert.isNotNull( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (null)' );
+			assert.isNotUndefined( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (undefined)' );
+		},
+
+		test_loadXml_sync_404 : function()
+		{
+			var data = CKEDITOR.ajax.loadXml( '../_assets/404.xml' );
+			assert.isNull( data );
+		},
+
+		test_loadXml_async : function()
+		{
+			var testCase = this;
+			var callback = function( data )
+			{
+				testCase.resume( function()
+					{
+						assert.isInstanceOf( CKEDITOR.xml, data );
+						assert.isNotNull( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (null)' );
+						assert.isNotUndefined( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (undefined)' );
+					} );
+			};
+
+			CKEDITOR.ajax.loadXml( '../_assets/sample.xml', callback );
+			this.wait();
+		},
+
+		test_loadXml_async_404 : function()
+		{
+			var testCase = this;
+			var callback = function( data )
+			{
+				testCase.resume( function()
+					{
+						assert.isNull( data );
+					} );
+			};
+
+			CKEDITOR.ajax.loadXml( '../_assets/404.xml', callback );
+			this.wait();
+		},
+
+		test_loadXml_sync_txt : function()
+		{
+			var data = CKEDITOR.ajax.loadXml( '../_assets/sample_xml.txt' );
+			assert.isInstanceOf( CKEDITOR.xml, data );
+			assert.isNotNull( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (null)' );
+			assert.isNotUndefined( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (undefined)' );
+		},
+
+		test_loadXml_async_txt : function()
+		{
+			var testCase = this;
+			var callback = function( data )
+			{
+				testCase.resume( function()
+					{
+						assert.isInstanceOf( CKEDITOR.xml, data );
+						assert.isNotNull( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (null)' );
+						assert.isNotUndefined( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (undefined)' );
+					} );
+			};
+
+			CKEDITOR.ajax.loadXml( '../_assets/sample_xml.txt', callback );
+			this.wait();
+		},
+
+		test_parseXml : function()
+		{
+			var data = new CKEDITOR.xml( '<?xml version="1.0" encoding="utf-8" ?><list><item id="test1" /><item id="test2" /></list>' );
+			assert.isInstanceOf( CKEDITOR.xml, data );
+			assert.isNotNull( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (null)' );
+			assert.isNotUndefined( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (undefined)' );
+		},
+
+		test_parseXml_nbsp : function()
+		{
+			var data = new CKEDITOR.xml( '<?xml version="1.0" encoding="utf-8" ?><list><item id="test1">&nbsp;</item><item id="test2" /></list>' );
+			assert.isInstanceOf( CKEDITOR.xml, data );
+			assert.isNotNull( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (null)' );
+			assert.isNotUndefined( data.selectSingleNode( '//list/item' ), 'The loaded data doesn\'t match (undefined)' );
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/bootstrap.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/bootstrap.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/bootstrap.html	(revision 4101)
@@ -0,0 +1,65 @@
+﻿<!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>CKEDITOR.bootstrap</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+var before_loadFullCore = CKEDITOR.status;
+
+var isLoaded;
+
+CKEDITOR.on( 'loaded', function()
+	{
+		isLoaded = true;
+	});
+
+
+CKEDITOR.loadFullCore();
+
+	//]]>
+	</script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_status1 : function()
+		{
+			assert.areSame( 'basic_loaded', before_loadFullCore );
+		},
+
+		test_status2 : function()
+		{
+			// In IE, CKEDITOR.on( 'loaded' ) will be called immediately if the scripts are cached.
+			if ( isLoaded )
+				return;
+
+			var testCase = this;
+			CKEDITOR.on( 'loaded', function()
+				{
+					testCase.resume( function()
+						{
+							assert.areSame( 'loaded', CKEDITOR.status );
+						});
+				});
+
+			this.wait();
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/ckeditor.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/ckeditor.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/ckeditor.html	(revision 4101)
@@ -0,0 +1,159 @@
+﻿<!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>CKEDITOR</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+	// Clean up all instances been created on the page.
+	function removeAllInstances()
+	{
+		var allInstances = CKEDITOR.instances;
+		for( var i in allInstances )
+		{
+			CKEDITOR.remove(  allInstances[ i ] );
+		}
+	}
+
+	CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_replaceClass : function()
+		{
+			// Asserts the default value.
+			assert.areSame( 'ckeditor', CKEDITOR.replaceClass );
+		},
+
+		test_replaceByClassEnabled : function()
+		{
+			// Asserts the default value.
+			assert.isTrue( CKEDITOR.replaceByClassEnabled );
+		},
+
+		test_replaceId : function()
+		{
+			CKEDITOR.replace( 'editor1' );
+			assert.isObject( CKEDITOR.instances.editor1, 'editor instance not found' );
+			assert.areSame( 'editor1', CKEDITOR.instances.editor1.name, 'instance name doesn\'t match' );
+			assert.areSame( document.getElementById( 'editor1' ), CKEDITOR.instances.editor1.element.$, 'instance element doesn\'t match' );
+		},
+
+		test_replaceName : function()
+		{
+			CKEDITOR.replace( 'editor2' );
+			assert.isObject( CKEDITOR.instances.editor2, 'editor instance not found' );
+			assert.areSame( 'editor2', CKEDITOR.instances.editor2.name, 'instance name doesn\'t match' );
+			assert.areSame( document.getElementsByName( 'editor2' )[0], CKEDITOR.instances.editor2.element.$, 'instance element doesn\'t match' );
+		},
+
+		test_replaceElement : function()
+		{
+			CKEDITOR.replace( document.getElementById( 'editor5' ) );
+			assert.isObject( CKEDITOR.instances.editor5, 'editor instance not found' );
+			assert.areSame( 'editor5', CKEDITOR.instances.editor5.name, 'instance name doesn\'t match' );
+			assert.areSame( document.getElementById( 'editor5' ), CKEDITOR.instances.editor5.element.$, 'instance element doesn\'t match' );
+		},
+
+
+		test_replaceError : function()
+		{
+			try
+			{
+				CKEDITOR.replace( 'error' );
+			}
+			catch ( e )
+			{
+				assert.areSame( '[CKEDITOR.editor.replace] The element with id or name "error" was not found.', e );
+			}
+		},
+
+		test_replaceAll_Class : function()
+		{
+			CKEDITOR.replaceAll( 'myclass' );
+
+			assert.isObject( CKEDITOR.instances.editor3, 'editor3 instance not found' );
+			assert.areSame( 'editor3', CKEDITOR.instances.editor3.name, 'editor3 instance name doesn\'t match' );
+			assert.areSame( document.getElementById( 'editor3' ), CKEDITOR.instances.editor3.element.$, 'editor3 instance element doesn\'t match' );
+
+			assert.isObject( CKEDITOR.instances.editor4, 'editor4 instance not found' );
+			assert.areSame( 'editor4', CKEDITOR.instances.editor4.name, 'editor4 instance name doesn\'t match' );
+			assert.areSame( document.getElementsByName( 'editor4' )[0], CKEDITOR.instances.editor4.element.$, 'editor4 instance element doesn\'t match' );
+
+			assert.isUndefined( CKEDITOR.instances.editor6, 'editor6 should be undefined' );
+			assert.isUndefined( CKEDITOR.instances.editor7, 'editor7 should be undefined' );
+			assert.isUndefined( CKEDITOR.instances.editor8, 'editor8 should be undefined' );
+			assert.isUndefined( CKEDITOR.instances.editor8, 'editor9 should be undefined' );
+		},
+
+		test_replaceAll_Function : function()
+		{
+			CKEDITOR.replaceAll( function( textarea )
+				{
+					return ( textarea.id != 'editor6' && textarea.id != 'editor8' );
+				} );
+
+			assert.isObject( CKEDITOR.instances.editor7, 'editor7 instance not found' );
+			assert.areSame( 'editor7', CKEDITOR.instances.editor7.name, 'editor7 instance name doesn\'t match' );
+			assert.areSame( document.getElementById( 'editor7' ), CKEDITOR.instances.editor7.element.$, 'editor7 instance element doesn\'t match' );
+
+			assert.isObject( CKEDITOR.instances.editor9, 'editor9 instance not found' );
+			assert.areSame( 'editor9', CKEDITOR.instances.editor9.name, 'editor9 instance name doesn\'t match' );
+			assert.areSame( document.getElementById( 'editor9' ), CKEDITOR.instances.editor9.element.$, 'editor9 instance element doesn\'t match' );
+
+			assert.isUndefined( CKEDITOR.instances.editor6, 'editor6 should be undefined' );
+			assert.isUndefined( CKEDITOR.instances.editor8, 'editor8 should be undefined' );
+		},
+
+		test_replaceAll : function()
+		{
+			CKEDITOR.replaceAll();
+
+			assert.isObject( CKEDITOR.instances.editor6, 'editor6 instance not found' );
+			assert.areSame( 'editor6', CKEDITOR.instances.editor6.name, 'editor6 instance name doesn\'t match' );
+			assert.areSame( document.getElementById( 'editor6' ), CKEDITOR.instances.editor6.element.$, 'editor6 instance element doesn\'t match' );
+
+			assert.isObject( CKEDITOR.instances.editor8, 'editor8 editor instance not found' );
+			assert.areSame( 'editor8', CKEDITOR.instances.editor8.name, 'editor8 instance name doesn\'t match' );
+			assert.areSame( document.getElementById( 'editor8' ), CKEDITOR.instances.editor8.element.$, 'editor8 instance element doesn\'t match' );
+		},
+
+		test_add : function()
+		{
+			var textarea = CKEDITOR.dom.element.createFromHtml( '<textarea id="test_add">&lt;p&gt;test_add&lt;/p&gt;<\/textarea>' );
+			textarea.appendTo( new CKEDITOR.dom.element( document.body ) );
+
+			var editor = new CKEDITOR.editor( {}, textarea, CKEDITOR.ELEMENT_MODE_REPLACE );
+
+			CKEDITOR.add( editor );
+
+			assert.areSame( editor, CKEDITOR.instances.test_add );
+		},
+
+		setUp : function()
+		{
+			removeAllInstances();
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+	<textarea id="editor1" cols="80" rows="10"></textarea>
+	<textarea name="editor2" cols="80" rows="10"></textarea>
+	<textarea id="editor3" name="editor3" class="myclass" cols="80" rows="10"></textarea>
+	<textarea name="editor4" class="myclass" cols="80" rows="10"></textarea>
+	<textarea id="editor5" cols="80" rows="10"></textarea>
+	<textarea id="editor6" cols="80" rows="10"></textarea>
+	<textarea id="editor7" cols="80" rows="10"></textarea>
+	<textarea id="editor8" cols="80" rows="10"></textarea>
+	<textarea id="editor9" cols="80" rows="10"></textarea>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/dom/document.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/dom/document.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/dom/document.html	(revision 4101)
@@ -0,0 +1,126 @@
+﻿<!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>CKEDITOR.dom.document</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert,
+		arrayAssert = YAHOO.util.ArrayAssert;
+
+	return {
+		test_$ : function()
+		{
+			var doc = new CKEDITOR.dom.document( document );
+			assert.areSame( document, doc.$ );
+		},
+
+		test_appendStyleSheet : function()
+		{
+			var cssUrl = CKEDITOR.basePath + '_tests/';
+			cssUrl = CKEDITOR.basePath + '_source/tests/';	// %REMOVE_LINE%
+			cssUrl += '_assets/sample.css';
+
+			var doc = new CKEDITOR.dom.document( document );
+			doc.appendStyleSheet( cssUrl );
+
+			var links = document.getElementsByTagName( 'link' );
+
+			for ( var i = 0 ; i < links.length ; i++ )
+			{
+				if ( links[i].href == cssUrl )
+					return ;
+			}
+
+			assert.fail( 'The link element was not found' );
+		},
+
+		test_equals1 : function()
+		{
+			// Actually checks the real need for equals().
+			var doc1 = new CKEDITOR.dom.document( document );
+			assert.isFalse( doc1 == document );
+		},
+
+		test_equals2 : function()
+		{
+			// Actually checks the real need for equals().
+			var doc1 = new CKEDITOR.dom.document( document );
+			var doc2 = new CKEDITOR.dom.document( document );
+			assert.isFalse( doc1 == doc2, 'doc1 == doc2' );
+		},
+
+		test_equals3 : function()
+		{
+			var doc1 = new CKEDITOR.dom.document( document );
+			var doc2 = new CKEDITOR.dom.document( document );
+			assert.isTrue( doc1.equals( doc2 ), 'doc1.equals( doc2 )' );
+			assert.isTrue( doc2.equals( doc1 ), 'doc2.equals( doc1 )' );
+		},
+
+		test_getById1 : function()
+		{
+			var doc = new CKEDITOR.dom.document( document );
+			var element = doc.getById( 'test1' );
+			assert.areSame( document.getElementById( 'test1' ), element.$ );
+		},
+
+		test_getById2 : function()
+		{
+			var doc = new CKEDITOR.dom.document( document );
+			var element = doc.getById( 'test_invalid' );
+			assert.isNull( element );
+		},
+
+		test_getHead : function()
+		{
+			var doc = new CKEDITOR.dom.document( document );
+			assert.areSame( document.getElementsByTagName( 'head' )[0], doc.getHead().$ );
+		},
+
+		test_getBody : function()
+		{
+			var doc = new CKEDITOR.dom.document( document );
+			assert.areSame( document.body, doc.getBody().$, '1st call failed' );
+			assert.areSame( document.body, doc.getBody().$, '2nd call failed' );
+		},
+		test_createText : function()
+		{
+			var doc = new CKEDITOR.dom.document( document ), contentText = 'text content';
+			var textNode = doc.createText( contentText );
+			assert.areSame( contentText, textNode.getText(),
+				'Create text node content doesn\'t match.' );
+		},
+
+		test_getByAddress1 : function()
+		{
+			var doc = new CKEDITOR.dom.document( document );
+			var node = doc.getByAddress( [ 1, 1, 0, 1, 0, 0 ] );
+			assert.areSame( 'target', node.getText(),
+				'Addressing target doesn\'t match.' );
+		},
+
+		test_getElementsByTag : function()
+		{
+			var nodeList = new CKEDITOR.dom.document( document ).getElementsByTag( 'span' ),
+				results = [];
+			for( var i = 0; i < nodeList.count(); i++ ){
+				results.push( nodeList.getItem( i ).$ );
+			}
+			arrayAssert.itemsAreEqual( results, document.getElementsByTagName( 'span' ) );
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body><div id="test1"></div><div><p>text<span><b id="addressTarget1">target</b>text</span>text</p></div><span ><b id="addressTarget2">target</b></span></body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/dom/documentfragment.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/dom/documentfragment.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/dom/documentfragment.html	(revision 4101)
@@ -0,0 +1,291 @@
+<!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>CKEDITOR.dom.documentFragment</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+var tc;
+CKEDITOR.test.addTestCase( tc = ( function()
+{
+	// Local reference to the "assert" object.
+	var assert = YAHOO.util.Assert,
+		doc = CKEDITOR.document;
+
+	return {
+
+		test_appendTo : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner =
+					new CKEDITOR.dom.element( 'b' );
+			frag.append( inner );
+			var container = CKEDITOR.document.getById( 'fragmentContainer1' );
+			frag.appendTo( container );
+			assert.isTrue( container.getLast().equals( inner ) );
+		},
+
+		test_append : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner =
+					new CKEDITOR.dom.element( 'b' );
+			frag.append( inner );
+			var container = CKEDITOR.document.getById( 'fragmentContainer3' );
+			container.append( frag );
+			assert.isTrue( container.getLast().equals( inner ) );
+		},
+
+		test_getFirst : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner =
+					new CKEDITOR.dom.element( 'b' );
+			frag.append( inner );
+			var first = frag.getFirst();
+			assert.isTrue( first.equals( inner ) );
+		},
+
+		test_getLast : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner =
+					new CKEDITOR.dom.element( 'b' ), lastInner = inner.clone();
+			frag.append( inner );
+			frag.append( lastInner );
+			var last = frag.getLast();
+			assert.isTrue( last.equals( lastInner ) );
+		},
+
+		test_moveChildren : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner1 = new CKEDITOR.dom.element( 'b' ),
+			inner2 = new CKEDITOR.dom.element( 'i' );
+			frag.append( inner1 );
+			frag.append( inner2 );
+
+			// Move to element
+			var element = new CKEDITOR.dom.element( 'span' );
+			frag.moveChildren( element, true );
+			assert.isTrue( element.getFirst().equals( inner1 ) );
+			assert.isTrue( element.getLast().equals( inner2 ) );
+		},
+
+		test_moveChildren2 : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner1 = new CKEDITOR.dom.element( 'b' ),
+				inner2 = new CKEDITOR.dom.element( 'i' );
+			frag.append( inner1 );
+			frag.append( inner2 );
+
+			// Move to fragment
+			var anotherFrag = new CKEDITOR.dom.documentFragment(
+				CKEDITOR.document );
+			frag.moveChildren( anotherFrag, true );
+			assert.isTrue( anotherFrag.getFirst().equals( inner1 ) );
+			assert.isTrue( anotherFrag.getLast().equals( inner2 ) );
+		},
+
+		test_appendText : function()
+		{
+			var element = new CKEDITOR.dom.element( 'script' );
+			element.appendText( 'Test appendText' );
+			assert.areEqual( 'Test appendText', element.$.text );
+		},
+
+		test_ltrim : function()
+		{
+			// IE need special treatement to setup this tc, see
+			// next tc for this.
+			if ( CKEDITOR.env.ie )
+				return;
+			var element = CKEDITOR.dom.element.createFromHtml(
+				'<div id="trimTarget">\t\ntext\t\n</div>'
+			);
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			element.moveChildren( frag );
+			frag.ltrim();
+			var ct = new CKEDITOR.dom.element( 'div' );
+			ct.append( frag );
+			assert.areSame( 'text\t\n', ct.getText() );
+		},
+
+		test_ltrim_ie : function()
+		{
+			if ( !CKEDITOR.env.ie )
+				return;
+			// IE dom operation will trim preceding empty text,
+			// here we use 'splitText' way to create leading spaces
+			// instead for testing.
+			var emptyTextNode = doc.createText( '@ \ttext\t ');
+			var frag =
+					new CKEDITOR.dom.documentFragment( doc );
+			frag.append( emptyTextNode );
+			frag.getFirst().$.splitText( 1 );
+			frag.getFirst().remove();
+			frag.ltrim();
+			var ct = new CKEDITOR.dom.element( 'div' );
+			ct.append( frag );
+			assert.areSame( 'text\t ', ct.getText() );
+		},
+
+		test_rtrim : function()
+		{
+			if ( CKEDITOR.env.ie )
+				return;
+			var element = CKEDITOR.dom.element.createFromHtml(
+				'<div id="trimTarget">\t\ntext\t\n</div>'
+			);
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			element.moveChildren( frag );
+			frag.rtrim();
+			var ct = new CKEDITOR.dom.element( 'div' );
+			ct.append( frag );
+
+			assert.areSame( '\t\ntext', ct.getText() );
+		},
+
+		/**
+		 *@see test_ltrim_ie
+		 */
+		test_rtrim_ie : function()
+		{
+			if ( !CKEDITOR.env.ie )
+				return;
+			var emptyTextNode = doc.createText( '@ \ttext\t ');
+			var frag =
+					new CKEDITOR.dom.documentFragment( doc );
+			frag.append( emptyTextNode );
+			frag.getFirst().$.splitText( 1 );
+			frag.getFirst().remove();
+			frag.rtrim();
+			var ct = new CKEDITOR.dom.element( 'div' );
+			ct.append( frag );
+			assert.areSame( ' \ttext', ct.getText() );
+		},
+
+
+		test_trim : function()
+		{
+			if ( !CKEDITOR.env.ie )
+				return;
+			var element = CKEDITOR.dom.element.createFromHtml(
+				'<div id="trimTarget">\t\ntext\t\n</div>'
+			);
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			element.moveChildren( frag );
+			frag.trim();
+			var ct = new CKEDITOR.dom.element( 'div' );
+			ct.append( frag );
+			assert.areSame( 'text', ct.getText() );
+		},
+
+
+		/**
+		 *@see test_ltrim_ie
+		 */
+		test_trim_ie : function()
+		{
+			if ( !CKEDITOR.env.ie )
+				return;
+			var emptyTextNode = doc.createText( '@ \t\ntext\t\n ');
+			var frag =
+					new CKEDITOR.dom.documentFragment( doc );
+			frag.append( emptyTextNode );
+			frag.getFirst().$.splitText( 1 );
+			frag.getFirst().remove();
+			frag.trim();
+			var ct = new CKEDITOR.dom.element( 'div' );
+			ct.append( frag );
+			assert.areSame( 'text', ct.getText() );
+		},
+
+		test_insertAfter : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner = new CKEDITOR.dom.element( 'b' );
+			frag.append( inner );
+			var container = CKEDITOR.document.getById( 'fragmentContainer2' );
+			var sibling = CKEDITOR.document.getById( 'fragmentSibling1' );
+			frag.insertAfterNode( sibling );
+			assert.isTrue( container.getLast().equals( inner ) );
+		},
+
+		test_getChildCount : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner1 = new CKEDITOR.dom.element( 'b' ),
+				inner2 = new CKEDITOR.dom.element( 'i' );
+			frag.append( inner1 );
+			frag.append( inner2 );
+
+			assert.areEqual( 2, frag.getChildCount() );
+		},
+
+		test_getChild : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner1 = new CKEDITOR.dom.element( 'b' ),
+				inner2 = new CKEDITOR.dom.element( 'i' );
+			frag.append( inner1 );
+			frag.append( inner2 );
+
+			assert.isTrue( inner2.equals( frag.getChild( 1 ) ) );
+		},
+
+		test_getChildren : function()
+		{
+			var frag =
+					new CKEDITOR.dom.documentFragment( CKEDITOR.document );
+			var inner1 = new CKEDITOR.dom.element( 'b' ),
+				inner2 = new CKEDITOR.dom.element( 'i' );
+			frag.append( inner1 );
+			frag.append( inner2 );
+
+			var childNodesList = frag.getChildren();
+			assert.areEqual( 2, childNodesList.count() );
+			assert.isTrue( inner2.equals( childNodesList.getItem( 1 ) ) );
+		},
+
+		test_getDocument : function()
+		{
+			var doc = CKEDITOR.document,
+				innerDoc = new CKEDITOR.dom.document(
+					doc.getById( 'innerFrame' ).$.contentWindow.document );
+			var frag1 = new CKEDITOR.dom.documentFragment( doc ),
+				frag2 = new CKEDITOR.dom.documentFragment( innerDoc );
+
+			assert.isTrue( doc.equals( frag1.getDocument() ) );
+			assert.isTrue( innerDoc.equals( frag2.getDocument() ) );
+		},
+
+		name :document.title
+	};
+} )() );
+
+//window.onload = tc.test_rtrim_ie;
+	//]]>
+	</script>
+</head>
+<body>
+	<div id="fragmentContainer1"></div>
+	<div id="fragmentContainer2"><div id="fragmentSibling1"></div></div>
+	<div id="fragmentContainer3"></div>
+	<iframe id="innerFrame" name="innerFrame" src="about:blank"></iframe>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/dom/element.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/dom/element.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/dom/element.html	(revision 4101)
@@ -0,0 +1,636 @@
+﻿<!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>CKEDITOR.dom.element</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+	var getInnerHtml = CKEDITOR.test.getInnerHtml;
+
+	return {
+		test_$ : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			assert.areSame( document.getElementById( 'test1' ), element.$ );
+		},
+
+		test_getId1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			assert.areEqual( 'test1', element.getId() );
+		},
+
+		test_getId2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementsByTagName( 'div' )[0] );
+			assert.isNull( element.getId() );
+		},
+
+		test_getNameAtt1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementsByName( 'named1' )[0] );
+			assert.areEqual( 'named1', element.getNameAtt() );
+		},
+
+		test_getNameAtt2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			assert.isNull( element.getNameAtt() );
+		},
+
+		test_getName : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			assert.areEqual( 'textarea', element.getName(), 'First call' );
+			assert.areEqual( 'textarea', element.getName(), 'Second call' );
+		},
+
+		test_hide : function()
+		{
+			var nativeElement = document.getElementById( 'test1' );
+			nativeElement.style.display = '';
+
+			var element = new CKEDITOR.dom.element( nativeElement );
+			element.hide();
+
+			assert.areEqual( 'none', nativeElement.style.display );
+		},
+
+		test_show : function()
+		{
+			var nativeElement = document.getElementById( 'test1' );
+			nativeElement.style.display = '';
+
+			var element = new CKEDITOR.dom.element( nativeElement );
+			element.show();
+
+			assert.areEqual( '', nativeElement.style.display );
+		},
+
+		test_createFromHtml : function()
+		{
+			var element = CKEDITOR.dom.element.createFromHtml( '<p>My test</p>' );
+			assert.areEqual( 'p', element.getName(), 'element name doesn\'t match' );
+			assert.areEqual( 'My test', element.$.innerHTML, 'the innerHTML doesn\'t match' );
+		},
+
+		test_createFromHtml2 : function()
+		{
+			var element = new CKEDITOR.dom.element.createFromHtml( '<div><x:x><p>Test</p></div>' );
+			assert.areNotEqual( 'Test', element.getChild( 0, 0 ).innerHTML );
+		},
+
+		test_append1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			element.append( new CKEDITOR.dom.element( 'b' ) );
+			assert.areEqual( 'b', document.getElementById( 'append' ).firstChild.nodeName.toLowerCase() );
+		},
+
+		test_append2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			element.append( 'i' );
+			assert.areEqual( 'i', document.getElementById( 'append' ).lastChild.nodeName.toLowerCase() );
+		},
+
+		test_appendText1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			element.setHtml( '' );
+			element.appendText( 'Test appendText' );
+			assert.areEqual( 'Test appendText', document.getElementById( 'append' ).innerHTML );
+		},
+
+		test_appendText2 : function()
+		{
+			var element = new CKEDITOR.dom.element( 'script' );
+			element.appendText( 'Test appendText' );
+			assert.areEqual( 'Test appendText', element.$.text );
+		},
+
+		test_setHtml : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			element.setHtml( '<b>Test</b>' );
+			assert.areEqual( '<b>test</b>', document.getElementById( 'append' ).innerHTML.toLowerCase(), 'First call doesn\'t match' );
+			element.setHtml( 'Another test' );
+			assert.areEqual( 'Another test', document.getElementById( 'append' ).innerHTML, 'Second call doesn\'t match' );
+		},
+
+		test_getDocument1 : function()
+		{
+			var element = new CKEDITOR.dom.element( 'div' );
+			assert.areSame( document, element.getDocument().$, 'First call' );
+			assert.areSame( document, element.getDocument().$, 'Second call' );
+		},
+
+		test_getDocument2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.body );
+			assert.areSame( document, element.getDocument().$, 'First call' );
+			assert.areSame( document, element.getDocument().$, 'Second call' );
+		},
+
+		test_getFirst1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			element.setHtml( '<b>Test</b>' );
+			var first = element.getFirst();
+			assert.areSame( 'b', first.getName() );
+		},
+
+		test_getFirst2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			element.setHtml( 'Test' );
+			var first = element.getFirst();
+			assert.areSame( 'Test', first.$.nodeValue );
+		},
+
+		test_getFirst3 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			element.setHtml( '' );
+			var first = element.getFirst();
+			assert.isNull( first );
+		},
+
+		test_setAttribute1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			element.setAttribute( 'class', 'Test' );
+
+			assert.areSame( 'Test', document.getElementById( 'test1' ).className );
+		},
+
+		test_setAttribute2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			element.setAttribute( 'style', 'position: absolute;' );
+
+			assert.areEqual( 'position: absolute;', CKEDITOR.test.getAttribute( element, 'style' ) );
+		},
+
+		test_setAttribute3 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			element.setAttribute( 'name', 'TestName' );
+
+			assert.areEqual( 'TestName', document.getElementById( 'test1' ).name );
+		},
+
+		test_setAttribute4 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			element.setAttribute( 'rows', 5 );
+
+			assert.areEqual( 5, document.getElementById( 'test1' ).rows );
+		},
+
+		test_setAttributes : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'test2' ) );
+			element.setAttributes({
+				'class'	: 'Test',
+				'style'	: 'position: absolute;',
+				'name'	: 'TestName',
+				'rows'	: 5 });
+
+			assert.areSame( 'Test', document.getElementById( 'test1' ).className, 'class is wrong' );
+			assert.areEqual( 'position: absolute;', CKEDITOR.test.getAttribute( element, 'style'), 'style is wrong' );
+			assert.areEqual( 'TestName', document.getElementById( 'test1' ).name, 'name is wrong' );
+			assert.areEqual( 5, document.getElementById( 'test1' ).rows, 'rows is wrong' );
+		},
+
+		test_setStyle1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'setStyle' ) );
+			element.setStyle( 'position', 'absolute' );
+
+			assert.areEqual( 'absolute', document.getElementById( 'setStyle' ).style.position );
+		},
+
+		test_setStyle2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'setStyle' ) );
+			element.setStyle( 'float', 'right' );
+
+			assert.areEqual( 'right', document.getElementById( 'setStyle' ).style.cssFloat );
+		},
+
+		test_setStyles : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'setStyle' ) );
+			element.setStyles({
+					'position' : 'absolute',
+					'float' : 'right'
+				});
+
+			assert.areEqual( 'absolute', document.getElementById( 'setStyle' ).style.position );
+			assert.areEqual( 'right', document.getElementById( 'setStyle' ).style.cssFloat );
+		},
+
+		test_setText1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'setText' ) );
+			element.setText( 'A & B' );
+			assert.areSame( 'A &amp; B', document.getElementById( 'setText' ).innerHTML );
+		},
+
+		test_setText2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'setText' ) );
+			element.setText( 'C & D' );
+			assert.areSame( 'C &amp; D', document.getElementById( 'setText' ).innerHTML );
+		},
+
+		test_addClass1 : function()
+		{
+			var element = new CKEDITOR.dom.element( 'div' );
+			element.addClass( 'classA' );
+			assert.areSame( 'classA', element.$.className );
+		},
+
+		test_addClass2 : function()
+		{
+			var element = new CKEDITOR.dom.element( 'div' );
+			element.addClass( 'classA' );
+			element.addClass( 'classA' );
+			assert.areSame( 'classA', element.$.className );
+		},
+
+		test_addClass3 : function()
+		{
+			var element = new CKEDITOR.dom.element( 'div' );
+			element.addClass( 'classA' );
+			element.addClass( 'classB' );
+			element.addClass( 'classC' );
+			assert.areSame( 'classA classB classC', element.$.className );
+		},
+
+		test_addClass4 : function()
+		{
+			var element = new CKEDITOR.dom.element( 'div' );
+			element.addClass( 'classA' );
+			element.addClass( 'classB' );
+			element.addClass( 'classC' );
+			element.addClass( 'classA' );
+			element.addClass( 'classB' );
+			element.addClass( 'classC' );
+			assert.areSame( 'classA classB classC', element.$.className );
+		},
+
+		test_removeClass1 : function()
+		{
+			document.getElementById( 'removeClass' ).innerHTML = '';
+
+			var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA"></div>' );
+			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
+
+			assert.areSame( '<div class="classa"></div>', getInnerHtml( 'removeClass' ) );
+			element.removeClass( 'classA' );
+			assert.areSame( '<div></div>', getInnerHtml( 'removeClass' ) );
+		},
+
+		test_removeClass2 : function()
+		{
+			document.getElementById( 'removeClass' ).innerHTML = '';
+
+			var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA classB classC classD"></div>' );
+			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
+
+			assert.areSame( '<div class="classa classb classc classd"></div>', getInnerHtml( 'removeClass' ) );
+			element.removeClass( 'classA' );
+			assert.areSame( '<div class="classb classc classd"></div>', getInnerHtml( 'removeClass' ) );
+			element.removeClass( 'classC' );
+			assert.areSame( '<div class="classb classd"></div>', getInnerHtml( 'removeClass' ) );
+			element.removeClass( 'classD' );
+			assert.areSame( '<div class="classb"></div>', getInnerHtml( 'removeClass' ) );
+			element.removeClass( 'classB' );
+			assert.areSame( '<div></div>', getInnerHtml( 'removeClass' ) );
+		},
+
+		test_removeClass3 : function()
+		{
+			document.getElementById( 'removeClass' ).innerHTML = '';
+
+			var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA classB"></div>' );
+			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
+
+			assert.areSame( '<div class="classa classb"></div>', getInnerHtml( 'removeClass' ) );
+			element.removeClass( 'classXXX' );
+			assert.areSame( '<div class="classa classb"></div>', getInnerHtml( 'removeClass' ) );
+			element.removeClass( 'classB' );
+			assert.areSame( '<div class="classa"></div>', getInnerHtml( 'removeClass' ) );
+			element.removeClass( 'classYYY' );
+			assert.areSame( '<div class="classa"></div>', getInnerHtml( 'removeClass' ) );
+		},
+
+		test_removeAttribute1 : function()
+		{
+			document.getElementById( 'removeClass' ).innerHTML = '';
+
+			var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA"></div>' );
+			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
+
+			element.removeAttribute( 'class' );
+			assert.areSame( '<div></div>', getInnerHtml( 'removeClass' ) );
+		},
+
+		test_removeAttribute2 : function()
+		{
+			document.getElementById( 'removeClass' ).innerHTML = '';
+
+			var element = CKEDITOR.dom.element.createFromHtml( '<div style="position:absolute"></div>' );
+			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
+
+			element.removeAttribute( 'style' );
+			assert.areSame( '<div></div>', getInnerHtml( 'removeClass' ) );
+		},
+
+		test_removeAttribute3 : function()
+		{
+			document.getElementById( 'removeClass' ).innerHTML = '';
+
+			var element = CKEDITOR.dom.element.createFromHtml( '<div title="Test"></div>' );
+			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
+
+			element.removeAttribute( 'title' );
+			assert.areSame( '<div></div>', getInnerHtml( 'removeClass' ) );
+		},
+
+		test_remove : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'removeInner' ) );
+			element.remove();
+
+			assert.areSame( '', getInnerHtml( 'removeOuter' ) );
+		},
+
+		test_getAttribute_tabindex1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndex10' ) );
+			assert.areEqual( 10, CKEDITOR.test.getAttribute( element, 'tabindex' ) );
+		},
+
+		test_getAttribute_tabindex2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexDef' ) );
+			assert.isNull( CKEDITOR.test.getAttribute( element, 'tabindex' ) );
+		},
+
+		test_getAttribute_tabindex3 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexInputDef' ) );
+			assert.isNull( CKEDITOR.test.getAttribute( element, 'tabindex' ) );
+		},
+
+		test_getAttribute_tabindex4 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexInput20' ) );
+			assert.areEqual( 20, CKEDITOR.test.getAttribute( element, 'tabindex' ) );
+		},
+
+		test_getAttribute_tabindex5 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexScriptDef' ) );
+			assert.areEqual( null, CKEDITOR.test.getAttribute( element, 'tabindex' ) );
+		},
+
+		/**
+		 *  Test getAttribute and getAttribute will ingore  '_cke_expando' attribute.
+		 */
+		test_getAttribute_ignoreExpandoAttributes : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'testExpandoAttributes' ) );
+
+			assert.areEqual( 1, CKEDITOR.test.getAttribute( element, 'tabindex' ), 'tabindex is wrong' );
+			element.removeAttribute( 'tabindex' );
+
+			assert.areEqual( 'hasExpando', CKEDITOR.test.getAttribute( element, 'class' ), 'class is wrong' );
+			element.removeAttribute( 'class' );
+
+			assert.areEqual( 'float: right;', CKEDITOR.test.getAttribute( element, 'style' ) );
+
+			element.removeAttribute( 'style' );
+			element.removeAttribute( 'id' );
+			assert.isFalse( element.hasAttributes(), 'hasAttributes should be false' );
+		},
+
+		test_getTabIndex1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndex10' ) );
+			assert.areSame( 10, element.getTabIndex() );
+		},
+
+		test_getTabIndex2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexDef' ) );
+			assert.areSame( -1, element.getTabIndex() );
+		},
+
+		test_getTabIndex3 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexInputDef' ) );
+			assert.areSame( 0, element.getTabIndex() );
+		},
+
+		test_getTabIndex4 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexInput20' ) );
+			assert.areSame( 20, element.getTabIndex() );
+		},
+
+		test_getTabIndex5 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexScriptDef' ) );
+			assert.areSame( -1, element.getTabIndex() );
+		},
+
+		test_private1 : function()
+		{
+			var a = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			var b = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+
+			assert.areSame( a.getPrivate(), b.getPrivate() );
+		},
+
+		test_private2 : function()
+		{
+			var a = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
+			var b = new CKEDITOR.dom.element( document.getElementById( 'test2' ) );
+
+			assert.areNotSame( a.getPrivate(), b.getPrivate() );
+		},
+
+		test_getText1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'getText' ) );
+
+			// IE gives us a different result, which is ok for us (see code comments).
+			if ( CKEDITOR.env.ie )
+				assert.areSame( 'Some \nsample text for testing', element.getText().replace( /\r\n|\r/g, '\n' ) );
+			else
+				assert.areSame( 'Some  sample text    for    testing', element.getText() );
+		},
+
+		test_getText2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'getText2' ).getElementsByTagName( 'b' )[0] );
+			assert.areSame( ' ', element.getText() );
+		},
+
+		test_getText3 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'getText3' ) );
+
+			// IE gives us a different result, which is ok for us (see code comments).
+			if ( CKEDITOR.env.ie )
+				assert.areSame( 'A B', element.getText() );
+			else
+				assert.areSame( 'A\nB', element.getText().replace( /\r\n|\r/g, '\n' ) );
+		},
+
+		test_hasAttributes1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementsByTagName( 'big' )[0] );
+			assert.isFalse( element.hasAttributes() );
+		},
+
+		test_hasAttributes2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementsByTagName( 'small' )[0] );
+			assert.isTrue( element.hasAttributes() );
+		},
+
+		/**
+		 * Test detecting of 'class' attribute in IE6/7.
+		 */
+		test_hasAttributes_4075 : function()
+		{
+			if ( !CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
+				return;
+			var element = new CKEDITOR.dom.element( 'span' );
+			element.setAttribute( 'class', 'value' );
+			assert.isTrue( element.hasAttributes(), 'Failed to detect "class" attribute existence.' );
+		},
+
+		test_getDocumentPosition : function()
+		{
+			// Assign the page location of the element.
+			YAHOO.util.Dom.setXY('DocPositionTarget', [ 350, 450 ] );
+			var pos = CKEDITOR.document.getById( 'DocPositionTarget' ).getDocumentPosition();
+			if ( !CKEDITOR.env.ie || CKEDITOR.env.ie8 )
+			{
+				assert.areEqual( 350, pos.x, 'Position coordinates:x relative to document doesn\'t match.' );
+				assert.areEqual( 450, pos.y, 'Position coordinates:y relative to document doesn\'t match.' );
+			}
+			else
+			{
+				assert.isTrue( pos.x == 348 || pos.x == 349, 'Position coordinates:x relative to document doesn\'t match.' );
+				assert.areEqual( 448, pos.y, 'Position coordinates:y relative to document doesn\'t match.' );
+			}
+		},
+
+		// Test get last non-spaces child node.
+		test_getLast : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			var span1 = new CKEDITOR.dom.element( 'span' );
+			element.append( span1 );
+			element.append( new CKEDITOR.dom.text( ' ' ) );
+			element.append( new CKEDITOR.dom.text( ' ' ) );
+			var last = element.getLast( CKEDITOR.dom.walker.whitespaces( true ) );
+			assert.areSame( span1.$, last.$ );
+		},
+
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+	<style type="text/css" media="screen">
+		#test-computed {
+		    width:50%;
+		    margin:auto;
+		    padding:10px;
+		    z-index: 2;
+		}
+		.scroll
+		{
+			overflow: visible;
+		}
+		.block1
+		{
+			width: 100%;
+			height: 100px;
+			background-color: blue;
+			text-align: center;
+		}
+		.block2
+		{
+			margin: auto;
+			width: 200px;
+			height: 200px;
+			background-color: green;
+			position : relative;
+		}
+		.block3
+		{
+			position: absolute;
+			float:right;
+			width: 300px;
+			height: 300px;
+			background-color: black;
+		}
+		.block4
+		{
+			position: absolute;
+			left: 50px;
+			right: 50px;
+			width: 400px;
+			height: 400px;
+			background-color: pink;
+		}
+		#DocPositionTarget
+		{
+			background-color: red;
+		}
+	</style>
+</head>
+<body>
+	<textarea id="test1" rows="10" cols="80"></textarea>
+	<textarea id="test2" rows="10" cols="80"></textarea>
+	<textarea name="named1" rows="10" cols="80"></textarea>
+	<div>Test</div>
+	<div id="append"></div>
+	<div id="setStyle"></div>
+	<div id="setText"></div>
+	<div id="removeClass"></div>
+	<div id="removeOuter"><b id="removeInner"></b></div>
+	<div id="tabIndex10" tabindex="10"></div>
+	<div id="testExpandoAttributes" _cke_expando ="1" class="hasExpando" tabindex="1" style="float:right"></div>
+	<div id="tabIndexDef"></div>
+	<input id="tabIndexInputDef" />
+	<input id="tabIndexInput20" tabindex="20" />
+	<script id="tabIndexScriptDef" type="text/javascript"></script>
+	<div id="getText">Some <br /><b> sample text</b>    for <i>   testing</i></div>
+	<div id="getText2">A<b> </b>B</div>
+	<div id="getText3">A
+B</div>
+	<big>Test</big>
+	<small title="Testing">Test</small>
+
+	<div class="block1 scroll" id="block1"><div class="block2 scroll" id="block2"><div class="block3 scroll" id="block3"><div class="block4 scroll" id="block4"><div id="DocPositionTarget">target</div></div></div></div></div>
+
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/dom/node.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/dom/node.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/dom/node.html	(revision 4101)
@@ -0,0 +1,163 @@
+<!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>CKEDITOR.dom.node</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+var tc;
+CKEDITOR.test.addTestCase( tc = (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_getPosition1 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'p' )[0] );
+
+			assert.areSame( CKEDITOR.POSITION_PRECEDING, node1.getPosition( node2 ) );
+		},
+
+		test_getPosition2 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'p' )[0] );
+
+			assert.areSame( CKEDITOR.POSITION_FOLLOWING, node2.getPosition( node1 ) );
+		},
+
+		test_getPosition3 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'p' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'b' )[0] );
+
+			assert.areSame( CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING, node1.getPosition( node2 ) );
+		},
+
+		test_getPosition4 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'p' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'b' )[0] );
+
+			assert.areSame( CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING, node2.getPosition( node1 ) );
+		},
+
+		test_getPosition5 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'div' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'div' )[0] );
+
+			assert.areSame( CKEDITOR.POSITION_IDENTICAL, node1.getPosition( node2 ) );
+		},
+
+		test_getPosition6 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0].firstChild );
+
+			assert.areSame( CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING, node1.getPosition( node2 ) );
+		},
+
+		test_getPosition7 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0].firstChild );
+			assert.areSame( CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING, node2.getPosition( node1 ) );
+		},
+
+		test_getPosition8 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0].firstChild );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'b' )[0].firstChild );
+
+			assert.areSame( CKEDITOR.POSITION_PRECEDING, node1.getPosition( node2 ) );
+		},
+
+		test_getPosition9 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0].firstChild );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'b' )[0].firstChild );
+
+			assert.areSame( CKEDITOR.POSITION_FOLLOWING, node2.getPosition( node1 ) );
+		},
+
+		test_getPosition10 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'b' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'i' )[0] );
+
+			assert.areSame( CKEDITOR.POSITION_PRECEDING, node1.getPosition( node2 ) );
+		},
+
+		test_getPosition11 : function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'b' )[0] );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'i' )[0] );
+
+			assert.areSame( CKEDITOR.POSITION_FOLLOWING, node2.getPosition( node1 ) );
+		},
+
+		/**
+		 *  Test 'preceding' position.
+		 */
+		test_getPosition_3240: function()
+		{
+			var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'b' )[0].firstChild );
+			var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'span' )[0].firstChild );
+
+			assert.areSame( CKEDITOR.POSITION_PRECEDING, node1.getPosition( node2 ) );
+		},
+
+		// Test get previous non-spaces node.
+		test_getPrevious : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			var span1 = new CKEDITOR.dom.element( 'span' );
+			element.append( span1 );
+			element.append( new CKEDITOR.dom.text( ' ' ) );
+			var span2 = new CKEDITOR.dom.element( 'span' );
+			element.append( span2 );
+			var previous = span2.getPrevious( CKEDITOR.dom.walker.whitespaces( true ) );
+			assert.areSame( span1.$, previous.$ );
+		},
+
+		// Test get next non-spaces node.
+		test_getNext : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'append' ) );
+			var span1 = new CKEDITOR.dom.element( 'span' );
+			element.append( span1 );
+			element.append( new CKEDITOR.dom.text( ' ' ) );
+			var span2 = new CKEDITOR.dom.element( 'span' );
+			element.append( span2 );
+			var next = span1.getNext( CKEDITOR.dom.walker.whitespaces( true ) );
+			assert.areSame( span2.$, next.$ );
+		},
+
+		name : document.title
+	};
+})() );
+
+//window.onload = function()
+//{
+//	var node1 = new CKEDITOR.dom.node( document.getElementsByTagName( 'h1' )[0] );
+//	var node2 = new CKEDITOR.dom.node( document.getElementsByTagName( 'p' )[0] );
+//
+//	alert( node1.getPosition( node2 ) );
+//};
+//window.onload =  tc.test_getPosition6;
+	//]]>
+	</script>
+</head>
+<body>
+	<div>
+		<h1>Title</h1>
+		<p><b>Sample</b> <i>Text</i></p>
+	</div>
+	<span>Another</span>
+	<p id="append"></p>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/dom/range.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/dom/range.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/dom/range.html	(revision 4101)
@@ -0,0 +1,2410 @@
+<!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>CKEDITOR.dom.range</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+var html1, html2;
+var tests;
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local references.
+	var assert			= CKEDITOR.test.assert;
+	var getInnerHtml	= CKEDITOR.test.getInnerHtml;
+
+	var doc = new CKEDITOR.dom.document( document );
+
+	var getRange = function( startId, endId )
+	{
+		var range = new CKEDITOR.dom.range( CKEDITOR.document );
+		range.moveToBookmark( { startNode : startId, endNode : endId, serializable : true } );
+		return range;
+	};
+
+	return tests = {
+		test__constructor : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			assert.isNotNull( range, 'range must not be null' );
+
+			assert.isNull( range.startContainer, 'startContainer must be null' );
+			assert.isNull( range.startOffset, 'startOffset must be null' );
+			assert.isNull( range.endContainer, 'endContainer must be null' );
+			assert.isNull( range.endOffset, 'endOffset must be null' );
+
+			assert.isTrue( range.collapsed, 'range must be collapsed' );
+		},
+
+		test_setStart : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( 'playground' ), 1 );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$ );
+			assert.areSame( 1, range.startOffset );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$ );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed );
+		},
+
+		test_setEnd : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setEnd( doc.getById( 'playground' ), 1 );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$ );
+			assert.areSame( 1, range.startOffset );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$ );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed );
+		},
+
+		test_setStartAfter : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAfter( doc.getById( '_B' ) );
+			range.setStartAfter( doc.getById( '_H1' ).getFirst() );
+
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_setStartBefore : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartBefore( doc.getById( '_B' ) );
+			range.setStartBefore( doc.getById( '_H1' ).getFirst() );
+
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_setEndAfter : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setEndAfter( doc.getById( '_H1' ).getFirst() );
+			range.setEndAfter( doc.getById( '_B' ) );
+
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_setEndBefore : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setEndBefore( doc.getById( '_H1' ).getFirst() );
+			range.setEndBefore( doc.getById( '_B' ) );
+
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_setStartAt_1 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById('_Span'), CKEDITOR.POSITION_AFTER_START );
+
+			assert.areSame( document.getElementById('_Span'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_Span'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_setStartAt_2 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById('_Span'), CKEDITOR.POSITION_BEFORE_END );
+
+			assert.areSame( document.getElementById('_Span'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_Span'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_setStartAt_3 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById('_Span'), CKEDITOR.POSITION_BEFORE_START );
+
+			assert.areSame( document.getElementById('_P'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_P'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_setStartAt_4 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById('_Span'), CKEDITOR.POSITION_AFTER_END );
+
+			assert.areSame( document.getElementById('_P'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_P'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_setEndAt_1 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setEndAt( doc.getById('_Span'), CKEDITOR.POSITION_AFTER_START );
+
+			assert.areSame( document.getElementById('_Span'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_Span'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_setEndAt_2 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setEndAt( doc.getById('_Span'), CKEDITOR.POSITION_BEFORE_END );
+
+			assert.areSame( document.getElementById('_Span'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_Span'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_setEndAt_3 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setEndAt( doc.getById('_Span'), CKEDITOR.POSITION_BEFORE_START );
+
+			assert.areSame( document.getElementById('_P'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_P'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_setEndAt_4 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setEndAt( doc.getById('_Span'), CKEDITOR.POSITION_AFTER_END );
+
+			assert.areSame( document.getElementById('_P'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_P'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_collapsed : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( '_P' ), CKEDITOR.POSITION_AFTER_START );
+
+			assert.isTrue( range.collapsed );
+		},
+
+		test_collapse : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( doc.getById( '_Para' ), CKEDITOR.POSITION_AFTER_START );
+			range.setEndAt( doc.getById( '_Span' ), CKEDITOR.POSITION_BEFORE_END );
+
+			assert.areSame( document.getElementById('_Para'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_Span'), range.endContainer.$, 'endContainer' );
+			assert.isFalse( range.collapsed, 'collapsed' );
+
+			range.collapse( true );
+
+			assert.areSame( document.getElementById('_Para'), range.startContainer.$, 'startContainer' );
+			assert.areSame( document.getElementById('_Para'), range.endContainer.$, 'endContainer' );
+			assert.isTrue( range.collapsed, 'collapsed' );
+		},
+
+		test_selectNodeContents_Element : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			// Test with an Element node.
+			range.selectNodeContents( doc.getById( '_Para' ) );
+
+			assert.areSame( document.getElementById( '_Para' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 3, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_selectNodeContents_Text : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			// Test with a Text node.
+			range.selectNodeContents( doc.getById( '_Para' ).getFirst() );
+
+			assert.areSame( document.getElementById( '_Para' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ).firstChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 8, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_collapse_ToStart : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.selectNodeContents( doc.getById( '_P' ) );
+			range.collapse( true );
+
+			assert.areSame( document.getElementById( '_P' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_P' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_collapse_ToEnd : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.selectNodeContents( doc.getById( '_Para' ) );
+			range.collapse( false );
+
+			assert.areSame( document.getElementById( '_Para' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 3, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 3, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_insertNode_ElementContents : function()
+		{
+			var newNode = new CKEDITOR.dom.element( 'span' );
+			newNode.setHtml( 'test_' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.selectNodeContents( doc.getById( '_B' ) );
+			range.insertNode( newNode );
+
+			assert.areSame( document.getElementById( '_B' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_B' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_insertNode_ElementCollapsed : function()
+		{
+			var newNode = new CKEDITOR.dom.element( 'span' );
+			newNode.setHtml( 'test_' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartBefore( doc.getById( '_Para' ) );
+			range.insertNode( newNode );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_insertNode_ElementNotCollapsed : function()
+		{
+			var newNode = new CKEDITOR.dom.element( 'span' );
+			newNode.setHtml( 'test_' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartBefore( doc.getById( '_Para' ) );
+			range.setStartBefore( doc.getById( '_H1' ) );
+			range.insertNode( newNode );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_insertNode_DiffElements : function()
+		{
+			var newNode = new CKEDITOR.dom.element( 'span' );
+			newNode.setHtml( 'test_' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.selectNodeContents( doc.getById( '_Para' ) );
+
+			range.setStart( doc.getById( '_H1' ), 0 );
+			range.insertNode( newNode );
+
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 3, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+
+			assert.isTrue( range.startContainer.getChild( range.startOffset ).equals( newNode ), 'Start must be on new node' );
+		},
+
+		test_insertNode_TextCollapsed : function()
+		{
+			var newNode = new CKEDITOR.dom.element( 'span' );
+			newNode.setHtml( 'test_' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( doc.getById( '_H1' ).getFirst(), 3 );
+			range.insertNode( newNode );
+
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_H1' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_insertNode_TextNotCollapsed : function()
+		{
+			var newNode = new CKEDITOR.dom.element( 'span' );
+			newNode.setHtml( 'test_' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( doc.getById( '_H1' ).getFirst(), 3 );
+			range.setEnd( doc.getById( '_H1' ).getFirst(), 5 );
+			range.insertNode( newNode );
+
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_H1' ).childNodes[2], range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_insertNode_Mixed : function()
+		{
+			var newNode = new CKEDITOR.dom.element( 'span' );
+			newNode.setHtml( 'test_' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( doc.getById( '_H1' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_P' ), 1 );
+			range.insertNode( newNode );
+
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_P' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_getCommonAncestor1 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_H1' ).getFirst(), 3 );
+
+			assert.areSame( document.getElementById( '_H1' ).firstChild, range.getCommonAncestor().$ );
+		},
+
+		test_getCommonAncestor2 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_H1' ), 0 );
+
+			assert.areSame( document.getElementById( '_H1' ), range.getCommonAncestor().$ );
+		},
+
+		test_getCommonAncestor3 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_H1' ), 0 );
+			range.setEnd( doc.getById( '_Para' ), 0 );
+
+			assert.areSame( document.getElementById( 'playground' ), range.getCommonAncestor().$ );
+		},
+
+		test_getCommonAncestor4 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_Para' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_B' ), 0 );
+
+			assert.areSame( document.getElementById( '_Para' ), range.getCommonAncestor().$ );
+		},
+
+		test_getCommonAncestor5 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getBody(), 0 );
+			range.setEnd( doc.getById( '_B' ).getFirst(), 1 );
+
+			assert.areSame( document.body, range.getCommonAncestor().$ );
+		},
+
+		test_getCommonAncestor6 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI' ).getFirst(), 2 );
+			range.setEnd( doc.getById( '_EnlargeB' ), 3 );
+
+			assert.areSame( document.getElementById( '_EnlargeB' ), range.getCommonAncestor().$ );
+		},
+
+		test_enlarge_element1 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p> Test <b> <i>  [Enlarge]</i> this</b>   </p>
+			// <p> Test <b> [<i>  Enlarge</i>] this</b>   </p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI' ).getFirst(), 2 );
+			range.setEnd( doc.getById( '_EnlargeI' ), 1 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeB' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeB' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element1_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p>Test <b><i>[Enlarge]</i> this</b></p>
+			// <p>Test <b>[<i>Enlarge</i>] this</b></p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeI' ), 1 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeB' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeB' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element2 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p> Test <b> <i>  [Enlarge</i> this]</b>   </p>
+			// <p> Test [<b> <i>  Enlarge</i> this</b>]   </p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI' ).getFirst(), 2 );
+			range.setEnd( doc.getById( '_EnlargeB' ), 3 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element2_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p>Test <b><i>[Enlarge</i> this]</b></p>
+			// <p>Test [<b><i>Enlarge</i> this</b>]</p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeB' ), 2 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element3 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p> [Test <b> <i>  Enlarge]</i> this</b>   </p>
+			// <p> [Test <b> <i>  Enlarge</i>] this</b>   </p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeP' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_EnlargeI' ), 1 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeB' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element3_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p>[Test <b><i>Enlarge]</i> this</b></p>
+			// <p>[Test <b><i>Enlarge</i>] this</b></p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeP' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeI' ), 1 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeB' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element4 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p> [Test <b> <i>  Enlarge</i> this]</b>   </p>
+			// [<p> Test <b> <i>  Enlarge</i> this</b>   </p>]
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeP' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_EnlargeB' ).getChild( 2 ), 5 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ).parentNode, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( doc.getById( '_EnlargeP' ).getIndex(), range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP' ).parentNode, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( doc.getById( '_EnlargeP' ).getIndex() + 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element4_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p>[Test <b><i>Enlarge</i> this]</b></p>
+			// [<p>Test <b><i>Enlarge</i> this</b></p>]
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeP' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeB' ).getChild( 1 ), 5 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ).parentNode, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( doc.getById( '_EnlargeP' ).getIndex(), range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP' ).parentNode, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( doc.getById( '_EnlargeP' ).getIndex() + 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element5 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p> Test<b> <i>  [Enlarge</i> this]</b>   </p>
+			// <p> Test<b> [<i>  Enlarge</i> this]</b>   </p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI1' ).getFirst(), 2 );
+			range.setEnd( doc.getById( '_EnlargeB1' ).getChild( 2 ), 5 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeB1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeB1' ).childNodes[ 2 ], range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 5, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element5_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p>Test<b> <i>[Enlarge</i> this]</b></p>
+			// <p>Test<b> [<i>Enlarge</i> this]</b></p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI1' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeB1' ).getChild( 2 ), 5 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeB1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeB1' ).childNodes[ 2 ], range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 5, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element6 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p> <b> <i>  [Enlarge</i>] this</b>   </p>
+			// <p> <b> [<i>  Enlarge</i>] this</b>   </p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI2' ).getFirst(), 2 );
+			range.setEnd( doc.getById( '_EnlargeB2' ), 2 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeB2' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeB2' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element6_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p><b><i>[Enlarge</i>] this</b></p>
+			// <p><b>[<i>Enlarge</i>] this</b></p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI2' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeB2' ), 1 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeB2' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeB2' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element7 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p> <b> <i>  [Enlarge</i> this]</b>   </p>
+			// [<p> <b> <i>  Enlarge</i> this</b>   </p>]
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI2' ).getFirst(), 2 );
+			range.setEnd( doc.getById( '_EnlargeB2' ), 3 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP2' ).parentNode, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( doc.getById( '_EnlargeP2' ).getIndex(), range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP2' ).parentNode, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( doc.getById( '_EnlargeP2' ).getIndex() + 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element7_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p><b><i>[Enlarge</i> this]</b></p>
+			// [<p><b><i>Enlarge</i> this</b></p>]
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI2' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeB2' ), 2 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP2' ).parentNode, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( doc.getById( '_EnlargeP2' ).getIndex(), range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP2' ).parentNode, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( doc.getById( '_EnlargeP2' ).getIndex() + 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element8 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p> Test <b> <i>  [Enlarge</i> this</b>   </p><p> <b> <i>  Enlarge</i> this]</b>   </p>
+			// <p> Test [<b> <i>  Enlarge</i> this</b>   </p><p> <b> <i>  Enlarge</i> this</b>   </p>]
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI' ).getFirst(), 2 );
+			range.setEnd( doc.getById( '_EnlargeB2' ), 3 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP2' ).parentNode, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( doc.getById( '_EnlargeP2' ).getIndex() + 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element8_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p>Test <b><i>[Enlarge</i> this</b></p><p><b><i>Enlarge</i> this]</b></p>
+			// <p>Test [<b><i>Enlarge</i> this</b></p><p><b><i>Enlarge</i> this</b></p>]
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeB2' ), 2 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP2' ).parentNode, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( doc.getById( '_EnlargeP2' ).getIndex() + 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element9 : function()
+		{
+			// <p>Test<i> [Enlarge</i>]</p>
+			// <p>Test<i> [Enlarge</i>]</p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI3' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_EnlargeP3' ), 2 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeI3' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP3' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element10 : function()
+		{
+			// For IE, see the next test.
+			if ( CKEDITOR.env.ie )
+				return;
+
+			// <p>Test <i> [Enlarge</i>]</p>
+			// <p>Test [<i> Enlarge</i>]</p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI4' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_EnlargeP4' ), 2 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP4' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP4' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element10_ie : function()
+		{
+			// IE normalizes whitespaces when parsing the HTML, so we need a
+			// specific test for it.
+			if ( !CKEDITOR.env.ie )
+				return;
+
+			// <p>Test <i>[Enlarge</i>]</p>
+			// <p>Test [<i>Enlarge</i>]</p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI4' ).getFirst(), 0 );
+			range.setEnd( doc.getById( '_EnlargeP4' ), 2 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP4' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP4' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element11 : function()
+		{
+			// <p>Test <i>[Enlarge]</i></p>
+			// <p>Test [<i>Enlarge</i>]</p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI5' ), 0 );
+			range.setEnd( doc.getById( '_EnlargeI5' ), 1 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP5' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP5' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element12 : function()
+		{
+			// <p>Test <i><b></b>[Enlarge]</i></p>
+			// <p>Test [<i><b></b>Enlarge</i>]</p>
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeI6' ), 1 );
+			range.setEnd( doc.getById( '_EnlargeI6' ), 2 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP6' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP6' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_element13 : function()
+		{
+			// <p>Test <i><b></b>[Enlarge]</i></p>
+			// <p>Test [<i><b></b>Enlarge</i>]</p>
+
+			doc.getById( '_EnlargeP' ).setHtml( 'this <i>is some </i>sample text' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_EnlargeP' ), 0 );
+			range.setEnd( doc.getById( '_EnlargeP' ).getChild( 1 ), 0 );
+
+			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+			assert.areSame( document.getElementById( '_EnlargeP' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP' ).childNodes[ 1 ], range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_list1 : function()
+		{
+			var range = getRange( 'S1', null );
+			range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP7' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP7' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 3, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_list2 : function()
+		{
+			var range = getRange( 'S2', 'E2' );
+			range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP8' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP8' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 4, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_list3 : function()
+		{
+			var range = getRange( 'S3', null );
+			range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP9' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 2, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP9' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 3, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_list4 : function()
+		{
+			var range = getRange( 'S4', null );
+			range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP10' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 3, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP10' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 5, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_list5 : function()
+		{
+			var range = getRange( 'S9', null );
+			var bookmark = range.createBookmark();
+			range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP15' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP15' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 4, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+			range.moveToBookmark( bookmark );
+		},
+
+		test_enlarge_block1 : function()
+		{
+			var range = getRange( 'S5', null );
+			range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP11' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP11'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 5, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_block2 : function()
+		{
+			var range = getRange( 'S10', null );
+			var bookmark = range.createBookmark();
+			range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP16' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP16'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 5, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+			range.moveToBookmark( bookmark );
+		},
+
+		test_enlarge_block3 : function()
+		{
+			var range = getRange( 'S6', null );
+			range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP12' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP12'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_block4 : function()
+		{
+			var range = getRange( 'S7', null );
+			range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP13' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_EnlargeP13'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_enlarge_block5 : function()
+		{
+			var range = getRange( 'S8', null );
+			range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP14' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		/**
+		 * Test enlarge list when there's no nodes between
+		 * range start and the block boundary.
+		 */
+		test_enlarge_block6 : function()
+		{
+			var range = getRange( 'S11', null );
+			range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS );
+
+			assert.areSame( document.getElementById( '_EnlargeP17' ),
+				range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+		},
+
+		test_deleteContents_W3C_1 : function()
+		{
+			// W3C DOM Range Specs - Section 2.6 - Example 1
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_Para' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_Para' ), 2 );
+
+			range.deleteContents();
+
+			assert.areSame( 't text.', getInnerHtml( '_Para' ), 'HTML after deletion' );
+
+			assert.areSame( document.getElementById( '_Para' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ).firstChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_deleteContents_W3C_2 : function()
+		{
+			// W3C DOM Range Specs - Section 2.6 - Example 2
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_B' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_B' ).getNext(), 1 );
+
+			range.deleteContents();
+
+			assert.areSame( 'this is <b id="_b">s</b>text.', getInnerHtml( '_Para' ) );
+
+			assert.areSame( document.getElementById( '_Para' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 2, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_deleteContents_W3C_3 : function()
+		{
+			// W3C DOM Range Specs - Section 2.6 - Example 3
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_B' ).getPrevious(), 1 );
+			range.setEnd( doc.getById( '_B' ).getFirst(), 1 );
+
+			range.deleteContents();
+
+			assert.areSame( 't<b id="_b">ome</b> text.', getInnerHtml( '_Para' ) );
+
+			assert.areSame( document.getElementById( '_Para' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_deleteContents_W3C_4 : function()
+		{
+			// W3C DOM Range Specs - Section 2.6 - Example 4
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_H1' ).getFirst(), 1 );
+			range.setEnd( doc.getById( 'playground' ).getLast().getFirst(), 1 );
+
+			range.deleteContents();
+
+			assert.areSame( '<h1 id="_h1">f</h1><p>nother paragraph.</p>', getInnerHtml( 'playground' ) );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_deleteContents_Other : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_H1' ), 0 );
+			range.setEnd( doc.getById( 'playground' ).getLast(), 1 );
+
+			range.deleteContents();
+
+			assert.areSame( '<h1 id="_h1"></h1><p></p>', getInnerHtml( 'playground' ) );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_deleteContents_Other_2 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( 'playground' ), 0 );
+			range.setEnd( doc.getById( 'playground' ), 2 );
+
+			range.deleteContents();
+
+			assert.areSame( '<p>another paragraph.</p>', getInnerHtml( 'playground' ) );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame(document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_deleteContents_Other_3 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( doc.getById('_B') );
+
+			range.deleteContents();
+
+			assert.areSame( '', getInnerHtml('_B') );
+
+			assert.areSame( document.getElementById('_B'), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('_B'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_deleteContents_Other_4 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( doc.getById('_Para') );
+
+			range.deleteContents();
+
+			assert.areSame( '', getInnerHtml('_Para') );
+
+			assert.areSame( document.getElementById('_Para'), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('_Para'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_W3C_1 : function()
+		{
+			// W3C DOM Range Specs - Section 2.7 - Example 1
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_Para' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_Para' ), 2 );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( 'his is <b id="_b">some</b>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( 't text.', getInnerHtml( '_Para' ), 'HTML after extraction' );
+
+			assert.areSame( document.getElementById( '_Para' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ).firstChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_W3C_2 : function()
+		{
+			// W3C DOM Range Specs - Section 2.7 - Example 2
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_B' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_B' ).getNext(), 2 );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<b>ome</b> t', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( 'this is <b id="_b">s</b>ext.', getInnerHtml( '_Para' ), 'HTML after extraction' );
+
+			assert.areSame( document.getElementById( '_Para' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 2, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_W3C_3 : function()
+		{
+			// W3C DOM Range Specs - Section 2.6 - Example 3
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_B' ).getPrevious(), 1 );
+			range.setEnd( doc.getById( '_B' ).getFirst(), 1 );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( 'his is <b>s</b>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( 't<b id="_b">ome</b> text.', getInnerHtml( '_Para' ), 'HTML after extraction' );
+
+			assert.areSame( document.getElementById( '_Para' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_W3C_4 : function()
+		{
+			// W3C DOM Range Specs - Section 2.6 - Example 4
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_H1' ).getFirst(), 1 );
+			range.setEnd( doc.getById( 'playground' ).getLast().getFirst(), 1 );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<h1>ckw3crange test</h1><p id="_para">this is <b id="_b">some</b> text.</p><p>a</p>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( '<h1 id="_h1">f</h1><p>nother paragraph.</p>', getInnerHtml( 'playground' ) );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_Other : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_H1' ), 0 );
+			range.setEnd( doc.getById( 'playground' ).getLast(), 1 );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<h1>fckw3crange test</h1><p id="_para">this is <b id="_b">some</b> text.</p><p>another paragraph.</p>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( '<h1 id="_h1"></h1><p></p>', getInnerHtml( 'playground' ) );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_Other_2 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( 'playground' ), 0 );
+			range.setEnd( doc.getById( 'playground' ), 2 );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<h1 id="_h1">fckw3crange test</h1><p id="_para">this is <b id="_b">some</b> text.</p>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( '<p>another paragraph.</p>', getInnerHtml( 'playground' ) );
+
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_Other_3 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.selectNodeContents( doc.getById('_B') );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( 'some', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( '', getInnerHtml('_B'), 'HTML after extraction' );
+
+			assert.areSame( document.getElementById('_B'), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('_B'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_Other_4 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.selectNodeContents( doc.getById('_Para') );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( 'this is <b id="_b">some</b> text.', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( '', getInnerHtml('_Para'), 'HTML after extraction' );
+
+			assert.areSame( document.getElementById('_Para'), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('_Para'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_Other_5 : function()
+		{
+			document.getElementById( 'playground' ).innerHTML = '<p><b><i>test</i></b></p>';
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAfter( new CKEDITOR.dom.element( document.getElementById( 'playground' ).getElementsByTagName('i')[0] ) );
+			range.setEndAfter( new CKEDITOR.dom.element( document.getElementById( 'playground' ).getElementsByTagName('b')[0] ) );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<b></b>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( '<p><b><i>test</i></b></p>', getInnerHtml( 'playground' ), 'HTML after extraction' );
+
+			assert.areSame( document.getElementById( 'playground' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ).firstChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_extractContents_Other_6 : function()
+		{
+			document.getElementById( 'playground' ).innerHTML = '<p><b><i>test</i></b></p>';
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartBefore( new CKEDITOR.dom.element( document.getElementById( 'playground' ).getElementsByTagName('b')[0] ) );
+			range.setEndBefore( new CKEDITOR.dom.element( document.getElementById( 'playground' ).getElementsByTagName('i')[0] ) );
+
+			var docFrag = range.extractContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<b></b>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( '<p><b><i>test</i></b></p>', getInnerHtml( 'playground' ), 'HTML after extraction' );
+
+			assert.areSame( document.getElementById( 'playground' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ).firstChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 0, range.endOffset, 'range.endOffset' );
+			assert.isTrue( range.collapsed, 'range.collapsed' );
+		},
+
+		test_cloneContents_W3C_1 : function()
+		{
+			// W3C DOM Range Specs - Section 2.7 - Example 1
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_Para' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_Para' ), 2 );
+
+			var bodyHtml = document.getElementById( 'playground' ).innerHTML;
+
+			var docFrag = range.cloneContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( 'his is <b>some</b>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+
+			// The body HTML must remain unchanged.
+			assert.areSame( bodyHtml.replace( /\s+_cke_expando=["\d]+/g, '' ), document.getElementById( 'playground' ).innerHTML.replace( /\s+_cke_expando=["\d]+/g, '' ), 'The HTML must remain untouched' );
+
+			// The range must also remain unchanged.
+			assert.areSame( document.getElementById( '_Para' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_Para' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_cloneContents_W3C_2 : function()
+		{
+			// W3C DOM Range Specs - Section 2.7 - Example 2
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_B' ).getFirst(), 1 );
+			range.setEnd( doc.getById( '_B' ).getNext(), 2 );
+
+			var bodyHtml = document.getElementById( 'playground' ).innerHTML;
+
+			var docFrag = range.cloneContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<b>ome</b> t', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+
+			// The body HTML must remain unchanged.
+			assert.areSame( bodyHtml.replace( /\s+_cke_expando=["\d]+/g, '' ), document.getElementById( 'playground' ).innerHTML.replace( /\s+_cke_expando=["\d]+/g, '' ), 'The HTML must remain untouched' );
+
+			// The range must also remain unchanged.
+			assert.areSame( document.getElementById( '_B' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_B' ).nextSibling, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_cloneContents_W3C_3 : function()
+		{
+			// W3C DOM Range Specs - Section 2.6 - Example 3
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStart( doc.getById( '_B' ).getPrevious(), 1 );
+			range.setEnd( doc.getById( '_B' ).getFirst(), 1 );
+
+			var bodyHtml = document.getElementById( 'playground' ).innerHTML;
+
+			var docFrag = range.cloneContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( 'his is <b>s</b>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+
+			// The body HTML must remain unchanged.
+			assert.areSame( bodyHtml.replace( /\s+_cke_expando=["\d]+/g, '' ), document.getElementById( 'playground' ).innerHTML.replace( /\s+_cke_expando=["\d]+/g, '' ), 'The HTML must remain untouched' );
+
+			// The range must also remain unchanged.
+			assert.areSame( document.getElementById( '_B' ).previousSibling, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( '_B' ).firstChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		// W3C DOM Range Specs - Section 2.6 - Example 4
+		test_cloneContents_W3C_4 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( doc.getById( '_H1' ).getFirst(), 1 );
+			range.setEnd( doc.getById( 'playground' ).getLast().getFirst(), 1 );
+
+			var bodyHtml = document.getElementById( 'playground' ).innerHTML;
+
+			var docFrag = range.cloneContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<h1>ckw3crange test</h1><p>this is <b>some</b> text.</p><p>a</p>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+
+			// The body HTML must remain unchanged.
+			assert.areSame( bodyHtml.replace( /\s+_cke_expando=["\d]+/g, '' ), document.getElementById( 'playground' ).innerHTML.replace( /\s+_cke_expando=["\d]+/g, '' ), 'The HTML must remain untouched' );
+
+			// The range must also remain unchanged.
+			assert.areSame( document.getElementById( '_H1' ).firstChild, range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 1, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ).lastChild.firstChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_cloneContents_Other : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( doc.getById( '_H1' ), 0 );
+			range.setEnd( doc.getById( 'playground' ).getLast(), 1 );
+
+			var bodyHtml = document.getElementById( 'playground' ).innerHTML;
+
+			var docFrag = range.cloneContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<h1>fckw3crange test</h1><p>this is <b>some</b> text.</p><p>another paragraph.</p>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+
+			// The body HTML must remain unchanged.
+			assert.areSame( bodyHtml.replace( /\s+_cke_expando=["\d]+/g, '' ), document.getElementById( 'playground' ).innerHTML.replace( /\s+_cke_expando=["\d]+/g, '' ), 'The HTML must remain untouched' );
+
+			// The range must also remain unchanged.
+			assert.areSame( document.getElementById( '_H1' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ).lastChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_cloneContents_Other_2 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( doc.getById( 'playground' ), 0 );
+			range.setEnd( doc.getById( 'playground' ), 2 );
+
+			var bodyHtml = document.getElementById( 'playground' ).innerHTML;
+
+			var docFrag = range.cloneContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( '<h1>fckw3crange test</h1><p>this is <b>some</b> text.</p>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+
+			// The body HTML must remain unchanged.
+			assert.areSame( bodyHtml.replace( /\s+_cke_expando=["\d]+/g, '' ), document.getElementById( 'playground' ).innerHTML.replace( /\s+_cke_expando=["\d]+/g, '' ), 'The HTML must remain untouched' );
+
+			// The range must also remain unchanged.
+			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById( 'playground' ), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 2, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_cloneContents_Other_3 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.selectNodeContents( doc.getById('_B') );
+
+			var bodyHtml = document.getElementById( 'playground' ).innerHTML;
+
+			var docFrag = range.cloneContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( 'some', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+
+			// The body HTML must remain unchanged.
+			assert.areSame( bodyHtml.replace( /\s+_cke_expando=["\d]+/g, '' ), document.getElementById( 'playground' ).innerHTML.replace( /\s+_cke_expando=["\d]+/g, '' ), 'The HTML must remain untouched' );
+
+			assert.areSame( document.getElementById('_B'), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('_B'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 1, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_cloneContents_Other_4 : function()
+		{
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.selectNodeContents( doc.getById('_Para') );
+
+			var bodyHtml = document.getElementById( 'playground' ).innerHTML;
+
+			var docFrag = range.cloneContents();
+
+			var tmpDiv = doc.createElement( 'div' );
+			docFrag.appendTo( tmpDiv );
+
+			assert.areSame( 'this is <b>some</b> text.', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+
+			// The body HTML must remain unchanged.
+			assert.areSame( bodyHtml.replace( /\s+_cke_expando=["\d]+/g, '' ), document.getElementById( 'playground' ).innerHTML.replace( /\s+_cke_expando=["\d]+/g, '' ), 'The HTML must remain untouched' );
+
+			assert.areSame( document.getElementById('_Para'), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('_Para'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 3, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_createBookmark2_1 : function()
+		{
+			doc.getById( 'playground' ).setHtml( '<p id="P">This is <b id="B">a test</b></p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( doc.getById( 'P' ), 0 );
+			range.setEnd( doc.getById( 'B' ).getFirst(), 3 );
+
+			var bookmark = range.createBookmark2();
+
+			range = new CKEDITOR.dom.range( doc );
+			range.moveToBookmark( bookmark );
+
+			assert.areSame( document.getElementById('P'), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 0, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('B').firstChild, range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 3, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		// This test is just like test_createBookmark2_3, but uses a "non
+		// normalized" bookmark.
+		test_createBookmark2_2 : function()
+		{
+			var html = '<p id="P">A B <b>C </b>D E</p>';
+
+			doc.getById( 'playground' ).setHtml( html );
+
+			var p = doc.getById( 'P' );
+
+			// Split the text nodes.
+			p.getFirst().split( 2 );	// Right before "B"
+			p.getChild( 3 ).split( 2 );	// Right before "E"
+
+			assert.areSame( 5, p.getChildCount(), 'The number of nodes after split doesn\'t match' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			// Create a range that enbraces "E".
+			range.setStartBefore( p.getChild( 4 ) );
+			range.setEndAfter( p.getChild( 4 ) );
+
+			var bookmark = range.createBookmark2();
+
+			range = new CKEDITOR.dom.range( doc );
+			range.moveToBookmark( bookmark );
+
+			assert.areSame( document.getElementById('P'), range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 4, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('P'), range.endContainer.$, 'range.endContainer' );
+			assert.areSame( 5, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_createBookmark2_3 : function()
+		{
+			var html = '<p id="P">A B <b>C </b>D E</p>';
+
+			doc.getById( 'playground' ).setHtml( html );
+
+			var p = doc.getById( 'P' );
+
+			// Split the text nodes.
+			p.getFirst().split( 2 );	// Right before "B"
+			p.getChild( 3 ).split( 2 );	// Right before "E"
+
+			assert.areSame( 5, p.getChildCount(), 'The number of nodes after split doesn\'t match' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			// Create a range that enbraces "E".
+			range.setStartBefore( p.getChild( 4 ) );
+			range.setEndAfter( p.getChild( 4 ) );
+
+			var bookmark = range.createBookmark2( true );
+
+			// Normalize the contents.
+			doc.getById( 'playground' ).setHtml( html );
+
+			range = new CKEDITOR.dom.range( doc );
+			range.moveToBookmark( bookmark );
+
+			assert.areSame( document.getElementById('P').childNodes[2], range.startContainer.$, 'range.startContainer' );
+			assert.areSame( 2, range.startOffset, 'range.startOffset' );
+			assert.areSame( document.getElementById('P'), range.endContainer.$, 'range.endContainer' );
+
+			// Note that the endOffset doesn't get normalized as it's not
+			// needed. Any offset pointing over the container size is meant to
+			// be at the end of it.
+			assert.areSame( 5, range.endOffset, 'range.endOffset' );
+			assert.isFalse( range.collapsed, 'range.collapsed' );
+		},
+
+		test_checkStartOfBlock1 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p, CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			assert.isTrue( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock2 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p, CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+
+			assert.isFalse( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock3 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p.getFirst(), CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			assert.isTrue( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock4 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p.getFirst(), CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+
+			assert.isFalse( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock5 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p> Test </p>' );
+			el = el.getFirst().getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			// IE trims the space in the beginning of text nodes in our case.
+			// So, let's just check it and make it pass.
+			range.setStart( el, ( el.substring( 0, 1 ) == 'T' ) ? 0 : 1 );
+			range.collapse( true );
+
+			assert.isTrue( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock6 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p> Test </p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( p.getFirst(), 5 );
+			range.collapse( true );
+
+			assert.isFalse( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock7 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p><b>Test</b></p>' );
+			el = el.getFirst().getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( el );
+
+			assert.isTrue( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock8 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p>A<b>Test</b>B</p>' );
+			el = el.getFirst().getFirst().getNext();
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( el );
+
+			assert.isFalse( range.checkStartOfBlock() );
+		},
+
+		test_checkEndOfBlock1 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p, CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			assert.isFalse( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock2 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p, CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+
+			assert.isTrue( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock3 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p.getFirst(), CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			assert.isFalse( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock4 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p.getFirst(), CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+
+			assert.isTrue( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock5 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p> Test </p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( p.getFirst(), 1 );
+			range.collapse( true );
+
+			assert.isFalse( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock6 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p> Test </p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( p.getFirst(), 5 );
+			range.collapse( true );
+
+			assert.isTrue( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock7 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p><b>Test</b></p>' );
+			el = el.getFirst().getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( el );
+
+			assert.isTrue( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock8 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p>A<b>Test</b>B</p>' );
+			el = el.getFirst().getFirst().getNext();
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( el );
+
+			assert.isFalse( range.checkEndOfBlock() );
+		},
+
+		test_checkStartOfBlock1 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p, CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			assert.isTrue( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock2 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p, CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+
+			assert.isFalse( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock3 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p.getFirst(), CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			assert.isTrue( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock4 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p.getFirst(), CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+
+			assert.isFalse( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock5 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p> Test </p>' );
+			el = el.getFirst().getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			// IE trims the space in the beginning of text nodes in our case.
+			// So, let's just check it and make it pass.
+			range.setStart( el, ( el.substring( 0, 1 ) == 'T' ) ? 0 : 1 );
+			range.collapse( true );
+
+			assert.isTrue( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock6 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p> Test </p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( p.getFirst(), 5 );
+			range.collapse( true );
+
+			assert.isFalse( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock7 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p><b>Test</b></p>' );
+			el = el.getFirst().getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( el );
+
+			assert.isTrue( range.checkStartOfBlock() );
+		},
+
+		test_checkStartOfBlock8 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p>A<b>Test</b>B</p>' );
+			el = el.getFirst().getFirst().getNext();
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( el );
+
+			assert.isFalse( range.checkStartOfBlock() );
+		},
+
+		test_checkEndOfBlock1 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p, CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			assert.isFalse( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock2 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p, CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+
+			assert.isTrue( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock3 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p.getFirst(), CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			assert.isFalse( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock4 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p>Test</p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( p.getFirst(), CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+
+			assert.isTrue( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock5 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p> Test </p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( p.getFirst(), 1 );
+			range.collapse( true );
+
+			assert.isFalse( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock6 : function()
+		{
+			var p = doc.getById( 'playground' );
+			p.setHtml( '<p> Test </p>' );
+			p = p.getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStart( p.getFirst(), 5 );
+			range.collapse( true );
+
+			assert.isTrue( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock7 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p><b>Test</b></p>' );
+			el = el.getFirst().getFirst();
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( el );
+
+			assert.isTrue( range.checkEndOfBlock() );
+		},
+
+		test_checkEndOfBlock8 : function()
+		{
+			var el = doc.getById( 'playground' );
+			el.setHtml( '<p>A<b>Test</b>B</p>' );
+			el = el.getFirst().getFirst().getNext();
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( el );
+
+			assert.isFalse( range.checkEndOfBlock() );
+		},
+
+		/**
+		 *  Test trim with text range.
+		 */
+		test_trim : function()
+		{
+			var text = doc.getById( '_trim_ct').getFirst();
+			var range = new CKEDITOR.dom.range();
+			range.setStart(text, 2);
+			range.setEnd(text, 6);
+			range.trim();
+
+			assert.isFalse( range.collapsed );
+			assert.isTrue( range.startContainer.equals( doc.getById( '_trim_ct') ) );
+			assert.areEqual( range.startOffset, 1 );
+			assert.isTrue( range.endContainer.equals( doc.getById( '_trim_ct') ) );
+			assert.areEqual( range.endOffset, 2 );
+		},
+
+		/**
+		 * Trim range which collapsed at text node boundary.
+		 */
+		test_trim_3790 : function()
+		{
+
+			var ct = doc.getById( '_trim_ct');
+			ct.setHtml( '<span id="_SPAN1">text</span>' );
+			var text = doc.getById( '_trim_ct').getFirst();
+
+			// <span id="_SPAN1">text^</span>
+			var range = new CKEDITOR.dom.range();
+			range.setStartAt( doc.getById( '_SPAN1' ).getFirst(), CKEDITOR.POSITION_BEFORE_END );
+			range.collapse( true );
+			range.trim( true );
+
+			// <span id="_SPAN1">text^</span>
+			assert.isTrue( range.collapsed );
+			assert.areEqual( doc.getById( '_SPAN1').$, range.startContainer.$ );
+			assert.areEqual( range.startOffset, 1 );
+		},
+
+		/**
+		 * Trim range which collapsed inside text node.
+		 */
+		test_trim_3790_2 : function()
+		{
+
+			var ct = doc.getById( '_trim_ct');
+			ct.setHtml( '<span id="_SPAN1">text</span>' );
+			var text = doc.getById( '_trim_ct').getFirst();
+
+			// <span id="_SPAN1">te^xt</span>
+			var range = new CKEDITOR.dom.range();
+			range.setStart( doc.getById( '_SPAN1' ).getFirst(), 2 );
+			range.collapse( true );
+			range.trim( true );
+
+			// <span id="_SPAN1">te^xt</span>
+			assert.isTrue( range.collapsed );
+			assert.areEqual( doc.getById( '_SPAN1').$, range.startContainer.$ );
+			assert.areEqual( range.startOffset, 1 );
+		},
+		/////////////
+
+		setUp : function()
+		{
+			 document.getElementById( 'playground' ).innerHTML = html1;
+			 document.getElementById( 'playground2' ).innerHTML = html2;
+		},
+
+		name : document.title
+	};
+})() );
+
+//window.onload = tests.test_trim;
+	//]]>
+	</script>
+</head>
+<body>
+	<div id="playground" style="visibility:hidden"><h1 id="_H1">FCKW3CRange Test</h1><p id="_Para">This is <b id="_B">some</b> text.</p><p>Another paragraph.</p></div>
+	<div id="playground2" style="visibility: hidden">
+		<h1>
+			Test page for CKEditor
+		</h1>
+		<p id="_P">
+			This document contains various markup features commonly used by content editors
+			or "<span id="_Span" lang="fr">r&eacute;dacteurs de contenu</span>" as they are
+			called in <a id="_A" href="http://en.wikipedia.org/wiki/France" title="Wikipedia article about France">
+				France</a>.<br />
+			It is important that a <acronym id="_Acronym" title="what you see is what you get">WYSIWYG</acronym>
+			tool has features that are easily available for the editor. If not, there is a risk
+			that content won't receive <strong id="_Strong">proper</strong> markup. Examples
+			of commonly found content are:</p>
+		<p id="_Pnbsp">
+			&nbsp;
+		</p>
+		<p id="_Pspaces">
+		</p>
+		<ol>
+			<li>Headings</li>
+			<li style="color: Red">Links (with optional title) </li>
+			<li>Lists (like this one)
+				<ul>
+					<li>including nested lists </li>
+				</ul>
+			</li>
+			<li>Tables
+				<ul>
+					<li>caption</li>
+					<li>headers</li>
+					<li>summary</li>
+				</ul>
+			</li>
+			<li>Language information</li>
+			<li>Acronyms and abbreviations</li>
+			<li>Emphasis and strong emphasis </li>
+			<li>Quotes, inline and block </li>
+			<li>Images</li>
+		</ol>
+		<hr />
+		<h2 style="background-color: Silver">
+			Test procedure
+		</h2>
+		This text has no block tag. It should be corrected when working with the enter key
+		set to "p" or "div" tags. The "br" configuration should not make changes instead.
+		<p id="_P2">
+			In the test we will try to recreate this document using the editor tools. To make
+			sure tables can be inserted <em id="_Em">properly</em> we re-visit banana import
+			statistics from 1998.
+		</p>
+		<p id="_P3">
+			This paragraph has and image at the very end of its contents.<img id="_Img" src="img.gif"
+				alt="" />
+		</p>
+		This text has no block tag.<br />It should be corrected when working with the enter key
+		set to "p" or "div" tags. The <strong id="_StrongNoPara">"br" configuration</strong>
+		should not make changes instead.<br />It has three lines separated by BR tags.
+		<p>
+			In the test we will try to recreate this document using the editor tools. To make
+			sure tables can be inserted <em>properly</em> we re-visit banana import statistics
+			from 1998.
+		</p>
+		<table summary="Sweden was the top importing country by far in 1998.">
+			<caption>
+				Top banana importers 1998 (value of banana imports in millions of US dollars per
+				million people)<br />
+				<br />
+			</caption>
+			<tr>
+				<th scope="col">
+					Country</th>
+				<th scope="col">
+					Millions of US dollars per million people</th>
+			</tr>
+			<tr>
+				<td>
+					Sweden</td>
+				<td>
+					17.12</td>
+			</tr>
+			<tr>
+				<td>
+					United&nbsp;Kingdom</td>
+				<td>
+					8.88</td>
+			</tr>
+			<tr>
+				<td>
+					Germany</td>
+				<td>
+					8.36</td>
+			</tr>
+			<tr>
+				<td>
+					Italy</td>
+				<td>
+					5.96</td>
+			</tr>
+			<tr>
+				<td>
+					United States</td>
+				<td>
+					4.78</td>
+			</tr>
+		</table>
+		<p>
+			For block quotes we will have a look at <a href="http://fawny.org/rhcp.html">what Joe
+				Clark says about redheads</a>:</p>
+		<blockquote cite="http://fawny.org/rhcp.html#me">
+			<p>
+				"Since boyhood I&rsquo;ve always believed, at the deepest level, that redheads are
+				standard-bearers of the grandest and most wondrous human beauty."</p>
+		</blockquote>
+		<p>
+			<img src="img.gif" alt="" /></p>
+		<p>
+			The above is the CKEditor logo loaded from the FCKeditor.net web site.</p>
+		<p><b id="_B1">Line 1<br />Line 2<br /><i id="_I1">Line 3</i></b></p>
+		<p id="_EnlargeP"> Test <b id="_EnlargeB"> <i id="_EnlargeI">  Enlarge</i> this</b>   </p>
+		<p id="_EnlargeP1"> Test<b id="_EnlargeB1"> <i id="_EnlargeI1">  Enlarge</i> this</b>   </p>
+		<p id="_EnlargeP2"> <b id="_EnlargeB2"> <i id="_EnlargeI2">  Enlarge</i> this</b>   </p>
+		<p id="_EnlargeP3">Test<i id="_EnlargeI3"> Enlarge</i></p>
+		<p id="_EnlargeP4">Test <i id="_EnlargeI4"> Enlarge</i></p>
+		<p id="_EnlargeP5">Test <i id="_EnlargeI5">Enlarge</i></p>
+		<p id="_EnlargeP6">Test <i id="_EnlargeI6"><b></b>Enlarge</i></p>
+		<p id="_EnlargeP7">Test <span id="S1"></span>List<br/ >Item Enlarge</p>
+		<p id="_EnlargeP8">Test <span id="S2"></span>List<span id="E2"></span> <br /><br />Item Enlarge</p>
+		<p id="_EnlargeP9">Test List <br /><span id="S3"></span><br />Item Enlarge</p>
+		<p id="_EnlargeP10">Test List <br /><br />Item<span id="S4"></span> Enlarge</p>
+		<p id="_EnlargeP11">Test <strong>Block<span id="S5"></span></strong><br /><br />Enlarge</p>
+		<div id="_EnlargeP12">Test<span id="S6"></span> Block <div>Enlarge</div></div>
+		<div>Test <div id="_EnlargeP13">Blo<span id="S7"></span>ck</div> Enlarge</div>
+		<p id="_EnlargeP14"><span id="S8"></span></p>
+		<p id="_EnlargeP15">Test <span id="S9"></span>List<br/ >Item Enlarge</p>
+		<p id="_EnlargeP16">Test <strong>Block<span id="S10"></span></strong><br /><br />Enlarge</p>
+		<p id="_EnlargeP17"><span><span id="S11"></span>Test Block Enlarge<span id="E11"></span></span></p>
+		<p id="_trim_ct">Test trim</p>
+	</div>
+	<script type="text/javascript">
+	//<![CDATA[
+
+html1 = document.getElementById( 'playground' ).innerHTML;
+html2 = document.getElementById( 'playground2' ).innerHTML;
+
+	//]]>
+	</script>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/dom/text.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/dom/text.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/dom/text.html	(revision 4101)
@@ -0,0 +1,127 @@
+<!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>CKEDITOR.dom.text</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+var tc;
+CKEDITOR.test.addTestCase( tc = (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+
+		test_substring1 : function()
+		{
+			var text = new CKEDITOR.dom.text( '0123456789' );
+
+			assert.areSame( '123', text.substring( 1, 4 ) );
+		},
+
+		test_substring2 : function()
+		{
+			var text = new CKEDITOR.dom.text( '0123456789' );
+
+			assert.areSame( '56789', text.substring( 5 ) );
+		},
+
+		test_substring3 : function()
+		{
+			var text = new CKEDITOR.dom.text( '0123456789' );
+
+			assert.areSame( '', text.substring( 1,1 ) );
+		},
+
+		test_substring4 : function()
+		{
+			var text = new CKEDITOR.dom.text( '0123456789' );
+
+			assert.areSame( '012', text.substring( -10,3 ) );
+		},
+
+		test_substring5 : function()
+		{
+			var text = new CKEDITOR.dom.text( '0123456789' );
+
+			assert.areSame( '89', text.substring( 8,100 ) );
+		},
+
+		test_substring6 : function()
+		{
+			var text = new CKEDITOR.dom.text( '0123456789' );
+
+			assert.areSame( '234', text.substring( 5,2 ) );
+		},
+
+		test_split1 : function()
+		{
+			var div = CKEDITOR.document.getById( 'playground' );
+			div.setHtml( '01234' );
+
+			var text = div.getFirst(),
+				next = text.split( 3 );
+
+			assert.areSame( '012', text.getText(), 'text.getText() is wrong' );
+			assert.areSame( '34', next.getText(), 'next.getText() is wrong' );
+
+			assert.areSame( div.$, next.$.parentNode, 'parentNode is wrong' );
+			assert.areSame( text.$, next.$.previousSibling, 'sibling is wrong' );
+		},
+
+		test_split2 : function()
+		{
+			var div = CKEDITOR.document.getById( 'playground' );
+			div.setHtml( '01234' );
+
+			var text = div.getFirst(),
+				next = text.split( 5 );
+
+			assert.areSame( '01234', text.getText(), 'text.getText() is wrong' );
+			assert.areSame( '', next.getText(), 'next.getText() is wrong' );
+
+			assert.areSame( div.$, next.$.parentNode, 'parentNode is wrong' );
+			assert.areSame( text.$, next.$.previousSibling, 'sibling is wrong' );
+		},
+
+		test_split3 : function()
+		{
+			var div = CKEDITOR.document.getById( 'playground' );
+			div.setHtml( '01234' );
+
+			var text = div.getFirst(),
+				next = text.split( 0 );
+
+			assert.areSame( '', text.getText(), 'text.getText() is wrong' );
+			assert.areSame( '01234', next.getText(), 'next.getText() is wrong' );
+
+			assert.areSame( div.$, next.$.parentNode, 'parentNode is wrong' );
+			assert.areSame( text.$, next.$.previousSibling, 'sibling is wrong' );
+		},
+
+		test_split_3436 : function()
+		{
+			var parent = CKEDITOR.document.getById( 'playground2' );
+			parent.setHtml( 'A B <b>C </b>D E' );
+			parent.getFirst().split( 2 );	// Right before "B"
+			parent.getChildren().getItem( 3 ).split( 2 );	// Right before "E"
+			assert.areSame( 5, parent.getChildren().count(), 'Child nodes num doesn\'t match after split' );
+		},
+
+		name : document.title
+	};
+})() );
+
+//window.onload = tc.test_split_3436;
+
+	//]]>
+	</script>
+</head>
+<body>
+	<p id="playground"></p>
+	<p id="playground2"></p>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/dom/walker.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/dom/walker.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/dom/walker.html	(revision 4101)
@@ -0,0 +1,339 @@
+<!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>CKEDITOR.dom.walker</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+var tc;
+
+CKEDITOR.test.addTestCase( tc = (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	var doc = new CKEDITOR.dom.document( document );
+
+	function assertNodesList( wanted, nodes )
+	{
+		var simplifiedNodes = [];
+
+		for ( var i = 0 ; i < nodes.length ; i++ )
+			simplifiedNodes.push( nodes[i].type == CKEDITOR.NODE_TEXT ? nodes[i].getText() : ( '<' + nodes[i].getName() + '>' ) );
+
+		assert.areSame( wanted.toString(), simplifiedNodes.toString() );
+	}
+
+	return {
+
+		test_collapsed : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>Test</p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+
+			range.setStartAt( node.getFirst(), CKEDITOR.POSITION_AFTER_START );
+			range.collapse( true );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			assert.isNull( walker.next() );
+		},
+
+		test_next_1 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>This is <b>a <i>simple</i></b> test</p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( node );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.next() ) )
+				nodes.push( node );
+
+			assertNodesList( [ '<p>', 'This is ', '<b>', 'a ', '<i>', 'simple', ' test' ], nodes );
+		},
+
+		test_next_2 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>This is <b>a <i>simple</i></b> test</p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( node.getFirst(), CKEDITOR.POSITION_AFTER_START );
+			range.setEnd( node.getChild( [0,1,1,0] ), 2 );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.next() ) )
+				nodes.push( node );
+
+			assertNodesList( [ 'This is ', '<b>', 'a ', '<i>', 'si' ], nodes );
+		},
+
+		test_next_3 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>Test</p><h1>More</h1>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( node.getChild( 1 ), CKEDITOR.POSITION_BEFORE_START );
+			range.setEndAt( node.getChild( 1 ), CKEDITOR.POSITION_AFTER_START );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.next() ) )
+				nodes.push( node );
+
+			assertNodesList( [ '<h1>' ], nodes );
+		},
+
+		test_next_4 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>Test</p><h1>More</h1>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( node.getChild( 0 ), CKEDITOR.POSITION_BEFORE_END );
+			range.setEndAt( node.getChild( 0 ), CKEDITOR.POSITION_AFTER_END );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.next() ) )
+				nodes.push( node );
+
+			assertNodesList( [], nodes );
+		},
+
+		test_next_5 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>Test</p><h1>More</h1>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( node.getChild( 0 ), CKEDITOR.POSITION_BEFORE_END );
+			range.setEndAt( node.getChild( 1 ), CKEDITOR.POSITION_AFTER_START );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.next() ) )
+				nodes.push( node );
+
+			assertNodesList( [ '<h1>' ], nodes );
+		},
+
+		test_previous_1 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>This is <b>a <i>simple</i></b> test</p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( node );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.previous() ) )
+				nodes.push( node );
+
+			assertNodesList( [ '<p>', ' test', '<b>', '<i>', 'simple', 'a ', 'This is ' ], nodes );
+		},
+
+		test_previous_2 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>This is <b>a <i>simple</i></b> test</p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setEnd( node.getChild( [0,0] ), 2 );
+			range.setEnd( node.getChild( [0,1,1,0] ), 2 );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.previous() ) )
+				nodes.push( node );
+
+			assertNodesList( [ 'si', 'a ', 'is is ' ], nodes );
+		},
+
+		test_previous_3 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>Test</p><h1>More</h1>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( node.getChild( 1 ), CKEDITOR.POSITION_BEFORE_START );
+			range.setEndAt( node.getChild( 1 ), CKEDITOR.POSITION_AFTER_START );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.previous() ) )
+				nodes.push( node );
+
+			assertNodesList( [ '' ], nodes );
+		},
+
+		test_previous_4 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>Test</p><h1>More</h1>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( node.getChild( 0 ), CKEDITOR.POSITION_BEFORE_END );
+			range.setEndAt( node.getChild( 0 ), CKEDITOR.POSITION_AFTER_END );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.previous() ) )
+				nodes.push( node );
+
+			assertNodesList( [ '<p>' ], nodes );
+		},
+
+		test_previous_5 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>Test</p><h1>More</h1>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.setStartAt( node.getChild( 0 ), CKEDITOR.POSITION_BEFORE_END );
+			range.setEndAt( node.getChild( 1 ), CKEDITOR.POSITION_AFTER_START );
+
+			var walker = new CKEDITOR.dom.walker( range );
+
+			var nodes = [];
+			while ( ( node = walker.previous() ) )
+				nodes.push( node );
+
+			assertNodesList( [ '<p>' ], nodes );
+		},
+
+		/**
+		 *  Test guard function is invoked on every move when iterating forward.
+		 */
+		test_guard_1 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>This is <b>a <i>simple</i></b> test</p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( node );
+
+			var walker = new CKEDITOR.dom.walker( range );
+			var nodes = [];
+
+			walker.guard = function( node )
+			{
+				nodes.push( node );
+				return true;
+			};
+
+			while ( ( node = walker.next() ) )
+			{ /*jsl:pass*/ }
+
+			assertNodesList( [ '<p>', 'This is ', '<b>', 'a ', '<i>', 'simple', '<i>', '<b>',' test' , '<p>' ], nodes );
+		},
+
+		/**
+		 *  Test guard function is invoked on every move when iterating backward.
+		 */
+		test_guard_2 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>This is <b>a <i>simple</i></b> test</p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( node );
+
+			var walker = new CKEDITOR.dom.walker( range );
+			var nodes = [];
+
+			walker.guard = function( node )
+			{
+				nodes.push( node );
+				return true;
+			};
+
+			while ( ( node = walker.previous() ) )
+			{ /*jsl:pass*/ }
+
+			assertNodesList( [ '<p>', ' test', '<b>', '<i>', 'simple', '<i>', 'a ', '<b>', 'This is ', '<p>' ], nodes );
+		},
+
+		/**
+		 *  Test evaluator function is invoked on every  step when iterating backward.
+		 */
+		test_evaluator_1 : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<p>This is <b>a <i>simple</i></b> test</p>' );
+
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( node );
+
+			var walker = new CKEDITOR.dom.walker( range );
+			var nodes = [];
+
+			walker.evaluator = function( node )
+			{
+				nodes.push( node );
+				return true;
+			};
+
+			while ( ( node = walker.previous() ) )
+			{ /*jsl:pass*/ }
+
+			assertNodesList( [ '<p>', ' test', '<b>', '<i>', 'simple', 'a ', 'This is ' ], nodes );
+		},
+		/**
+		 * Test walker stop at empty endContainer.
+		 */
+		test_stopGuard : function()
+		{
+			var node = doc.getById( 'playground' );
+			node.setHtml( '<span></span>afterEnd' );
+			var endContainer = node.getFirst();
+			var range = new CKEDITOR.dom.range( doc );
+			range.selectNodeContents( node );
+			range.setEndAt( endContainer, CKEDITOR.POSITION_AFTER_START );
+
+			var walker = new CKEDITOR.dom.walker( range );
+			var nodes = [];
+
+			walker.evaluator = function( node )
+			{
+				nodes.push( node );
+				return true;
+			};
+
+			while ( ( node = walker.next() ) )
+			{ /*jsl:pass*/ }
+
+			assertNodesList( [ '<span>' ], nodes );
+
+		},
+		name : document.title
+	};
+})() );
+
+//window.onload = tc.test_stopGuard;
+	//]]>
+	</script>
+</head>
+<body>
+	<div id="playground"></div>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/dom/window.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/dom/window.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/dom/window.html	(revision 4101)
@@ -0,0 +1,52 @@
+﻿<!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>CKEDITOR.dom.window</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+var loadCalled;
+
+var testWindow = new CKEDITOR.dom.window( window );
+
+var t;
+
+testWindow.on( 'load', function()
+	{
+		t = document.createElement( 'textarea' );
+		t.id = 'fred';
+
+		document.body.appendChild( t );
+
+		loadCalled = true;
+	});
+
+testWindow.on( 'beforeunload', function()
+	{
+		t.value = new Date();
+	});
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_loadEvent : function()
+		{
+			assert.isTrue( loadCalled );
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+	<div id="test1"></div>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/editor.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/editor.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/editor.html	(revision 4101)
@@ -0,0 +1,122 @@
+<!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>CKEDITOR.editor</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+// Disable 'scayt' plugin due to it may alter editor config after initialized.
+CKEDITOR.config.removePlugins = 'scayt';
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_name : function()
+		{
+			assert.areSame( 'editor1', CKEDITOR.instances.editor1.name );
+		},
+
+		test_element : function()
+		{
+			assert.areSame( document.getElementById( 'editor1' ), CKEDITOR.instances.editor1.element.$ );
+		},
+
+		test_config : function()
+		{
+			// The instance default config must match the CKEDITOR.config.
+
+			var config = CKEDITOR.instances.editor1.config;
+
+			for ( var prop in CKEDITOR.config )
+				assert.areSame( CKEDITOR.config[ prop ], config[ prop ], '"' + prop + '" doesn\'t match' );
+		},
+
+		test_config_inpage : function()
+		{
+			var self = this;
+
+			CKEDITOR.replace( 'editor2',
+				{
+					// The custom setting to be checked.
+					test1 : 'ball',
+					baseHref : 'test',
+
+					on :
+					{
+						instanceReady : function()
+						{
+							self.resume( function()
+							{
+								var config = CKEDITOR.instances.editor2.config;
+
+								assert.areSame( 'ball', config.test1, '"test1" doesn\'t match' );
+								assert.areSame( 'test', config.baseHref, '"baseHref" doesn\'t match' );
+
+								// All other settings must match CKEDITOR.config.
+								for ( var prop in CKEDITOR.config )
+								{
+									if ( prop != 'test1' && prop != 'baseHref' )
+										assert.areSame( CKEDITOR.config[ prop ], config[ prop ], '"' + prop + '" doesn\'t match' );
+								}
+							} );
+						}
+					}
+				});
+
+			this.wait();
+		},
+
+		test_config_customConfig : function()
+		{
+			var testCase = this;
+
+			CKEDITOR.on( 'instanceCreated', function( event )
+				{
+					if ( event.editor.name == 'editor3' )
+					{
+						event.editor.on( 'configLoaded', function()
+							{
+								testCase.resume( function()
+								{
+									var config = event.editor.config;
+
+									assert.areSame( 'Ok', config.test_custom1, '"test_custom1" doesn\'t match' );
+									assert.areSame( 'Ok', config.test_custom2, '"test_custom1" doesn\'t match' );
+									assert.areSame( 'ball', config.test1, '"test1" doesn\'t match' );
+									assert.areSame( 'test', config.baseHref, '"baseHref" doesn\'t match' );
+
+									// All other settings must match CKEDITOR.config.
+									for ( var prop in CKEDITOR.config )
+									{
+										if ( prop != 'customConfig' && prop != 'test_custom1' && prop != 'test_custom2' && prop != 'test1' && prop != 'baseHref' )
+											assert.areSame( CKEDITOR.config[ prop ], config[ prop ], '"' + prop + '" doesn\'t match' );
+									}
+								});
+							});
+					}
+				});
+
+			// Pass in-page settings to the instance.
+			CKEDITOR.replace( 'editor3', { customConfig : '_editor/custom_config_1.js', test1 : 'ball', baseHref : 'test' } );
+
+			this.wait();
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+	<textarea id="editor1" class="ckeditor" cols="80" rows="10"></textarea>
+	<textarea id="editor2" cols="80" rows="10"></textarea>
+	<textarea id="editor3" cols="80" rows="10"></textarea>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/env.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/env.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/env.html	(revision 4101)
@@ -0,0 +1,51 @@
+<!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>CKEDITOR.env</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_ie : function()
+		{
+			if ( window.ActiveXObject )
+			{
+				assert.isTrue( CKEDITOR.env.ie		, 'ie should be true' );
+				assert.isFalse( CKEDITOR.env.opera	, 'opera should be false' );
+				assert.isFalse( CKEDITOR.env.webkit	, 'webkit should be false' );
+				assert.isFalse( CKEDITOR.env.air	, 'air should be false' );
+				assert.isFalse( CKEDITOR.env.gecko	, 'gecko should be false' );
+				assert.isFalse( CKEDITOR.env.mac	, 'ismac should be false' );
+			}
+			else
+				assert.isFalse( CKEDITOR.env.ie );
+		},
+
+		test_isCompatible : function()
+		{
+			assert.isTrue( CKEDITOR.env.isCompatible );
+		},
+
+
+		test_quirks : function()
+		{
+			assert.isFalse( CKEDITOR.env.quirks );
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/event.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/event.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/event.html	(revision 4101)
@@ -0,0 +1,486 @@
+<!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>CKEDITOR.event</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_inherit : function()
+		{
+			// Create a testClass that inherits from CKEDITOR.event.
+			var testClass = function()
+			{
+				CKEDITOR.event.call( this );
+			};
+			testClass.prototype = CKEDITOR.event.prototype;
+
+			var calls = [];
+
+			var testInstance = new testClass();
+
+			testInstance.on( 'someEvent', function( ev )
+				{
+					assert.areSame( testInstance, this, 'Scope 1 is not valid' );
+
+					assert.areSame( 'someEvent', ev.name, 'ev.name (1) is wrong' );
+					assert.areSame( testInstance, ev.sender, 'ev.sender (1) is wrong' );
+					assert.isUndefined( ev.editor, 'ev.editor (1) is wrong' );
+					assert.isUndefined( ev.data, 'ev.data (1) is wrong' );
+					assert.isUndefined( ev.listenerData, 'ev.listenerData (1) is wrong' );
+
+					calls.push( 'a' );
+				} );
+
+			testInstance.on( 'someEvent', function( ev )
+				{
+					assert.areSame( testInstance, this, 'Scope 2 is not valid' );
+
+					assert.areSame( 'someEvent', ev.name, 'ev.name (2) is wrong' );
+					assert.areSame( testInstance, ev.sender, 'ev.sender (2) is wrong' );
+					assert.isUndefined( ev.editor, 'ev.editor (2) is wrong' );
+					assert.isUndefined( ev.data, 'ev.data (2) is wrong' );
+					assert.isUndefined( ev.listenerData, 'ev.listenerData (2) is wrong' );
+
+					calls.push( 'b' );
+				} );
+
+			assert.areSame( false, testInstance.fire( 'someEvent' ) );
+
+			assert.areSame( 2, calls.length, 'number of calls doesn\'t match' );
+			assert.areSame( 'a,b', calls.toString() );
+		},
+
+		test_implementOn : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = { someprop : 'Test' };
+			CKEDITOR.event.implementOn( testObject );
+
+			assert.areSame( 'Test', testObject.someprop );
+
+			var calls = [];
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( testObject, this, 'Scope 1 is not valid' );
+
+					assert.areSame( 'someEvent', ev.name, 'ev.name (1) is wrong' );
+					assert.areSame( testObject, ev.sender, 'ev.sender (1) is wrong' );
+					assert.isUndefined( ev.editor, 'ev.editor (1) is wrong' );
+					assert.isUndefined( ev.data, 'ev.data (1) is wrong' );
+					assert.isUndefined( ev.listenerData, 'ev.listenerData (1) is wrong' );
+
+					calls.push( 'a' );
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( testObject, this, 'Scope 2 is not valid' );
+
+					assert.areSame( 'someEvent', ev.name, 'ev.name (2) is wrong' );
+					assert.areSame( testObject, ev.sender, 'ev.sender (2) is wrong' );
+					assert.isUndefined( ev.editor, 'ev.editor (2) is wrong' );
+					assert.isUndefined( ev.data, 'ev.data (2) is wrong' );
+					assert.isUndefined( ev.listenerData, 'ev.listenerData (2) is wrong' );
+
+					calls.push( 'b' );
+				} );
+
+			assert.areSame( false, testObject.fire( 'someEvent' ) );
+
+			assert.areSame( 2, calls.length, 'number of calls doesn\'t match' );
+			assert.areSame( 'a,b', calls.toString() );
+		},
+
+		test_eventNameCase : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var counter = 0;
+
+			// Event Names are case sensitive.
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( 'someEvent', ev.name, 'ev.name (someEvent) is wrong' );
+					counter++;
+				} );
+
+			testObject.on( 'SomeEvent', function( ev )
+				{
+					assert.areSame( 'SomeEvent', ev.name, 'ev.name (SomeEvent) is wrong' );
+					counter++;
+				} );
+
+			testObject.fire( 'someEvent' );
+			assert.areSame( 1, counter, '"someEvent" calls doesn\'t match' );
+
+			counter = 0;
+
+			testObject.fire( 'SomeEvent' );
+			assert.areSame( 1, counter, '"SomeEvent" calls doesn\'t match' );
+		},
+
+		test_scope : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			// Counter used just to check that the calls are effectively done.
+			var counter = 0;
+
+			var testScope = {};
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( testObject, this, 'scope (testObject) is wrong' );
+					assert.areSame( testObject, ev.sender, 'sender (testObject) is wrong' );
+					counter++;
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( testScope, this, 'scope (testScope) is wrong' );
+					assert.areSame( testObject, ev.sender, 'sender (testScope) is wrong' );
+					counter++;
+				}, testScope );
+
+			testObject.fire( 'someEvent' );
+
+			assert.areSame( 2, counter, 'wrong number of calls' );
+		},
+
+		test_listenerData : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			// Counter used just to check that the calls are effectively done.
+			var counter = 0;
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( 'Test1', ev.listenerData, 'listenerData (1) is wrong' );
+					counter++;
+				}, null, 'Test1'  );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( 'Test2', ev.listenerData, 'listenerData (2) is wrong' );
+					counter++;
+				}, null, 'Test2' );
+
+			testObject.fire( 'someEvent' );
+
+			assert.areSame( 2, counter, 'wrong number of calls' );
+		},
+
+		test_data : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			// Counter used just to check that the calls are effectively done.
+			var counter = 0;
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( 'Test data', ev.data, 'data (1) is wrong' );
+					counter++;
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( 'Test data', ev.data, 'data (2) is wrong' );
+					counter++;
+				} );
+
+			testObject.fire( 'someEvent', 'Test data' );
+
+			assert.areSame( 2, counter, 'wrong number of calls' );
+		},
+
+		test_editor : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			// Counter used just to check that the calls are effectively done.
+			var counter = 0;
+
+			var editor = {};
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( editor, ev.editor, 'editor is wrong' );
+					counter++;
+				} );
+
+			testObject.fire( 'someEvent', null, editor );
+
+			assert.areSame( 1, counter, 'wrong number of calls' );
+		},
+
+		test_stop : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var counter = 0;
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					ev.stop();
+					counter++;
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					counter++;
+				} );
+
+			assert.areSame( false, testObject.fire( 'someEvent' ), 'fire must return "false"' );
+			assert.areSame( 1, counter, 'number of calls doesn\'t match' );
+		},
+
+		test_cancel : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var counter = 0;
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					ev.cancel();
+					counter++;
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					counter++;
+				} );
+
+			assert.areSame( true, testObject.fire( 'someEvent' ), 'fire must return "true"' );
+			assert.areSame( 1, counter, 'number of calls doesn\'t match' );
+		},
+
+		test_dataManipulation : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var counter = 0;
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( 'Test1', ev.data );
+					ev.data = 'Test2';
+					counter++;
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( 'Test2', ev.data );
+					ev.data = 'Test3';
+					counter++;
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					assert.areSame( 'Test3', ev.data );
+					ev.data = 'Test4';
+					counter++;
+				} );
+
+			assert.areSame( 'Test4', testObject.fire( 'someEvent', 'Test1' ), 'fire must return "Test4"' );
+			assert.areSame( 3, counter, 'number of calls doesn\'t match' );
+		},
+
+		test_priority : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var calls = [];
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					calls.push( 'e' );
+				}, null, null, 11 );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					calls.push( 'c' );
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					calls.push( 'a' );
+				}, null, null, 9 );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					calls.push( 'f' );
+				}, null, null, 11 );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					calls.push( 'd' );
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					calls.push( 'b' );
+				}, null, null, 9 );
+
+			testObject.fire( 'someEvent', 'Test data' );
+			assert.areSame( 'a,b,c,d,e,f', calls.toString() );
+		},
+
+		test_removeListener : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var calls = [];
+
+			var listener = function( ev )
+				{
+					calls.push( 'a' );
+				};
+
+			testObject.on( 'someEvent', listener );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					calls.push( 'b' );
+				} );
+
+			testObject.fire( 'someEvent', 'Test data' );
+			assert.areSame( 'a,b', calls.toString() );
+
+			testObject.removeListener( 'someEvent', listener );
+
+			calls = [];
+
+			testObject.fire( 'someEvent', 'Test data' );
+			assert.areSame( 'b', calls.toString() );
+		},
+
+		test_fireOnce : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var counter = 0;
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					counter++;
+				} );
+
+			testObject.on( 'someEvent', function( ev )
+				{
+					counter++;
+				} );
+
+			assert.areSame( false, testObject.fireOnce( 'someEvent' ), 'fireOnce must return "false"' );
+			assert.areSame( 2, counter, 'number of calls doesn\'t match' );
+
+			counter = 0;
+
+			assert.areSame( false, testObject.fire( 'someEvent' ), 'fire must return "false"' );
+			assert.areSame( 0, counter, 'number of calls doesn\'t match' );
+		},
+
+		test_nestedCancel : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var isCanceledA,
+				isCanceledB,
+				isCanceledC;
+
+			testObject.on( 'A', function( ev )
+				{
+					isCanceledB = testObject.fire( 'B' );
+					isCanceledC = testObject.fire( 'C' );
+				});
+
+			testObject.on( 'B', function( ev )
+				{
+					ev.cancel();
+				});
+
+			testObject.on( 'C', function( ev )
+				{
+				});
+
+			isCanceledA = testObject.fire( 'A' );
+
+			assert.areSame( false, isCanceledA, 'event A must not be canceled' );
+			assert.areSame( true, isCanceledB, 'event B must be canceled' );
+			assert.areSame( false, isCanceledC, 'event C must not be canceled' );
+		},
+
+		test_event_removeListener : function()
+		{
+			// Create a testObject and implement CKEDITOR.event on it.
+			var testObject = {};
+			CKEDITOR.event.implementOn( testObject );
+
+			var counter = 0;
+
+			// Add two listeners for the same event "A".
+
+			testObject.on( 'A', function( ev )
+				{
+					counter++;
+					ev.removeListener();
+				});
+
+			testObject.on( 'A', function( ev )
+				{
+					counter++;
+				});
+
+			// Fire the event twice.
+			testObject.fire( 'A' );
+			testObject.fire( 'A' );
+
+			assert.areSame( 3, counter );
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/htmlparser/fragment.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/htmlparser/fragment.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/htmlparser/fragment.html	(revision 4101)
@@ -0,0 +1,187 @@
+﻿<!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>CKEDITOR.htmlParser.fragment</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+var tc;
+
+CKEDITOR.test.addTestCase( tc = (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	function testParser( input, expected )
+	{
+		var fragment = CKEDITOR.htmlParser.fragment.fromHtml( input, 'p' ),
+			writer = new CKEDITOR.htmlParser.basicWriter();
+
+		fragment.writeHtml( writer );
+
+		assert.areSame( expected, writer.getHtml( true ) );
+	}
+
+	return {
+		test_fromHtml_1 : function()
+		{
+			var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p>Test</p>' );
+
+			assert.areSame( 1, fragment.children.length, 'Wrong size' );
+			assert.areSame( CKEDITOR.NODE_ELEMENT, fragment.children[0].type, 'Wrong child type' );
+			assert.areSame( 'p', fragment.children[0].name, 'Wrong child name' );
+		},
+
+		test_parser_1 : function()
+		{
+			testParser(	'<table><tr><td>1</td><p><b>2</b> Test</p><td>3</td></tr></table>',
+						'<p><b>2</b> Test</p><table><tr><td>1</td><td>3</td></tr></table>' );
+		},
+
+		test_parser_2 : function()
+		{
+			testParser(	'<b><table><tr><td>1</td><td>2</td></tr></table></b>',
+						'<table><tr><td><b>1</b></td><td><b>2</b></td></tr></table>' );
+		},
+
+		test_parser_3_1 : function()
+		{
+			testParser(	'<b><i>Table:<table><tr><td>1</td><td>2</td></tr></table></i></b>',
+						'<p><b><i>Table:</i></b></p><table><tr><td><b><i>1</i></b></td><td><b><i>2</i></b></td></tr></table>' );
+		},
+
+		test_parser_3_2 : function()
+		{
+			testParser(	'<b><i><table><tr><td>1</td><td>2</td></tr></table>Table</i></b>',
+						'<table><tr><td><b><i>1</i></b></td><td><b><i>2</i></b></td></tr></table><p><b><i>Table</i></b></p>' );
+		},
+
+		test_parser_4 : function()
+		{
+			testParser(	'<b><i>Test',
+						'<p><b><i>Test</i></b></p>' );
+		},
+
+		test_parser_5 : function()
+		{
+			testParser(	'<p>Para 1<p>Para 2<p>Para 3',
+						'<p>Para 1</p><p>Para 2</p><p>Para 3</p>' );
+		},
+
+		test_parser_6 : function()
+		{
+			testParser(	'<b>A</b><i>B</i>',
+						'<p><b>A</b><i>B</i></p>' );
+		},
+
+		test_parser_7 : function()
+		{
+			testParser(	'<p>Para 1<hr>Para 2<h1>Para 3',
+						'<p>Para 1</p><hr /><p>Para 2</p><h1>Para 3</h1>' );
+		},
+
+		/**
+		 * Test remove empty inline element.
+		 */
+		test_parser_8 : function()
+		{
+			testParser(	'<p><b></b>text</p>',
+						'<p>text</p>' );
+		},
+
+		/**
+		 *  Test remove multiple empty inline elements.
+		 */
+		test_parser_8_2 : function()
+		{
+			testParser(	'<p><b><i></b></i>text</p>',
+						'<p>text</p>' );
+		},
+
+		/**
+		 * Test fixing malformed inline element closing.
+		 */
+		test_parser_9 : function()
+		{
+			testParser(	'<p><b>bold<i>ita</b>lic</i></p>',
+						'<p><b>bold<i>ita</i></b><i>lic</i></p>' );
+		},
+
+		test_ticket_10 : function()
+		{
+			testParser(	'<table><tbody><tr><td>A<b></b></td></tr></tbody></table>',
+						'<table><tbody><tr><td>A</td></tr></tbody></table>' );
+		},
+
+		/**
+		 *  Test fixing paragraph inside table row.
+		 */
+		test_ticket_3195 : function()
+		{
+			testParser(	'<table><tr><td>1</td><p>2</p><td>3</td></tr></table>',
+						'<p>2</p><table><tr><td>1</td><td>3</td></tr></table>' );
+		},
+
+		/**
+		 *  Test fixing paragraph inside list.
+		 */
+		test_ticket_3195_2 : function()
+		{
+			testParser(	'<ul><li>1</li><p>2</p><li>3</li></ul>',
+						'<p>2</p><ul><li>1</li><li>3</li></ul>' );
+		},
+
+		/**
+		 *  Test fixing 'div' inside paragraph.
+		 */
+		test_ticket_3195_3 : function()
+		{
+			testParser(	'<p>1<div>2</div><span>3</span></p>',
+						'<p>1</p><div>2</div><p><span>3</span></p>' );
+		},
+
+		test_ticket_3441 : function()
+		{
+			testParser(	'<p><b>Test</b></p><script type="test">var a = "<A Href=xxx>Testing</ A>";\nGo();<\/script>',
+						'<p><b>Test</b></p><script type="test">var a = "<A Href=xxx>Testing</ A>";\nGo();<\/script>' );
+		},
+
+		test_ticket_3585 : function()
+		{
+			testParser(	'<p><br />\t\r\n</p>',
+						'<p><br /></p>' );
+		},
+
+		test_ticket_3585_1 : function()
+		{
+			testParser(	'<p><br />text\t\r\n</p>',
+						'<p><br />text</p>' );
+		},
+
+		test_ticket_3585_2 : function()
+		{
+			testParser(	'<b>inline </b>\n<p>paragraph\t\r\n</p>\t\r\n',
+						'<p><b>inline </b></p><p>paragraph</p>' );
+		},
+
+		test_ticket_3744 : function()
+		{
+			testParser(	'<div><b><font><span>A</font></span></b></div><div>X</div>',
+						'<div><b><font><span>A</span></font></b></div><div>X</div>' );
+		},
+
+		name : document.title
+	};
+})() );
+
+// Uncomment the following to run a single test.
+// window.onload = tc.test_ticket_3585_2;
+
+	//]]>
+	</script>
+</head>
+<body>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/htmlparser/htmlparser.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/htmlparser/htmlparser.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/htmlparser/htmlparser.html	(revision 4101)
@@ -0,0 +1,90 @@
+<!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>CKEDITOR.htmlParser</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.plugins.load( 'htmlwriter' );
+var tc;
+CKEDITOR.test.addTestCase( tc = ( function()
+{
+	// Local reference to the "assert" object.
+	var assert = YAHOO.util.Assert;
+
+	/**
+	 * Wrapper of the combination of htmlParser with htmlWriter, for convenience of
+	 * testing, formatting of writer has been disabled.
+	 */
+	function htmlParse( htmlString , writerConfig)
+	{
+		var writer = new CKEDITOR.htmlParser.basicWriter();
+		writer.reset();
+		var fragment = CKEDITOR.htmlParser.fragment.fromHtml( htmlString );
+		fragment.writeHtml( writer );
+		return writer.getHtml();
+	}
+
+	/**
+	 * 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, '' );
+	}
+
+	return {
+		/**
+		 * Test <pre> tag formatting.
+		 */
+		test_fromHtml_3715 : function()
+		{
+			assert.areSame( getTextAreaValue( 'htmlResult1' ),
+				htmlParse( getTextAreaValue( 'htmlOriginal1' ) ),
+				'<pre> parsing result doesn\'t match.' );
+		},
+
+		/**
+		 * Test compress empty spaces within text.
+		 */
+		test_fromHtml_3715_2 : function()
+		{
+			assert.areSame( getTextAreaValue( 'htmlResult2' ),
+				htmlParse( getTextAreaValue( 'htmlOriginal2' ) ),
+				'Spaces compressing result doesn\'t match.' );
+		},
+		name :document.title
+	};
+} )() );
+
+//window.onload = tc.test_fromHtml_3715;
+	//]]>
+	</script>
+</head>
+<body>
+
+	<textarea id="htmlOriginal1"><pre>
+	text<b>
+inside<br /><br /></b>
+
+pre
+</pre></textarea>
+	<textarea id="htmlResult1"><pre>
+	text<b>
+inside
+
+</b>
+
+pre
+</pre></textarea>
+
+<textarea id="htmlOriginal2"><p>
+	para		graph
+</p><b>compress   spaces</b></textarea>
+
+<textarea id="htmlResult2"><p>para graph</p><b>compress spaces</b></textarea>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/plugins.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/plugins.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/plugins.html	(revision 4101)
@@ -0,0 +1,43 @@
+<!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>CKEDITOR.plugins</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+
+		/**
+		 * Test loading self defined external plugin file paths.
+		 */
+		test_addExternal : function()
+		{
+			CKEDITOR.plugins.addExternal( 'myplugin',
+			CKTester.cell.getAbsolutePath( 'plugins/myplugins/sample/' ), 'my_plugin.js' );
+
+			CKEDITOR.plugins.load( 'myplugin', function(){
+				this.resume( function(){
+
+					assert.isTrue( CKEDITOR.plugins.get( 'myplugin' ).definition );
+				} );
+			}, this );
+			this.wait();
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/plugins/myplugins/sample/my_plugin.js
===================================================================
--- /CKEditor/trunk/_test/dt/core/plugins/myplugins/sample/my_plugin.js	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/plugins/myplugins/sample/my_plugin.js	(revision 4101)
@@ -0,0 +1,3 @@
+CKEDITOR.plugins.add( 'myplugin' , {
+	definition :  true
+} );
Index: /CKEditor/trunk/_test/dt/core/scriptloader.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/scriptloader.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/scriptloader.html	(revision 4101)
@@ -0,0 +1,51 @@
+﻿<!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>CKEDITOR.scriptLoader</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_loadCode : function()
+		{
+			CKEDITOR.scriptLoader.loadCode( 'var test="Testing!";' );
+
+			/*jsl:ignore*/
+			assert.areEqual( 'Testing!', test );
+			/*jsl:end*/
+		},
+
+		test_load : function()
+		{
+			var testCase = this;
+			var callback = function( data )
+			{
+				testCase.resume( function()
+					{
+						/*jsl:ignore*/
+						assert.areSame( 'Test!', testVar );
+						/*jsl:end*/
+					} );
+			};
+
+			CKEDITOR.scriptLoader.load( '../_assets/sample.js', callback );
+			this.wait();
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/tools.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/tools.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/tools.html	(revision 4101)
@@ -0,0 +1,169 @@
+<!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>CKEDITOR.tools</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+var testcase;
+CKEDITOR.test.addTestCase( testcase = (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_extend : function()
+		{
+			var fakeFn = function() {};
+			var fakeObj = { fake1 : 1, fake2 : 2 };
+			var fakeArray = [ 'Test', 10, fakeFn, fakeObj ];
+
+			var target =
+			{
+				prop1 : 'Test',
+				prop2 : 10,
+				prop3 : fakeFn,
+				prop4 : fakeObj,
+				prop5 : fakeArray
+			};
+
+			CKEDITOR.tools.extend( target,
+				{
+					prop3 : 'Wrong',
+					prop6 : 'Good',
+					prop7 : fakeArray
+				} );
+
+			assert.areSame( 'Test'		, target.prop1, 'prop1 doesn\'t match' );
+			assert.areSame( 10			, target.prop2, 'prop2 doesn\'t match' );
+			assert.areSame( fakeFn		, target.prop3, 'prop3 doesn\'t match' );
+			assert.areSame( fakeObj		, target.prop4, 'prop4 doesn\'t match' );
+			assert.areSame( fakeArray	, target.prop5, 'prop5 doesn\'t match' );
+			assert.areSame( 'Good'		, target.prop6, 'prop6 doesn\'t match' );
+			assert.areSame( fakeArray	, target.prop7, 'prop7 doesn\'t match' );
+		},
+
+		test_isArray1 : function()
+		{
+			assert.isTrue( CKEDITOR.tools.isArray( [] ) );
+		},
+
+		test_isArray2 : function()
+		{
+			assert.isFalse( CKEDITOR.tools.isArray( { length:1 } ) );
+		},
+
+		test_isArray3 : function()
+		{
+			assert.isFalse( CKEDITOR.tools.isArray( null ) );
+		},
+
+		test_isArray4 : function()
+		{
+			assert.isFalse( CKEDITOR.tools.isArray( window.x ) );
+		},
+
+		test_htmlEncode1 : function()
+		{
+			assert.areSame( '&lt;b&gt;Test&lt;/b&gt;', CKEDITOR.tools.htmlEncode( '<b>Test</b>' ) );
+		},
+
+		test_htmlEncode2 : function()
+		{
+			assert.areSame( 'Test\'s &amp; "quote"', CKEDITOR.tools.htmlEncode( 'Test\'s & "quote"' ) );
+		},
+
+		test_htmlEncode_3874 : function()
+		{
+			assert.areSame( 'line1\nline2', CKEDITOR.tools.htmlEncode( 'line1\nline2' ) );
+		},
+
+		test_cssStyleToDomStyle1 : function()
+		{
+			assert.areSame( 'backgroundColor', CKEDITOR.tools.cssStyleToDomStyle( 'background-color' ) );
+		},
+
+		test_cssStyleToDomStyle2 : function()
+		{
+			assert.areSame( 'cssFloat', CKEDITOR.tools.cssStyleToDomStyle( 'float' ) );
+		},
+
+		test_getNextNumber : function()
+		{
+			var number = CKEDITOR.tools.getNextNumber();
+			assert.areSame( number +  1, CKEDITOR.tools.getNextNumber() );
+			assert.areSame( number +  2, CKEDITOR.tools.getNextNumber() );
+			assert.areSame( number +  3, CKEDITOR.tools.getNextNumber() );
+		},
+
+		test_trim1 : function()
+		{
+			assert.areSame( 'test', CKEDITOR.tools.trim( '    test   ' ) );
+		},
+
+		test_trim2 : function()
+		{
+			assert.areSame( 'test', CKEDITOR.tools.trim( ' \n \t  test\n  \t ' ) );
+		},
+
+		test_ltrim1 : function()
+		{
+			assert.areSame( 'test   ', CKEDITOR.tools.ltrim( '    test   ' ) );
+		},
+
+		test_ltrim2 : function()
+		{
+			assert.areSame( 'test\n  \t ', CKEDITOR.tools.ltrim( ' \n \t  test\n  \t ' ) );
+		},
+
+		test_rtrim1 : function()
+		{
+			assert.areSame( '    test', CKEDITOR.tools.rtrim( '    test   ' ) );
+		},
+
+		test_rtrim2 : function()
+		{
+			assert.areSame( ' \n \t  test', CKEDITOR.tools.rtrim( ' \n \t  test\n  \t ' ) );
+		},
+
+		test_clone : function()
+		{
+			var obj =
+			{
+				name : 'John',
+				cars :
+				{
+					Mercedes : { color : 'blue' },
+					Porsche : { color : 'red' }
+				}
+			};
+
+			var clone = CKEDITOR.tools.clone( obj );
+
+			clone.name = 'Paul';
+			clone.cars.Porsche.color = 'silver';
+
+			assert.areSame( 'John', obj.name );
+			assert.areSame( 'Paul', clone.name );
+
+			assert.areSame( 'red', obj.cars.Porsche.color );
+			assert.areSame( 'silver', clone.cars.Porsche.color );
+		},
+
+		test_repeat : function()
+		{
+			assert.areSame( '&nbsp;&nbsp;&nbsp;', CKEDITOR.tools.repeat( '&nbsp;', 3 ) );
+		},
+
+		name : document.title
+	};
+})() );
+//window.onload = testcase.test_htmlEncode_3874;
+	//]]>
+	</script>
+</head>
+<body>
+	<iframe></iframe>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/core/xml.html
===================================================================
--- /CKEditor/trunk/_test/dt/core/xml.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/core/xml.html	(revision 4101)
@@ -0,0 +1,148 @@
+<!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>CKEDITOR.xml</title>
+	<meta name="tags" content="editor,unit,stable">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.loader.load( 'core/xml' );
+
+	//]]>
+	</script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (function()
+{
+	// Local reference to the "assert" object.
+	var assert = CKEDITOR.test.assert;
+
+	return {
+		test_baseXml : function()
+		{
+			var xml = new CKEDITOR.xml( '<data />' );
+			assert.isObject( xml.baseXml );
+		},
+
+		test_selectSingleNode1a : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item /></data>' );
+			var node = xml.selectSingleNode( 'data/item' );
+			assert.areEqual( 'item', node.nodeName );
+		},
+
+		test_selectSingleNode1b : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item /></data>' );
+			var node = xml.selectSingleNode( 'item' );
+			assert.isNull( node );
+		},
+
+		test_selectSingleNode2a : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item><subitem/></item></data>' );
+			var contextNode = xml.selectSingleNode( 'data' );
+			var node = xml.selectSingleNode( 'item/subitem', contextNode );
+			assert.areEqual( 'subitem', node.nodeName );
+		},
+
+		test_selectSingleNode2b : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item><subitem/></item></data>' );
+			var contextNode = xml.selectSingleNode( 'data' );
+			var node = xml.selectSingleNode( 'subitem', contextNode );
+			assert.isNull( node );
+		},
+
+		test_selectSingleNode3 : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item id="x1"/><item id="x2"/></data>' );
+			var node = xml.selectSingleNode( '//item[@id="x2"]' );
+			assert.areEqual( 'item', node.nodeName );
+			assert.areEqual( 'x2', node.getAttribute( 'id' ) );
+		},
+
+		test_selectSingleNode4 : function()
+		{
+			// For may results, the first one must be returned.
+
+			var xml = new CKEDITOR.xml( '<data><item id="x1"/><item id="x2"/></data>' );
+			var node = xml.selectSingleNode( '//item' );
+			assert.areEqual( 'item', node.nodeName );
+			assert.areEqual( 'x1', node.getAttribute( 'id' ) );
+		},
+
+		test_selectNodes1 : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item /></data>' );
+			var nodes = xml.selectNodes( 'data/item' );
+			assert.areEqual( 1, nodes.length );
+			assert.areEqual( 'item', nodes[0].nodeName );
+		},
+
+		test_selectNodes2a : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item id="x1"/><item id="x2"/><item id="x3"/></data>' );
+			var nodes = xml.selectNodes( '//item' );
+			assert.areEqual( 3, nodes.length );
+			assert.areEqual( 'x1', nodes[0].getAttribute( 'id' ) );
+			assert.areEqual( 'x2', nodes[1].getAttribute( 'id' ) );
+			assert.areEqual( 'x3', nodes[2].getAttribute( 'id' ) );
+		},
+
+		test_selectNodes2b : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item id="x1"/><item id="x2"/><item id="x3"/></data>' );
+			var nodes = xml.selectNodes( '//itemX' );
+			assert.areEqual( 0, nodes.length );
+		},
+
+		test_selectNodes3a : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item id="x1"/><item id="x2"/><item id="x3"/></data>' );
+			var contextNode = xml.selectSingleNode( 'data' );
+			var nodes = xml.selectNodes( 'item', contextNode );
+			assert.areEqual( 3, nodes.length );
+			assert.areEqual( 'x1', nodes[0].getAttribute( 'id' ) );
+			assert.areEqual( 'x2', nodes[1].getAttribute( 'id' ) );
+			assert.areEqual( 'x3', nodes[2].getAttribute( 'id' ) );
+		},
+
+		test_selectNodes3b : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item id="x1"/><item id="x2"/><item id="x3"/></data>' );
+			var contextNode = xml.selectSingleNode( 'data' );
+			var nodes = xml.selectNodes( 'itemX', contextNode );
+			assert.areEqual( 0, nodes.length );
+		},
+
+		test_getInnerXml1 : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item/></data>' );
+			assert.areEqual( '<item/>', xml.getInnerXml( 'data' ) );
+		},
+
+		test_getInnerXml2 : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item id="x1"><subitem name="sub1"/></item></data>' );
+			assert.areEqual( '<subitem name="sub1"/>', xml.getInnerXml( '//item' ) );
+		},
+
+		test_getInnerXml3 : function()
+		{
+			var xml = new CKEDITOR.xml( '<data><item id="x1"/></data>' );
+			assert.isNull( xml.getInnerXml( '//itemX' ) );
+		},
+
+		name : document.title
+	};
+})() );
+
+	//]]>
+	</script>
+</head>
+<body>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/plugins/domiterator/domiterator.html
===================================================================
--- /CKEditor/trunk/_test/dt/plugins/domiterator/domiterator.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/plugins/domiterator/domiterator.html	(revision 4101)
@@ -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">
+	<meta name="tags" content="editor,unit,stable">
+	<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/trunk/_test/dt/plugins/htmldataprocessor/htmldataprocessor.html
===================================================================
--- /CKEditor/trunk/_test/dt/plugins/htmldataprocessor/htmldataprocessor.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/plugins/htmldataprocessor/htmldataprocessor.html	(revision 4101)
@@ -0,0 +1,385 @@
+<!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>
+	<meta name="tags" content="editor,unit,stable">
+	<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/trunk/_test/dt/plugins/jquery/jquery.html
===================================================================
--- /CKEditor/trunk/_test/dt/plugins/jquery/jquery.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/plugins/jquery/jquery.html	(revision 4101)
@@ -0,0 +1,517 @@
+<!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: jquery</title>
+	<meta name="tags" content="editor,unit,jquery">
+	<script type="text/javascript" src="../../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+// Load the required plugins and launch the runner.
+CKEDITOR.test.fort.defer = true;
+
+$( window ).bind( 'load', function()
+{
+	CKEDITOR.plugins.load( 'jquery' , function()
+	{
+		CKEDITOR.test.fort.run();
+	} );
+} );
+
+CKEDITOR.test.addTestCase( ( function()
+{
+	// Local references.
+	var assert = CKEDITOR.test.assert,
+		doc = CKEDITOR.document,
+		action = YAHOO.util.UserAction,
+		selector = YAHOO.util.Selector;
+
+	function cleanup( id )
+	{
+		var instance = jQuery( '#' + id ).ckeditorGet();
+		if ( instance )
+			instance.destroy();
+	}
+
+	// Maximum time per each test. If test will take longer, failure will be thrown.
+	var testTimeout = 10000;
+
+	jQuery( 'textarea, input' ).val( 'default' );
+
+	return {
+		test_create : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery( 'textarea#create' ).ckeditor( function( textarea )
+			{
+				var editor = this;
+				testSelf.resume( function()
+				{
+					assert.isTrue( editor instanceof CKEDITOR.editor, 'editor instanceof CKEDITOR.editor' );
+					testFinished = true;
+					cleanup( 'create' );
+				});
+			});
+			testSelf.wait( function() {
+				cleanup( 'create' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout + 5000);
+		},
+
+		test_config : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			CKEDITOR.config.test_config = false;
+			jQuery( 'textarea#config' ).ckeditor(
+				{ test_config: true },
+				function( textarea )
+				{
+					var editor = this;
+					testSelf.resume( function()
+					{
+						assert.isTrue( editor.config.test_config );
+						testFinished = true;
+						cleanup( 'config' );
+					});
+				}
+			);
+			testSelf.wait(function(){
+				cleanup( 'config' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_config_no_order : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			CKEDITOR.config.test_config_no_order = false;
+			jQuery('textarea#config_no_order').ckeditor(
+				function( textarea )
+				{
+					var editor = this;
+					testSelf.resume( function()
+					{
+						assert.isTrue( editor.config.test_config_no_order );
+						testFinished = true;
+						cleanup( 'config_no_order' );
+					});
+				},
+				{ test_config_no_order: true }
+			);
+			testSelf.wait(function(){
+				cleanup( 'config_no_order' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_config_global : function()
+		{
+			CKEDITOR.config.test_config_global = false;
+			$.ckeditorConfig( { test_config_global: true } );
+			assert.isTrue( CKEDITOR.config.test_config_global );
+		},
+
+		test_val_get_without_timeout : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery('textarea#val_get_without_timeout').ckeditor(function( textarea )
+			{
+				var editor = this;
+				editor.dataProcessor.writer._.rules = {};
+				testSelf.resume( function()
+				{
+					// editor.setData is async, which creates the test failure
+					editor.setData( 'foo bar' );
+					assert.areSame( '<p>foo bar</p>', jQuery( textarea ).val() );
+					testFinished = true;
+					cleanup( 'val_get_without_timeout' );
+				});
+			});
+			testSelf.wait( function() {
+				cleanup( 'val_get_without_timeout' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_val_get_with_timeout : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery('textarea#val_get_with_timeout').ckeditor(function( textarea )
+			{
+				var editor = this;
+				editor.dataProcessor.writer._.rules = {};
+				editor.setData( 'foo bar' );
+				setTimeout(function()
+				{
+					testSelf.resume( function()
+					{
+						assert.areSame( '<p>foo bar</p>', jQuery( textarea ).val() );
+						testFinished = true;
+						cleanup( 'val_get_with_timeout' );
+					});
+				}, 1000);
+			});
+			testSelf.wait( function() {
+				cleanup( 'val_get_with_timeout' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_val_set_with_timeout : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery('textarea#val_set_with_timeout').ckeditor(function( textarea )
+			{
+				var editor = this;
+				editor.dataProcessor.writer._.rules = {};
+				jQuery( textarea ).val( 'foo bar' );
+				setTimeout( function()
+				{
+					testSelf.resume( function()
+					{
+						assert.areSame( '<p>foo bar</p>', editor.getData() );
+						testFinished = true;
+						cleanup( 'val_set_with_timeout' );
+					});
+				}, 1000);
+			});
+			testSelf.wait( function() {
+				cleanup( 'val_set_with_timeout' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_ckeditor_get : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery('#ckeditor_get').ckeditor(function( textarea )
+			{
+				var editor = this;
+				editor.dataProcessor.writer._.rules = {};
+				editor.setData( 'foo bar' );
+				setTimeout(function()
+				{
+					testSelf.resume( function()
+					{
+						var editor = jQuery( textarea ).ckeditorGet();
+						assert.isObject( editor );
+						testFinished = true;
+						cleanup( 'ckeditor_get' );
+					});
+				// longer timeout may be needed
+				}, 1000);
+			});
+			testSelf.wait( function() {
+				cleanup( 'ckeditor_get' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_destroy : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery('textarea#destroy').ckeditor(function( textarea )
+			{
+				var editor = this;
+				try
+				{
+					editor.destroy();
+				}
+				catch (e){}
+				setTimeout(function()
+				{
+					testSelf.resume( function()
+					{
+							assert.isUndefined( CKEDITOR.instances.destroy );
+							testFinished = true;
+					});
+				}, 0);
+			});
+			testSelf.wait(function(){
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_val_input_get : function()
+		{
+			assert.areSame( 'default', jQuery( '#val_input_get' ).val() );
+		},
+
+		test_val_input_set : function()
+		{
+			jQuery( '#val_input_set' ).val( 'new' );
+			assert.areSame( 'new', jQuery('#val_input_set').get( 0 ).value );
+		},
+
+		test_submit : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			// Synchronize textarea content on form submit
+			jQuery( '#submit textarea' ).ckeditor(
+				{ jquerySyncTextareaOnSubmit: true },
+				function( textarea )
+				{
+					var editor = this;
+					editor.dataProcessor.writer._.rules = {};
+					editor.setData( 'foo bar' );
+					jQuery('#submit').submit( function()
+					{
+						testSelf.resume( function()
+						{
+							assert.areSame( '<p>foo bar</p>', editor.getData(), '1' );
+							assert.areSame( editor.getData(), jQuery( '#submit textarea' ).get( 0 ).value, '2' );
+							testFinished = true;
+							cleanup( 'submit_textarea' );
+						});
+						return false;
+					});
+					setTimeout(function()
+					{
+						jQuery( '#submit' ).submit();
+					}, 1000 );
+				}
+			);
+			testSelf.wait( function() {
+				cleanup( 'submit_textarea' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_ajax_submit : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery( '#ajax_submit textarea' ).ckeditor(
+				{ jquerySyncTextareaOnSubmit: true },
+				function( textarea )
+				{
+					var editor = this;
+					editor.dataProcessor.writer._.rules = {};
+					editor.setData( 'foo bar' );
+					jQuery('#ajax_submit').ajaxForm(
+					{
+						beforeSubmit: function( formData )
+						{
+							testSelf.resume( function()
+							{
+								assert.areSame( '<p>foo bar</p>', editor.getData(), '1' );
+								assert.areSame( editor.getData(), formData[ 0 ][ 'value' ], '2' );
+								testFinished = true;
+								cleanup( 'ajax_submit_textarea' );
+							});
+							return false;
+						}
+					});
+					setTimeout(function()
+					{
+						jQuery('#ajax_submit').submit();
+					}, 1000 );
+				});
+			testSelf.wait(function(){
+				cleanup( 'ajax_submit_textarea' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_global_event : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery( document ).bind( 'instanceReady.ckeditor', function( event, editor )
+			{
+				if ( !jQuery( event.target ).is( '#global_event' ) )
+					return;
+				jQuery( this ).unbind( 'instanceReady.ckeditor', arguments.callee );
+				testSelf.resume( function()
+				{
+					assert.isTrue( editor instanceof CKEDITOR.editor, 'editor instanceof CKEDITOR.editor' );
+					testFinished = true;
+					cleanup( 'global_event' );
+				});
+			});
+			jQuery( 'textarea#global_event' ).ckeditor();
+			testSelf.wait( function(){
+				cleanup( 'global_event' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout );
+		},
+
+		test_setData_event : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery( 'textarea#setData_event' )
+				.one( 'setData.ckeditor', function( event, editor, data )
+				{
+					testSelf.resume( function()
+					{
+//								console.log('test_setData_event', data, editor.getData());
+						assert.areSame( 'foo bar', data );
+						assert.isTrue( editor instanceof CKEDITOR.editor, 'editor instanceof CKEDITOR.editor' );
+						testFinished = true;
+						cleanup( 'setData_event' );
+					});
+				})
+				.ckeditor( function()
+				{
+					var editor = this;
+					// Delay it a bit.
+					setTimeout( function()
+					{
+						editor.setData('foo bar');
+					}, 300 );
+				});
+//					testSelf.wait();
+			testSelf.wait( function() {
+				cleanup( 'setData_event' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_div_replace : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+			jQuery( '#div_replace' ).ckeditor( function() {
+				var editor = this;
+				testSelf.resume( function()
+				{
+					assert.isTrue( editor instanceof CKEDITOR.editor, 'editor instanceof CKEDITOR.editor' );
+					testFinished = true;
+					cleanup( 'div_replace' );
+				});
+			});
+			testSelf.wait( function() {
+				cleanup( 'div_replace' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_parallel_callbacks : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+
+			var called = [ false, false ];
+			function checkCalled()
+			{
+				if ( called[ 0 ] && called[ 1 ] )
+				{
+					testSelf.resume( function()
+					{
+						var editor = jQuery( '#parallel_callbacks' ).ckeditorGet();
+						editor.dataProcessor.writer._.rules = {};
+						var content = editor.getData();
+
+						assert.isTrue( !!content.match( 'callback1' ), 'callback1' );
+						assert.isTrue( !!content.match( 'callback2' ), 'callback2' );
+						// TODO cleanup here causes checkSelectionChange and other timeouted events to fail!
+						cleanup( 'parallel_callbacks' );
+					});
+				}
+			}
+
+			function callback1()
+			{
+				this.insertElement( CKEDITOR.dom.element.createFromHtml( '<p>callback1</p>' ) );
+				called[ 0 ] = true;
+				checkCalled();
+			}
+
+			function callback2()
+			{
+				this.insertElement( CKEDITOR.dom.element.createFromHtml( '<p>callback2</p>' ) );
+				called[ 1 ] = true;
+				checkCalled();
+			}
+
+			jQuery( '#parallel_callbacks' )
+				.ckeditor( callback1 )
+				.ckeditor( callback2 );
+
+			testSelf.wait( function() {
+				cleanup( 'parallel_callbacks' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		test_series_callbacks : function()
+		{
+			var testSelf = this,
+				testFinished = false;
+
+			function callback1( element )
+			{
+				this.insertElement( CKEDITOR.dom.element.createFromHtml( '<p>callback1</p>' ) );
+				jQuery( element ).ckeditor( callback2 );
+			}
+
+			function callback2()
+			{
+				var editor = this;
+				testSelf.resume( function()
+				{
+					editor.insertElement( CKEDITOR.dom.element.createFromHtml( '<p>callback2</p>' ) );
+
+					// Assert.
+					editor.dataProcessor.writer._.rules = {};
+					var content = editor.getData();
+
+					assert.isTrue( !!content.match( 'callback1' ), 'callback1' );
+					assert.isTrue( !!content.match( 'callback2' ), 'callback2' );
+					cleanup( 'series_callbacks' );
+				} );
+			}
+
+			jQuery( '#series_callbacks' ).ckeditor( callback1 );
+
+			testSelf.wait( function() {
+				cleanup( 'series_callbacks' );
+				assert.isTrue( testFinished, 'Test not finished.' );
+			}, testTimeout);
+		},
+
+		name :document.title
+	};
+} )() );
+	</script>
+</head>
+<body>
+		<form>
+			<textarea rows="10" cols="30" id="create">default</textarea>
+			<textarea rows="10" cols="30" id="config">default</textarea>
+			<textarea rows="10" cols="30" id="config_no_order">default</textarea>
+			<textarea rows="10" cols="30" id="val_get_without_timeout">default</textarea>
+			<textarea rows="10" cols="30" id="val_get_with_timeout">default</textarea>
+			<textarea rows="10" cols="30" id="val_set_with_timeout">default</textarea>
+			<textarea rows="10" cols="30" id="ckeditor_get">default</textarea>
+			<textarea rows="10" cols="30" id="destroy">default</textarea>
+			<input id="val_input_get" value="default" />
+			<input id="val_input_set" value="default" />
+			<textarea rows="10" cols="30" id="global_event">default</textarea>
+			<textarea rows="10" cols="30" id="setData_event">default</textarea>
+			<textarea rows="10" cols="30" id="parallel_callbacks">default</textarea>
+			<textarea rows="10" cols="30" id="series_callbacks">default</textarea>
+		</form>
+		<form action="./" method="post" id="submit">
+			<textarea rows="10" cols="30" name="submit_textarea" id="submit_textarea">default</textarea>
+			<input type="submit" value="Submit" />
+		</form>
+		<form action="/" method="post" id="ajax_submit">
+			<textarea rows="10" cols="30" name="ajax_submit_textarea" id="ajax_submit_textarea">default</textarea>
+			<input type="submit" value="Submit" />
+		</form>
+		<div style="padding: 15px; background-color: lightblue;">
+			<div id="div_replace" style="width: 200px; height: 300px; background-color: black;">default</div>
+		</div>
+</body>
+</html>
Index: /CKEditor/trunk/_test/dt/plugins/link/link.html
===================================================================
--- /CKEditor/trunk/_test/dt/plugins/link/link.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/plugins/link/link.html	(revision 4101)
@@ -0,0 +1,122 @@
+<!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>
+	<meta name="tags" content="editor,unit,stable">
+	<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/trunk/_test/dt/plugins/list/list.html
===================================================================
--- /CKEditor/trunk/_test/dt/plugins/list/list.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/plugins/list/list.html	(revision 4101)
@@ -0,0 +1,216 @@
+<!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>
+	<meta name="tags" content="editor,unit,stable">
+	<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/trunk/_test/dt/plugins/selection/selection.html
===================================================================
--- /CKEditor/trunk/_test/dt/plugins/selection/selection.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/plugins/selection/selection.html	(revision 4101)
@@ -0,0 +1,48 @@
+<!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 name="tags" content="editor,unit,stable">
+	<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/trunk/_test/dt/plugins/styles/styles.html
===================================================================
--- /CKEditor/trunk/_test/dt/plugins/styles/styles.html	(revision 4101)
+++ /CKEditor/trunk/_test/dt/plugins/styles/styles.html	(revision 4101)
@@ -0,0 +1,596 @@
+<!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>
+	<meta name="tags" content="editor,unit,stable">
+	<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/trunk/_test/index.html
===================================================================
--- /CKEditor/trunk/_test/index.html	(revision 4101)
+++ /CKEditor/trunk/_test/index.html	(revision 4101)
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+		"http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>Index Page - CKEditor Tests</title>
+</head>
+<body>
+<h1>Test Runner Links</h1>
+<ul>
+	<li><a href="cktester/fort.html">Blank fort</a></li>
+	<li><a href="cktester/fort.html?cells=editor">All tests of CKEditor</a></li>
+	<li><a href="cktester/fort.html?cells=tt">All ticket tests of CKEditor</a></li>
+</ul>
+</body>
+</html>
Index: /CKEditor/trunk/_test/profile.js
===================================================================
--- /CKEditor/trunk/_test/profile.js	(revision 4101)
+++ /CKEditor/trunk/_test/profile.js	(revision 4101)
@@ -0,0 +1,71 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKTester.fort.getProfile = function ()
+{
+	// Note : All paths specified below should refer to CKTester project root are generated automatically by fort tool. 
+	return {
+		cells : 
+[
+	[ '../dt/core/ajax',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/bootstrap',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/ckeditor',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/dom/document',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/dom/documentfragment',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/dom/element',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/dom/node',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/dom/range',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/dom/text',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/dom/walker',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/dom/window',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/editor',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/env',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/event',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/htmlparser/fragment',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/htmlparser/htmlparser',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/plugins',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/scriptloader',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/tools',[ 'editor','unit','stable' ]  ],
+	[ '../dt/core/xml',[ 'editor','unit','stable' ]  ],
+	[ '../dt/plugins/domiterator/domiterator',[ 'editor','unit','stable' ]  ],
+	[ '../dt/plugins/htmldataprocessor/htmldataprocessor',[ 'editor','unit','stable' ]  ],
+	[ '../dt/plugins/jquery/jquery',[ 'editor','unit','jquery' ]  ],
+	[ '../dt/plugins/link/link',[ 'editor','unit','stable' ]  ],
+	[ '../dt/plugins/list/list',[ 'editor','unit','stable' ]  ],
+	[ '../dt/plugins/selection/selection',[ 'editor','unit','stable' ]  ],
+	[ '../dt/plugins/styles/styles',[ 'editor','unit','stable' ]  ],
+	[ '../tt/3009/3009',[ 'editor','unit','all' ]  ],
+	[ '../tt/3978/3978',[ 'editor','unit','all' ]  ],
+	[ '../tt/4048/4048',[ 'editor','unit','all' ]  ],
+	[ '../tt/4219/4219',[ 'editor','unit','all' ]  ],
+	[ '../tt/4227/4227',[ 'editor','unit','all' ]  ]
+],
+
+		cellResolvers :
+		[
+			function( cell )
+			{
+				var tags = cell.tags, env = cell.environment;
+
+				// Inject CKEditor source.
+				if( ( tags.indexOf( 'editor' ) != -1 )
+					&& ( tags.indexOf( 'editor-ondemand' ) == -1  ) )
+					env.push( '${CKEDITOR_ROOT}/ckeditor_source.js' );
+				else if ( tags.indexOf( 'editor-ondemand' ) )
+					env.push( '${CKEDITOR_ROOT}/ckeditor_basic_source.js' );
+
+				// Inject CKEditor unit test library.
+				if ( tags.indexOf( 'unit' ) != -1  )
+					env.push( '${CKEDITOR_ROOT}/_source/core/test.js' );
+
+				if ( tags.indexOf( 'jquery' ) != -1  )
+					env.push( 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js' );
+
+				if ( tags.indexOf( 'jquery-form' ) != -1  )
+					env.push( 'http://malsup.com/jquery/form/jquery.form.js' );
+			}
+		]
+	};
+};
Index: /CKEditor/trunk/_test/tt/3009/3009.html
===================================================================
--- /CKEditor/trunk/_test/tt/3009/3009.html	(revision 4101)
+++ /CKEditor/trunk/_test/tt/3009/3009.html	(revision 4101)
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+	"http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>CKEDITOR.test.suites.editorTestSuite</title>
+	<meta name="tags" content="editor,unit,all">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script>
+( function()
+{
+	var ts, tc, assert = CKEDITOR.test.assert, doc = CKEDITOR.document,
+		editorData = '<p>\n\tsample text</p>\n';
+	YAHOO.tool.TestRunner.add( ts = new CKEDITOR.test.suites.editorTestSuite(
+		{
+				startupData :  editorData,
+				editorName : 'test_editor',
+				config : { lang : 'zh-cn' },
+				name :document.title
+		} ) );
+
+	ts.add( tc = new YAHOO.tool.TestCase(
+		{
+			/**
+			 * Test the editor is fully interactive.
+			 */
+			test_editor_full_interactive : function()
+			{
+				assert.areSame( 'test_editor', ts.editor.name );
+				assert.areSame( 'zh-cn', ts.editor.config.lang );
+				assert.areSame( editorData, ts.editor.getData() );
+				assert.isTrue( ts.editor.focusManager.hasFocus );
+			},
+
+			/**
+			 * Test the editor has been reset by 'setUp'.
+			 */
+			test_editor_reset : function()
+			{
+				assert.areSame( editorData, ts.editor.getData() );
+				assert.isTrue( ts.editor.focusManager.hasFocus );
+			},
+
+			setUp : function()
+			{
+				ts.editor.loadSnapshot( editorData );
+				ts.editor.focus();
+			},
+
+			tearDown : function()
+			{
+				ts.editor.loadSnapshot( '' );
+				doc.getById( 'focusTarget' ).focus();
+			}
+			
+		} ) );
+} )();
+	</script>
+</head>
+<body>
+<input id="focusTarget" type="text"/>
+</body>
+</html>
Index: /CKEditor/trunk/_test/tt/3978/3978.html
===================================================================
--- /CKEditor/trunk/_test/tt/3978/3978.html	(revision 4101)
+++ /CKEditor/trunk/_test/tt/3978/3978.html	(revision 4101)
@@ -0,0 +1,44 @@
+<!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>Ticket: #3978</title>
+	<meta name="tags" content="editor,unit,all">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( ( function()
+	{
+
+		// Local references.
+		var assert = CKEDITOR.test.assert,
+			doc = CKEDITOR.document,
+			action = YAHOO.util.UserAction,
+			selector = YAHOO.util.Selector;
+
+		return	{
+
+			test_ticket_3978 : function()
+			{
+				var div = new CKEDITOR.dom.element( 'div' );
+				div.setStyle( 'float', 'left' );
+				assert.areSame( 'float', div.getAttribute( 'style' ).split( ':' )[ 0 ] );
+			},
+
+			name :document.title
+		};
+	} )() );
+	
+//]]>
+	</script>
+</head>
+<body>
+<textarea id="ticket_4048"><table>
+	<tbody>
+		<tr>
+			<td>table cell</td>
+		</tr>
+	</tbody>
+</table></textarea>
+</body>
+</html>
Index: /CKEditor/trunk/_test/tt/4048/4048.html
===================================================================
--- /CKEditor/trunk/_test/tt/4048/4048.html	(revision 4101)
+++ /CKEditor/trunk/_test/tt/4048/4048.html	(revision 4101)
@@ -0,0 +1,173 @@
+<!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>Ticket: #4048</title>
+	<meta name="tags" content="editor,unit,all">
+	<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_ticket_4048 : function()
+			{
+				var testSelf = this;
+				prepareEditor( 'ticket_4048', null,
+					{ enterMode : CKEDITOR.ENTER_BR, uiColor : '#FFFFFF' },
+					function( editor )
+					{
+							editor.setUiColor( '#FFF' );
+							editor.focus();
+
+							// Set range for context menu.
+							var doc = editor.document,
+								range = new CKEDITOR.dom.range( doc );
+							setRange( range, [ 0, 0, 0, 0 ], true );
+
+							// Show context menu.
+							editor.contextMenu.show(
+								editor._.cke_contents.getChild( 0 ).getFrameDocument().getBody().getChild( [ 0, 0, 0, 0 ] )
+							);
+
+							// Wait for context menu to show.
+							setTimeout( function()
+							{
+								var submenuEval = editor.contextMenu._.menu._.element.getChild( [ 0, 4, 0 ] ).getAttribute( 'onclick' );
+								submenuEval = submenuEval.split( ';' )[ 0 ];
+
+								// Show context submenu.
+								eval( submenuEval );
+
+								// Wait for context submenu to show.
+								setTimeout( function()
+								{
+									// Get submenu background color.
+									var backgroundBefore = editor.contextMenu._.menu._.subMenu._.element.getChild( [ 0, 0, 0, 0 ] )
+										.getComputedStyle( 'background-color' );
+
+									// Change UI color and give it some time to propagate.
+									editor.setUiColor( '#000' );
+									setTimeout( function()
+									{
+										testSelf.resume( function()
+										{
+											// Get submenu background color (the new one).
+											var backgroundAfter = editor.contextMenu._.menu._.subMenu._.element.getChild( [ 0, 0, 0, 0 ] )
+												.getComputedStyle( 'background-color' );
+											
+											assert.areSame( 'rgb(255,255,255)', backgroundBefore.toLowerCase().replace( / /g, '' ),
+												'Before UI color change' );
+
+											assert.areSame( 'rgb(0,0,0)', backgroundAfter.toLowerCase().replace( / /g, '' ),
+												'After UI color change' );
+										});
+									}, 200);
+								}, 200);
+							}, 1000);
+					}, this );
+				testSelf.wait( function() {
+					assert.isTrue( false, "Timed out." );
+				}, 8000);
+			},
+
+			name :document.title
+		};
+	} )() );
+	//]]>
+	</script>
+</head>
+<body>
+<textarea id="ticket_4048"><table>
+	<tbody>
+		<tr>
+			<td>table cell</td>
+		</tr>
+	</tbody>
+</table></textarea>
+</body>
+</html>
Index: /CKEditor/trunk/_test/tt/4219/4219.html
===================================================================
--- /CKEditor/trunk/_test/tt/4219/4219.html	(revision 4101)
+++ /CKEditor/trunk/_test/tt/4219/4219.html	(revision 4101)
@@ -0,0 +1,93 @@
+<!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>Ticket: #4219</title>
+	<meta name="tags" content="editor,unit,all">
+	<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;
+
+		return	{
+
+
+			test_ticket_4219_2 : function()
+			{
+				prepareEditor( 'ticket_4219_2', null,
+					{ language : 'unsupported' },
+					function( editor )
+					{
+						this.resume( function()
+						{
+							debugger;
+							assert.areSame( editor.config.defaultLanguage, editor.langCode );
+						} );
+					}, this );
+				this.wait();
+			},
+
+			name :document.title
+		};
+	} )() );
+	//]]>
+	</script>
+</head>
+<body>
+<textarea id="ticket_4219_1"></textarea>
+<textarea id="ticket_4219_2"></textarea>
+</body>
+</html>
Index: /CKEditor/trunk/_test/tt/4227/4227.html
===================================================================
--- /CKEditor/trunk/_test/tt/4227/4227.html	(revision 4101)
+++ /CKEditor/trunk/_test/tt/4227/4227.html	(revision 4101)
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+	"http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>Ticket: #4227</title>
+	<meta name="tags" content="editor,unit,all">
+	<script type="text/javascript" src="../../cktester/cell.js"></script>
+	<script>
+CKEDITOR.test.addTestCase( {
+	/**
+	 * Test destroying an editor instance created from textarea which is not inside a form. 
+	 */
+	test_destroy : function()
+	{
+		var self = this;
+
+		CKEDITOR.replace( 'editor1',
+			{
+				on :
+				{
+					instanceReady : function()
+					{
+						self.resume( function()
+						{
+							CKEDITOR.instances.editor1.destroy();
+						} );
+					}
+				}
+			} );
+
+		this.wait();
+	}
+} );
+	</script>
+</head>
+<body>
+<textarea id="editor1"></textarea>
+</body>
+</html>
Index: /CKEditor/trunk/_test/variables.js
===================================================================
--- /CKEditor/trunk/_test/variables.js	(revision 4101)
+++ /CKEditor/trunk/_test/variables.js	(revision 4101)
@@ -0,0 +1,14 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview
+ * This file is intended to provide customized local definition for developers,
+ * so it should be ignored by SVN. 
+ */
+CKTester.fort.variables =
+{
+	'CKEDITOR_ROOT' : '../../../CKEditor-V3'
+};
