Index: /CKEditor/branches/prototype/_source/core/dom/element.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/element.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/core/dom/element.js	(revision 2616)
@@ -156,4 +156,6 @@
 		 * @param {CKEDITOR.dom.node|String} node The node or element name to be
 		 *		appended.
+		 * @param {Boolean} [toStart] Indicates that the element is to be
+		 *		appended at the start.
 		 * @returns {CKEDITOR.dom.node} The appended node.
 		 * @example
@@ -167,10 +169,14 @@
 		 * // result: "&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;"
 		 */
-		append : function( node )
+		append : function( node, toStart )
 		{
 			if ( typeof node == 'string' )
 				node = new CKEDITOR.dom.element( node );
 
-			this.$.appendChild( node.$ );
+			if ( toStart )
+				this.$.insertBefore( node.$, this.$.firstChild );
+			else
+				this.$.appendChild( node.$ );
+
 			return node;
 		},
@@ -344,22 +350,8 @@
 
 		/**
-		 * Gets the document containing this element.
-		 * @returns {CKEDITOR.dom.document} The document.
-		 * @example
-		 * var element = CKEDITOR.document.getById( 'example' );
-		 * alert( <b>element.getDocument().equals( CKEDITOR.document )</b> );  // "true"
-		 */
-		getDocument : function()
-		{
-			var document = new CKEDITOR.dom.document( this.$.ownerDocument );
-
-			return (
-			/** @ignore */
-			this.getDocument = function()
-				{
-					return document;
-				})();
-		},
-
+		 * Gets the DTD entries for this element.
+		 * @returns {Object} An object containing the list of elements accepted
+		 *		by this element.
+		 */
 		getDtd : function()
 		{
@@ -372,4 +364,9 @@
 
 			return dtd;
+		},
+
+		getElementsByTag : function( tagName )
+		{
+			return new CKEDITOR.dom.nodeList( this.$.getElementsByTagName( tagName ) );
 		},
 
@@ -563,4 +560,54 @@
 			return false;
 		},
+
+		/**
+		 * Indicates that the element has defined attributes.
+		 * @returns {Boolean} True if the element has attributes.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '<div title="Test">Example</div>' );
+		 * alert( <b>element.hasAttributes()</b> );  "true"
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '<div>Example</div>' );
+		 * alert( <b>element.hasAttributes()</b> );  "false"
+		 */
+		hasAttributes :
+			CKEDITOR.env.ie ?
+				function()
+				{
+					var attributes = this.$.attributes;
+
+					for ( var i = 0 ; i < attributes.length ; i++ )
+					{
+						var attribute = attributes[i];
+
+						switch ( attribute.nodeName )
+						{
+							case 'class' :
+								// IE has a strange bug. If calling removeAttribute('className'),
+								// the attributes collection will still contain the "class"
+								// attribute, which will be marked as "specified", even if the
+								// outerHTML of the element is not displaying the class attribute.
+								// Note : I was not able to reproduce it outside the editor,
+								// but I've faced it while working on the TC of #1391.
+								if ( this.$.className.length > 0 )
+									return true;
+
+							// Attributes to be ignored.
+							case '_cke_expando' :
+								continue;
+
+							default :
+								if ( attribute.specified )
+									return true;
+						}
+					}
+
+					return false;
+				}
+			:
+				function()
+				{
+					return this.$.attributes.length > 0;
+				},
 
 		/**
@@ -705,4 +752,20 @@
 				return standard;
 		})(),
+
+		/**
+		 * Removes a style from the element.
+		 * @param {String} name The style name.
+		 * @function
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '<div style="display:none"></div>' );
+		 * element.removeStyle( 'display' );
+		 */
+		removeStyle : function( name )
+		{
+			this.setStyle( name, '' );
+
+			if ( !this.$.style.cssText )
+				this.removeAttribute( 'style' );
+		},
 
 		/**
Index: /CKEditor/branches/prototype/_source/core/dom/node.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/node.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/core/dom/node.js	(revision 2616)
@@ -81,4 +81,11 @@
 CKEDITOR.NODE_DOCUMENT_FRAGMENT = 11;
 
+CKEDITOR.POSITION_IDENTICAL = 0;
+CKEDITOR.POSITION_DISCONNECTED = 1;
+CKEDITOR.POSITION_FOLLOWING = 2;
+CKEDITOR.POSITION_PRECEDING = 4;
+CKEDITOR.POSITION_IS_CONTAINED = 8;
+CKEDITOR.POSITION_CONTAINS = 16;
+
 CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype,
 	/** @lends CKEDITOR.dom.node.prototype */
@@ -96,7 +103,7 @@
 		 * // result: "&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;"
 		 */
-		appendTo : function( element )
-		{
-			element.append( this );
+		appendTo : function( element, toStart )
+		{
+			element.append( this, toStart );
 			return element;
 		},
@@ -105,4 +112,14 @@
 		{
 			return new CKEDITOR.dom.node( this.$.cloneNode( includeChildren ) );
+		},
+
+		hasPrevious : function()
+		{
+			return !!this.$.previousSibling;
+		},
+
+		hasNext : function()
+		{
+			return !!this.$.nextSibling;
 		},
 
@@ -175,4 +192,23 @@
 		},
 
+		/**
+		 * Gets the document containing this element.
+		 * @returns {CKEDITOR.dom.document} The document.
+		 * @example
+		 * var element = CKEDITOR.document.getById( 'example' );
+		 * alert( <b>element.getDocument().equals( CKEDITOR.document )</b> );  // "true"
+		 */
+		getDocument : function()
+		{
+			var document = new CKEDITOR.dom.document( this.$.ownerDocument || this.$.parentNode.ownerDocument );
+
+			return (
+			/** @ignore */
+			this.getDocument = function()
+				{
+					return document;
+				})();
+		},
+
 		getIndex : function()
 		{
@@ -203,4 +239,20 @@
 			var next = this.$.nextSibling;
 			return next ? new CKEDITOR.dom.node( next ) : null;
+		},
+
+		getNextSourceNode : function( startFromSibling )
+		{
+			var $ = this.$;
+
+			var node = ( !startFromSibling && $.firstChild ) ?
+				$.firstChild :
+				$.nextSibling;
+
+			var parent;
+
+			while ( !node && ( parent = ( parent || $ ).parentNode ) )
+				node = parent.nextSibling;
+
+			return new CKEDITOR.dom.node( node );
 		},
 
@@ -222,5 +274,5 @@
 		{
 			var parent = this.$.parentNode;
-			return ( parent && parent.nodeType == 1 ) ? new CKEDITOR.dom.element( parent ) : null;
+			return ( parent && parent.nodeType == 1 ) ? new CKEDITOR.dom.node( parent ) : null;
 		},
 
@@ -239,4 +291,61 @@
 		},
 
+		getPosition : function( otherNode )
+		{
+			var $ = this.$;
+			var $other = otherNode.$;
+
+			if ( $.compareDocumentPosition )
+				return $.compareDocumentPosition( $other );
+
+			// IE and Safari have no support for compareDocumentPosition.
+
+			if ( $ == $other )
+				return CKEDITOR.POSITION_IDENTICAL;
+
+			// Handle non element nodes (don't support contains nor sourceIndex).
+			if ( this.type != CKEDITOR.NODE_ELEMENT || otherNode.type != CKEDITOR.NODE_ELEMENT )
+			{
+				if ( $.parentNode == $other )
+					return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
+				else if ( $other.parentNode == $ )
+					return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
+				else if ( $.parentNode == $other.parentNode )
+					return this.getIndex() < otherNode.getIndex() ? CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
+				else
+				{
+					$ = $.parentNode;
+					$other = $other.parentNode;
+				}
+			}
+
+			if ( $.contains( $other ) )
+				return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
+
+			if ( $other.contains( $ ) )
+				return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
+
+			if ( 'sourceIndex' in $ )
+			{
+				return ( $.sourceIndex < 0 || $other.sourceIndex < 0 ) ? CKEDITOR.POSITION_DISCONNECTED :
+					( $.sourceIndex < $other.sourceIndex ) ? CKEDITOR.POSITION_PRECEDING :
+					CKEDITOR.POSITION_FOLLOWING;
+			}
+
+			// WebKit has no support for sourceIndex.
+
+			var doc = this.getDocument().$;
+
+			var range1 = doc.createRange();
+			var range2 = doc.createRange();
+
+			range1.selectNode( $ );
+			range2.selectNode( $other );
+
+			return range1.compareBoundaryPoints( 1, range2 ) > 0 ?
+				CKEDITOR.POSITION_FOLLOWING :
+				CKEDITOR.POSITION_PRECEDING;
+		},
+
 		getAscendant : function( name )
 		{
@@ -250,13 +359,38 @@
 		},
 
+		move : function( target, toStart )
+		{
+			target.append( this.remove(), toStart );
+		},
+
 		/**
 		 * Removes this node from the document DOM.
+		 * @param {Boolean} [preserveChildren] Indicates that the children
+		 *		elements must remain in the document, removing only the outer
+		 *		tags.
 		 * @example
 		 * var element = CKEDITOR.dom.element.getById( 'MyElement' );
 		 * <b>element.remove()</b>;
 		 */
-		remove : function()
-		{
-			this.$.parentNode.removeChild( this.$ );
+		remove : function( preserveChildren )
+		{
+			var $ = this.$;
+			var parent = $.parentNode;
+
+			if ( parent )
+			{
+				if ( preserveChildren )
+				{
+					// Move all children before the node.
+					for ( var child ; ( child = $.firstChild ) ; )
+					{
+						parent.insertBefore( $.removeChild( child ), $ );
+					}
+				}
+
+				parent.removeChild( $ );
+			}
+
+			return this;
 		}
 	}
Index: /CKEditor/branches/prototype/_source/core/dom/nodelist.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/nodelist.js	(revision 2616)
+++ /CKEditor/branches/prototype/_source/core/dom/nodelist.js	(revision 2616)
@@ -0,0 +1,38 @@
+﻿/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+CKEDITOR.dom.nodeList = function( nativeList )
+{
+	this.$ = nativeList;
+};
+
+CKEDITOR.dom.nodeList.prototype =
+{
+	count : function()
+	{
+		return this.$.length;
+	},
+
+	getItem : function( index )
+	{
+		return new CKEDITOR.dom.node( this.$[ index ] );
+	}
+};
Index: /CKEditor/branches/prototype/_source/core/dom/range.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/range.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/core/dom/range.js	(revision 2616)
@@ -36,5 +36,9 @@
 	var updateCollapsed = function( range )
 	{
-      range.collapsed = ( range.startContainer.equals( range.endContainer ) && range.startOffset == range.endOffset );
+		range.collapsed = (
+			range.startContainer &&
+			range.endContainer &&
+			range.startContainer.equals( range.endContainer ) &&
+			range.startOffset == range.endOffset );
 	};
 
@@ -343,5 +347,5 @@
 			startNode = this.document.createElement( 'span' );
 			startNode.setAttribute( '_fck_bookmark', 1 );
-			startNode.setStyle( 'display', 'display' );
+			startNode.setStyle( 'display', 'none' );
 
 			// For IE, it must have something inside, otherwise it may be
@@ -353,4 +357,5 @@
 			{
 				endNode = startNode.clone();
+				endNode.setHtml( '&nbsp;' );
 
 				clone = this.clone();
@@ -366,6 +371,6 @@
 			if ( endNode )
 			{
-				this.setStart( startNode, CKEDITOR.POSITION_AFTER_END );
-				this.setEnd( endNode, CKEDITOR.POSITION_BEFORE_START );
+				this.setStartAfter( startNode );
+				this.setEndBefore( endNode );
 			}
 			else
@@ -378,4 +383,13 @@
 		},
 
+		moveToBookmark : function( bookmark )
+		{
+			this.setStartBefore( bookmark.startNode );
+			bookmark.startNode.remove();
+
+			this.setEndBefore( bookmark.endNode );
+			bookmark.endNode.remove();
+		},
+
 		getCommonAncestor : function()
 		{
@@ -410,5 +424,5 @@
 			var endOffset = this.endOffset;
 
-			if ( !ignoreStart && startContainer.type == CKEDITOR.NODE_TEXT )
+			if ( !ignoreStart && startContainer && startContainer.type == CKEDITOR.NODE_TEXT )
 			{
 				// If the offset is zero, we just insert the new node before
@@ -445,5 +459,5 @@
 			}
 
-			if ( !ignoreEnd && !this.collapsed && endContainer.type == CKEDITOR.NODE_TEXT )
+			if ( !ignoreEnd && endContainer && !this.collapsed && endContainer.type == CKEDITOR.NODE_TEXT )
 			{
 				// If the offset is zero, we just insert the new node before
@@ -487,8 +501,9 @@
 					var commonAncestor = this.getCommonAncestor();
 
-					// 1. For each boundary:
+					var body = this.document.getBody();
+
+					// For each boundary
 					//		a. Depending on its position, find out the first node to be checked (a sibling) or, if not available, to be enlarge.
 					//		b. Go ahead checking siblings and enlarging the boundary as much as possible until the common ancestor is not reached. After reaching the common ancestor, just save the enlargeable node to be used later.
-					// 2.
 
 					var startTop, endTop;
@@ -550,4 +565,7 @@
 								commonReached = true;
 
+							if ( !body.contains( enlargeable ) )
+								break;
+
 							// If we don't need space or this element breaks
 							// the line, then enlarge it.
@@ -582,8 +600,8 @@
 								siblingText = sibling.getText();
 
-								if ( /[^\s]/.test( siblingText ) )
+								if ( /[^\s\ufeff]/.test( siblingText ) )
 									sibling = null;
 
-								isWhiteSpace = /\s$/.test( siblingText );
+								isWhiteSpace = /[\s\ufeff]$/.test( siblingText );
 							}
 							else
@@ -599,7 +617,7 @@
 										// It must contains spaces and inline elements only.
 
-										var siblingText = sibling.getText();
-
-										if ( !(/[^\s]/).test( siblingText ) )
+										siblingText = sibling.getText();
+
+										if ( !(/[^\s\ufeff]/).test( siblingText ) )	// Spaces + Zero Width No-Break Space (U+FEFF)
 											sibling = null;
 										else
@@ -633,5 +651,5 @@
 									if ( commonReached )
 										startTop = enlargeable;
-									else
+									else if ( enlargeable )
 										this.setStartBefore( enlargeable );
 								}
@@ -700,5 +718,6 @@
 					else
 					{
-						if ( offset )
+						// Get the node right after the boudary to be checked
+						// first.
 						sibling = container.getChild( offset );
 
@@ -714,4 +733,7 @@
 								commonReached = true;
 
+							if ( !body.contains( enlargeable ) )
+								break;
+
 							if ( !needsWhiteSpace || enlargeable.getComputedStyle( 'display' ) != 'inline' )
 							{
@@ -720,5 +742,5 @@
 								if ( commonReached )
 									endTop = enlargeable;
-								else
+								else if ( enlargeable )
 									this.setEndAfter( enlargeable );
 							}
@@ -735,8 +757,8 @@
 								siblingText = sibling.getText();
 
-								if ( /[^\s]/.test( siblingText ) )
+								if ( /[^\s\ufeff]/.test( siblingText ) )
 									sibling = null;
 
-								isWhiteSpace = /^\s/.test( siblingText );
+								isWhiteSpace = /^[\s\ufeff]/.test( siblingText );
 							}
 							else
@@ -752,12 +774,12 @@
 										// It must contains spaces and inline elements only.
 
-										var siblingText = sibling.getText();
-
-										if ( !(/[^\s]/).test( siblingText ) )
+										siblingText = sibling.getText();
+
+										if ( !(/[^\s\ufeff]/).test( siblingText ) )
 											sibling = null;
 										else
 										{
-											var allChildren = sibling.$.all || sibling.$.getElementsByTagName( '*' );
-											for ( var i = 0, child ; child = allChildren[ i++ ] ; )
+											allChildren = sibling.$.all || sibling.$.getElementsByTagName( '*' );
+											for ( i = 0 ; child = allChildren[ i++ ] ; )
 											{
 												if ( !CKEDITOR.dtd.$removeEmpty[ child.nodeName.toLowerCase() ] )
@@ -790,5 +812,5 @@
 							if ( sibling )
 							{
-								var next = sibling.getNext();
+								next = sibling.getNext();
 
 								if ( !enlargeable && !next )
Index: /CKEditor/branches/prototype/_source/core/dtd.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dtd.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/core/dtd.js	(revision 2616)
@@ -139,5 +139,5 @@
         form : X(A,D,E,I),
         select : {optgroup:1,option:1},
-        font : J,		// Changed from L to J (see (1))
+        font : L,
         ins : P,
         menu : Q,
@@ -152,22 +152,22 @@
         input : {},
         iframe : P,
-        strong : J,		// Changed from L to J (see (1))
+        strong : L,
         textarea : N,
         noframes : P,
-        big : J,		// Changed from L to J (see (1))
-        small : J,		// Changed from L to J (see (1))
-        span : J,		// Changed from L to J (see (1))
+        big : L,
+        small : L,
+        span : L,
         hr : {},
         dt : L,
-        sub : J,		// Changed from L to J (see (1))
+        sub : L,
         optgroup : {option:1},
         param : {},
         bdo : L,
-        'var' : J,		// Changed from L to J (see (1))
+        'var' : L,
         div : P,
         object : O,
-        sup : J,		// Changed from L to J (see (1))
+        sup : L,
         dd : P,
-        strike : J,		// Changed from L to J (see (1))
+        strike : L,
         area : {},
         dir : Q,
@@ -181,31 +181,22 @@
         ul : Q,
         acronym : L,
-        b : J,			// Changed from L to J (see (1))
+        b : L,
         a : J,
         blockquote : P,
         caption : L,
-        i : J,			// Changed from L to J (see (1))
-        u : J,			// Changed from L to J (see (1))
+        i : L,
+        u : L,
         tbody : M,
         s : L,
         address : X(D,I),
-        tt : J,			// Changed from L to J (see (1))
+        tt : L,
         legend : L,
         q : L,
         pre : X(G,C),
         p : L,
-        em : J,			// Changed from L to J (see (1))
+        em : L,
         dfn : L
     };
 })();
 
-/*
-	Notes:
-	(1) According to the DTD, many elements, like <b> accept <a> elements
-	    inside of them. But, to produce better output results, we have manually
-	    changed the map to avoid breaking the links on pieces, having
-	    "<b>this is a </b><a><b>link</b> test</a>", instead of
-	    "<b>this is a <a>link</a></b><a> test</a>".
-*/
-
 // PACKAGER_RENAME( CKEDITOR.dtd )
Index: /CKEditor/branches/prototype/_source/core/loader.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/loader.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/core/loader.js	(revision 2616)
@@ -49,7 +49,8 @@
 			'core/dom/document'		: [ 'core/dom','core/dom/element', 'core/dom/domobject', 'core/dom/window' ],
 			'core/dom/documentfragment'	: [ 'core/dom/element' ],
-			'core/dom/element'		: [ 'core/dom', 'core/dom/document', 'core/dom/domobject', 'core/dom/node', 'core/tools' ],
+			'core/dom/element'		: [ 'core/dom', 'core/dom/document', 'core/dom/domobject', 'core/dom/node', 'core/dom/nodelist', 'core/tools' ],
 			'core/dom/event'		: [],
 			'core/dom/node'			: [ 'core/dom/domobject', 'core/tools' ],
+			'core/dom/nodelist'		: [ 'core/dom/node' ],
 			'core/dom/domobject'	: [ 'core/dom/event' ],
 			'core/dom/range'		: [ 'core/dom/document', 'core/dom/documentfragment', 'core/dom/element' ],
Index: /CKEditor/branches/prototype/_source/core/plugins.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/plugins.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/core/plugins.js	(revision 2616)
@@ -65,5 +65,5 @@
 						if ( requiredPlugins.length )
 							loadPlugins.call( this, requiredPlugins );
-						else
+						else if ( callback )
 							callback.call( scope || window, allPlugins );
 					}
Index: /CKEditor/branches/prototype/_source/core/test.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/test.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/core/test.js	(revision 2616)
@@ -69,5 +69,5 @@
 		YAHOO.tool.TestRunner.add( new YAHOO.tool.TestCase( testCase ) );
 	},
-	
+
 	/**
 	 * Gets the inner HTML of an element, for testing purposes.
@@ -77,6 +77,55 @@
 		var html = ( elementOrId.nodeType ? elementOrId : document.getElementById( elementOrId ) ).innerHTML;
 		html = html.toLowerCase();
-		html = html.replace( /["\n\r]/g, '' );
-		html = html.replace( /\s+_cke_expando=\d+/g, '' );
+		html = html.replace( /[\n\r]/g, '' );
+
+		html = html.replace( /<\w[^>]*/g, function( match )
+			{
+				var attribs = [];
+				var hasClass;
+
+				match = match.replace( /\s([^\s=]+)=((?:"[^"]*")|(?:'[^']*')|(?:[^\s]+))/g, function( match, attName, attValue )
+					{
+						if ( attName == 'style' )
+						{
+							// IE doesn't add the final ";"
+							attValue = attValue.replace( /([^"';\s])\s*(["']?)$/, '$1;$2' );
+
+							// Safari adds some extra space to the end.
+							attValue = attValue.replace( /\s+(["']?)$/, '$1' );
+						}
+
+						// IE may have 'class' more than once.
+						if ( attName == 'class' )
+						{
+							if ( hasClass )
+								return '';
+
+							hasClass = true;
+						}
+
+						if ( attName != '_cke_expando' )
+							attribs.push( [ attName, attValue ] );
+
+						return '';
+					} );
+
+				attribs.sort( function( a, b )
+					{
+						var nameA = a[ 0 ];
+						var nameB = b[ 0 ];
+						return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
+					} );
+
+				var ret = match.replace( /\s{2,}/g, ' ' );
+
+				for ( var i = 0 ; i < attribs.length ; i++ )
+				{
+					ret += ' ' + attribs[i][0] + '=';
+					ret += (/^["']/).test( attribs[i][1] ) ? attribs[i][1] : '"' + attribs[i][1] + '"';
+				}
+
+				return ret;
+			} );
+
 		return html;
 	}
Index: /CKEditor/branches/prototype/_source/plugins/basicstyles/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/basicstyles/plugin.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/plugins/basicstyles/plugin.js	(revision 2616)
@@ -22,55 +22,41 @@
 CKEDITOR.plugins.add( 'basicstyles',
 {
+	requires : [ 'styles' ],
+
 	init : function( editor, pluginPath )
 	{
-		var basicstyles = CKEDITOR.plugins.basicstyles,
-			commands = basicstyles.commands,
-			ui = basicstyles.ui;
+		// All buttons use the same code to register. So, to avoid
+		// duplications, let's use this tool function.
+		var addButtonCommand = function( buttonName, buttonLabel, commandName, styleDefiniton )
+		{
+			editor.addCommand( commandName, new CKEDITOR.styleCommand( styleDefiniton ) );
 
-		editor.addCommand( 'bold', commands.bold );
-		editor.addCommand( 'italic', commands.italic );
+			editor.ui.addButton( buttonName,
+				{
+					label : buttonLabel,
+					command : commandName
+				});
+		};
 
-		editor.ui.addButton( 'Bold',
-			{
-				label : editor.lang.bold,
-				command : 'bold'
-			});
-		editor.ui.addButton( 'Italic',
-			{
-				label : editor.lang.italic,
-				command : 'italic'
-			});
+		var coreStyles = editor.config.coreStyles;
+		var lang = editor.lang;
+
+		addButtonCommand( 'Bold'		, lang.bold			, 'bold'		, coreStyles.bold );
+		addButtonCommand( 'Italic'		, lang.italic		, 'italic'		, coreStyles.italic );
+		addButtonCommand( 'Underline'	, lang.underline	, 'underline'	, coreStyles.underline );
+		addButtonCommand( 'Strike'		, lang.strike		, 'strike'		, coreStyles.strike );
+		addButtonCommand( 'Subscript'	, lang.subscript	, 'subscript'	, coreStyles.subscript );
+		addButtonCommand( 'Superscript'	, lang.superscript	, 'superscript'	, coreStyles.superscript );
 	}
 });
 
-CKEDITOR.plugins.basicstyles =
+CKEDITOR.config.coreStyles =
 {
-	commands :
-	{
-		bold :
-		{
-			exec : function( editor )
-			{
-				editor.focus();
-
-				var doc = editor.document;
-				if ( doc )
-					return doc.$.execCommand( 'bold', false, null );
-				return false;
-			}
-		},
-
-		italic :
-		{
-			exec : function( editor )
-			{
-				editor.focus();
-
-				var doc = editor.document;
-				if ( doc )
-					return doc.$.execCommand( 'italic', false, null );
-				return false;
-			}
-		}
-	}
+	// Basic Inline Styles.
+	bold			: { element : 'strong', overrides : 'b' },
+	italic			: { element : 'em', overrides : 'i' },
+	underline		: { element : 'u' },
+	strike			: { element : 'strike' },
+	subscript		: { element : 'sub' },
+	superscript		: { element : 'sup' }
 };
Index: /CKEditor/branches/prototype/_source/plugins/elementspath/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/elementspath/plugin.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/plugins/elementspath/plugin.js	(revision 2616)
@@ -67,9 +67,11 @@
 				});
 
-			editor.on( 'selectionChange', function()
+			editor.on( 'selectionChange', function( ev )
 				{
 					var env = CKEDITOR.env;
 
-					var element = this.getSelection().getStartElement(),
+					var selection = ev.data.selection;
+
+					var element = selection.getStartElement(),
 						html = [],
 						elementsList = this._.elementsPath.list = [];
Index: /CKEditor/branches/prototype/_source/plugins/selection/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/selection/plugin.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/plugins/selection/plugin.js	(revision 2616)
@@ -36,6 +36,8 @@
 			return;
 
+		sel.normalize();
+
 		// Get the element at the start of the selection.
-		var node = this.getSelection().getStartElement(),
+		var node = sel.getStartElement(),
 			changed,
 			currentPath = [],
@@ -56,5 +58,5 @@
 
 		if ( changed )
-			this.fire( 'selectionChange' );
+			this.fire( 'selectionChange', { selection : sel } );
 	};
 
@@ -181,4 +183,8 @@
 {
 	this.document = document;
+	this._ =
+	{
+		cache : {}
+	};
 };
 
@@ -200,10 +206,10 @@
 				function()
 				{
-					return this.document.$.selection;
+					return this._.cache.nativeSel || ( this._.cache.nativeSel = this.document.$.selection );
 				}
 			:
 				function()
 				{
-					return this.document.getWindow().$.getSelection();
+					return this._.cache.nativeSel || ( this._.cache.nativeSel = this.document.getWindow().$.getSelection() );
 				},
 
@@ -230,4 +236,9 @@
 				function()
 				{
+					if ( this._.cache.type )
+						return this._.cache.type;
+
+					var type = CKEDITOR.SELECTION_NONE;
+
 					try
 					{
@@ -236,8 +247,8 @@
 
 						if ( ieType == 'Text' )
-							return CKEDITOR.SELECTION_TEXT;
+							type = CKEDITOR.SELECTION_TEXT;
 
 						if ( ieType == 'Control' )
-							return CKEDITOR.SELECTION_ELEMENT;
+							type = CKEDITOR.SELECTION_ELEMENT;
 
 						// It is possible that we can still get a text range
@@ -246,18 +257,23 @@
 						// createRange() rather than by looking at the type.
 						if ( sel.createRange().parentElement )
-							return CKEDITOR.SELECTION_TEXT;
+							type = CKEDITOR.SELECTION_TEXT;
 					}
 					catch(e) {}
 
-					return CKEDITOR.SELECTION_NONE;
+					return ( this._.cache.type = type );
 				}
 			:
 				function()
 				{
+					if ( this._.cache.type )
+						return this._.cache.type;
+
+					var type = CKEDITOR.SELECTION_TEXT;
+
 					var sel = this.getNative();
+
 					if ( !sel )
-						return CKEDITOR.SELECTION_NONE;
-
-					if ( sel.rangeCount == 1 )
+						type = CKEDITOR.SELECTION_NONE;
+					else if ( sel.rangeCount == 1 )
 					{
 						// Check if the actual selection is a control (IMG,
@@ -272,9 +288,133 @@
 							&& styleObjectElements[ startContainer.childNodes[ range.startOffset ].nodeName.toLowerCase() ] )
 						{
-							return CKEDITOR.SELECTION_ELEMENT;
+							type = CKEDITOR.SELECTION_ELEMENT;
 						}
 					}
 
-					return CKEDITOR.SELECTION_TEXT;
+					return ( this._.cache.type = type );
+				},
+
+		getRanges :
+			CKEDITOR.env.ie ?
+				( function()
+				{
+					// Finds the container and offset for a specific boundary
+					// of an IE range.
+					var getBoundaryInformation = function( range, start )
+					{
+						// Creates a collapsed range at the requested boundary.
+						range = range.duplicate();
+						range.collapse( start );
+
+						// Gets the element that encloses the range entirely.
+						var parent = range.parentElement();
+						var siblings = parent.childNodes;
+
+						var testRange;
+
+						for ( var i = 0 ; i < siblings.length ; i++ )
+						{
+							var child = siblings[ i ];
+							if ( child.nodeType == 1 )
+							{
+								testRange = range.duplicate();
+
+								testRange.moveToElementText( child );
+								testRange.collapse();
+
+								var comparison = testRange.compareEndPoints( 'StartToStart', range );
+
+								if ( comparison > 0 )
+									break;
+								else if ( comparison === 0 )
+									return {
+										container : parent,
+										offset : i
+									};
+
+								testRange = null;
+							}
+						}
+
+						if ( !testRange )
+						{
+							testRange = range.duplicate();
+							testRange.moveToElementText( parent );
+							testRange.collapse( false );
+						}
+
+						testRange.setEndPoint( 'StartToStart', range );
+						var distance = testRange.text.length;
+
+						while ( distance > 0 )
+							distance -= siblings[ --i ].nodeValue.length;
+
+						if ( distance === 0 )
+						{
+							return {
+								container : parent,
+								offset : i
+							};
+						}
+						else
+						{
+							return {
+								container : siblings[ i ],
+								offset : -distance
+							};
+						}
+					};
+
+					return function()
+					{
+						if ( this._.cache.ranges )
+							return this._.cache.ranges;
+
+						// IE doesn't have range support (in the W3C way), so we
+						// need to do some magic to transform selections into
+						// CKEDITOR.dom.range instances.
+
+						var range = new CKEDITOR.dom.range( this.document );
+
+						var sel = this.getNative();
+
+						if ( this.getType() == CKEDITOR.SELECTION_TEXT )
+						{
+							var nativeRange = sel.createRange();
+
+							var boundaryInfo = getBoundaryInformation( nativeRange, true );
+							range.setStart( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset );
+
+							boundaryInfo = getBoundaryInformation( nativeRange );
+							range.setEnd( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset );
+						}
+
+						return ( this._.cache.ranges = [ range ] );
+					}
+				})()
+			:
+				function()
+				{
+					if ( this._.cache.ranges )
+						return this._.cache.ranges;
+
+					// On browsers implementing the W3C range, we simply
+					// tranform the native ranges in CKEDITOR.dom.range
+					// instances.
+
+					var ranges = [];
+					var sel = this.getNative();
+
+					for ( var i = 0 ; i < sel.rangeCount ; i++ )
+					{
+						var nativeRange = sel.getRangeAt( i );
+						var range = new CKEDITOR.dom.range( this.document );
+
+						range.setStart( new CKEDITOR.dom.node( nativeRange.startContainer ), nativeRange.startOffset );
+						range.setEnd( new CKEDITOR.dom.node( nativeRange.endContainer ), nativeRange.endOffset );
+						ranges.push( range );
+					}
+
+					return ( this._.cache.ranges = ranges );
 				},
 
@@ -353,25 +493,38 @@
 		},
 
+		normalize : function()
+		{
+			var ranges = this.getRanges();
+
+			for ( var i = 0 ; i < ranges.length ; i++ )
+				ranges[ i ].enlarge( CKEDITOR.ENLARGE_ELEMENT );
+		},
+
+		reset : function()
+		{
+			this._.cache = {};
+		},
+
 		selectElement :
 			CKEDITOR.env.ie ?
 				function( element )
 				{
-					this.getNative().empty() ;
-
-					var range ;
+					this.getNative().empty();
+
+					var range;
 					try
 					{
 						// Try to select the node as a control.
-						range = this.document.$.body.createControlRange() ;
-						range.addElement( element.$ ) ;
+						range = this.document.$.body.createControlRange();
+						range.addElement( element.$ );
 					}
 					catch(e)
 					{
 						// If failed, select it as a text range.
-						range = this.document.$.body.createTextRange() ;
-						range.moveToElementText( element.$ ) ;
-					}
-
-					range.select() ;
+						range = this.document.$.body.createTextRange();
+						range.moveToElementText( element.$ );
+					}
+
+					range.select();
 				}
 			:
@@ -379,12 +532,163 @@
 				{
 					// Create the range for the element.
-					var range = this.document.$.createRange() ;
-					range.selectNode( element.$ ) ;
+					var range = this.document.$.createRange();
+					range.selectNode( element.$ );
 
 					// Select the range.
-					var sel = this.getNative() ;
-					sel.removeAllRanges() ;
-					sel.addRange( range ) ;
+					var sel = this.getNative();
+					sel.removeAllRanges();
+					sel.addRange( range );
+				},
+
+		selectRanges :
+			CKEDITOR.env.ie ?
+				function( ranges )
+				{
+					// IE doesn't accept multiple ranges selection, so we just
+					// select the first one.
+					if ( ranges[ 0 ] )
+						ranges[ 0 ].select();
+				}
+			:
+				function( ranges )
+				{
+					var sel = this.getNative();
+					sel.removeAllRanges();
+
+					for ( var i = 0 ; i < ranges.length ; i++ )
+					{
+						var range = ranges[ i ];
+						var nativeRange = this.document.$.createRange();
+						nativeRange.setStart( range.startContainer.$, range.startOffset );
+						nativeRange.setEnd( range.endContainer.$, range.endOffset );
+
+						// Select the range.
+						sel.addRange( nativeRange );
+					}
 				}
 	};
 })();
+
+CKEDITOR.dom.range.prototype.select =
+	CKEDITOR.env.ie ?
+		// V2
+		function()
+		{
+			var collapsed = this.collapsed;
+			var isStartMakerAlone;
+			var dummySpan;
+
+			var bookmark = this.createBookmark();
+
+			// Create marker tags for the start and end boundaries.
+			var startNode = bookmark.startNode;
+
+			var endNode;
+			if ( !collapsed )
+				endNode = bookmark.endNode;
+
+			// Create the main range which will be used for the selection.
+			var ieRange = this.document.$.body.createTextRange();
+
+			// Position the range at the start boundary.
+			ieRange.moveToElementText( startNode.$ );
+			ieRange.moveStart( 'character', 1 );
+
+			if ( endNode )
+			{
+				// Create a tool range for the end.
+				var ieRangeEnd = this.document.$.body.createTextRange();
+
+				// Position the tool range at the end.
+				ieRangeEnd.moveToElementText( endNode.$ );
+
+				// Move the end boundary of the main range to match the tool range.
+				ieRange.setEndPoint( 'EndToEnd', ieRangeEnd );
+				ieRange.moveEnd( 'character', -1 );
+			}
+			else
+			{
+				isStartMakerAlone = ( !startNode.hasPrevious() || startNode.getPrevious().is( 'br' ) ) && !startNode.hasNext();
+
+				// Append a temporary <span>&#65279;</span> before the selection.
+				// This is needed to avoid IE destroying selections inside empty
+				// inline elements, like <b></b> (#253).
+				// It is also needed when placing the selection right after an inline
+				// element to avoid the selection moving inside of it.
+				dummySpan = this.document.createElement( 'span' );
+				dummySpan.setHtml( '&#65279;' );	// Zero Width No-Break Space (U+FEFF). See #1359.
+				dummySpan.insertBefore( startNode );
+
+				if ( isStartMakerAlone )
+				{
+					// To expand empty blocks or line spaces after <br>, we need
+					// instead to have any char, which will be later deleted using the
+					// selection.
+					// \ufeff = Zero Width No-Break Space (U+FEFF). See #1359.
+					this.document.createText( '\ufeff' ).insertBefore( startNode );
+				}
+			}
+
+			// Remove the markers (reset the position, because of the changes in the DOM tree).
+			this.setStartBefore( startNode );
+			startNode.remove();
+
+			if ( collapsed )
+			{
+				if ( isStartMakerAlone )
+				{
+					// Move the selection start to include the temporary &#65279;.
+					//ieRange.moveStart( 'character', -1 );
+
+					ieRange.select();
+
+					// Remove our temporary stuff.
+//					this.document.$.selection.clear();
+				}
+				else
+					ieRange.select();
+
+				dummySpan.remove();
+			}
+			else
+			{
+				this.setEndBefore( endNode );
+				endNode.remove();
+				ieRange.select();
+			}
+		}
+	:
+		function()
+		{
+			var startContainer = this.startContainer;
+
+			// If we have a collapsed range, inside an empty element, we must add
+			// something to it, otherwise the caret will not be visible.
+			if ( this.collapsed && startContainer.type == CKEDITOR.NODE_ELEMENT && !startContainer.getChildCount() )
+				startContainer.append( new CKEDITOR.dom.text( '' ) );
+
+			var nativeRange = this.document.$.createRange();
+			nativeRange.setStart( startContainer.$, this.startOffset );
+
+			try
+			{
+				nativeRange.setEnd( this.endContainer.$, this.endOffset );
+			}
+			catch ( e )
+			{
+				// There is a bug in Firefox implementation (it would be too easy
+				// otherwise). The new start can't be after the end (W3C says it can).
+				// So, let's create a new range and collapse it to the desired point.
+				if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 )
+				{
+					this.collapse( true );
+					nativeRange.setEnd( this.endContainer.$, this.endOffset );
+				}
+				else
+					throw( e );
+			}
+
+			var selection = this.document.getSelection().getNative();
+			selection.removeAllRanges();
+			selection.addRange( nativeRange );
+		};
Index: /CKEditor/branches/prototype/_source/plugins/toolbar/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/toolbar/plugin.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/plugins/toolbar/plugin.js	(revision 2616)
@@ -222,4 +222,4 @@
 CKEDITOR.config.toolbar =
 [
-	[ 'Source', '-', 'Bold', 'Italic', '-', 'Smiley' ]
+	[ 'Source', '-', 'Bold', 'Italic', 'Underline', 'Strike', '-', 'Subscript', 'Superscript', '-', 'Smiley' ]
 ];
Index: /CKEditor/branches/prototype/_source/skins/default/toolbar.css
===================================================================
--- /CKEditor/branches/prototype/_source/skins/default/toolbar.css	(revision 2615)
+++ /CKEditor/branches/prototype/_source/skins/default/toolbar.css	(revision 2616)
@@ -142,4 +142,24 @@
 }
 
+.cke_skin_default a.cke_button_underline .cke_icon
+{
+	background-position: 0 -336px;
+}
+
+.cke_skin_default a.cke_button_strike .cke_icon
+{
+	background-position: 0 -352px;
+}
+
+.cke_skin_default a.cke_button_subscript .cke_icon
+{
+	background-position: 0 -368px;
+}
+
+.cke_skin_default a.cke_button_superscript .cke_icon
+{
+	background-position: 0 -384px;
+}
+
 .cke_skin_default a.cke_button_smiley .cke_icon
 {
Index: /CKEditor/branches/prototype/_source/tests/core/dom/element.html
===================================================================
--- /CKEditor/branches/prototype/_source/tests/core/dom/element.html	(revision 2615)
+++ /CKEditor/branches/prototype/_source/tests/core/dom/element.html	(revision 2616)
@@ -290,5 +290,5 @@
 			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
 
-			assert.areSame( '<div class=classa></div>', getInnerHtml( 'removeClass' ) );
+			assert.areSame( '<div class="classa"></div>', getInnerHtml( 'removeClass' ) );
 			element.removeClass( 'classA' );
 			assert.areSame( '<div></div>', getInnerHtml( 'removeClass' ) );
@@ -302,11 +302,11 @@
 			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
 
-			assert.areSame( '<div class=classa classb classc classd></div>', getInnerHtml( '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' ) );
+			assert.areSame( '<div class="classb classc classd"></div>', getInnerHtml( 'removeClass' ) );
 			element.removeClass( 'classC' );
-			assert.areSame( '<div class=classb classd></div>', getInnerHtml( 'removeClass' ) );
+			assert.areSame( '<div class="classb classd"></div>', getInnerHtml( 'removeClass' ) );
 			element.removeClass( 'classD' );
-			assert.areSame( '<div class=classb></div>', getInnerHtml( 'removeClass' ) );
+			assert.areSame( '<div class="classb"></div>', getInnerHtml( 'removeClass' ) );
 			element.removeClass( 'classB' );
 			assert.areSame( '<div></div>', getInnerHtml( 'removeClass' ) );
@@ -320,11 +320,11 @@
 			element.appendTo( new CKEDITOR.dom.element( document.getElementById( 'removeClass' ) ) );
 
-			assert.areSame( '<div class=classa classb></div>', getInnerHtml( 'removeClass' ) );
+			assert.areSame( '<div class="classa classb"></div>', getInnerHtml( 'removeClass' ) );
 			element.removeClass( 'classXXX' );
-			assert.areSame( '<div class=classa classb></div>', getInnerHtml( 'removeClass' ) );
+			assert.areSame( '<div class="classa classb"></div>', getInnerHtml( 'removeClass' ) );
 			element.removeClass( 'classB' );
-			assert.areSame( '<div class=classa></div>', getInnerHtml( 'removeClass' ) );
+			assert.areSame( '<div class="classa"></div>', getInnerHtml( 'removeClass' ) );
 			element.removeClass( 'classYYY' );
-			assert.areSame( '<div class=classa></div>', getInnerHtml( 'removeClass' ) );
+			assert.areSame( '<div class="classa"></div>', getInnerHtml( 'removeClass' ) );
 		},
 
@@ -429,10 +429,10 @@
 			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._, b._ );
 		},
@@ -442,12 +442,12 @@
 			var a = new CKEDITOR.dom.element( document.getElementById( 'test1' ) );
 			var b = new CKEDITOR.dom.element( document.getElementById( 'test2' ) );
-			
+
 			assert.areNotSame( a._, b._ );
 		},
-		
+
 		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 )
@@ -466,5 +466,5 @@
 		{
 			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 )
@@ -472,4 +472,16 @@
 			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() );
 		},
 
@@ -500,4 +512,6 @@
 	<div id="getText3">A
 B</div>
+	<big>Test</big>
+	<small title="Testing">Test</small>
 </body>
 </html>
Index: /CKEditor/branches/prototype/_source/tests/core/dom/node.html
===================================================================
--- /CKEditor/branches/prototype/_source/tests/core/dom/node.html	(revision 2616)
+++ /CKEditor/branches/prototype/_source/tests/core/dom/node.html	(revision 2616)
@@ -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.node</title>
+	<link rel="stylesheet" type="text/css" href="../../test.css" />
+	<script type="text/javascript" src="../../../../ckeditor_source.js"></script>
+	<script type="text/javascript" src="../../test.js"></script>
+	<script type="text/javascript">
+	//<![CDATA[
+
+CKEDITOR.test.addTestCase( (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 ) );
+		},
+
+		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 ) );
+//};
+
+	//]]>
+	</script>
+</head>
+<body>
+	<div>
+		<h1>Title</h1>
+		<p><b>Sample</b> <i>Text</i></p>
+	</div>
+</body>
+</html>
Index: /CKEditor/branches/prototype/_source/tests/core/dom/range.html
===================================================================
--- /CKEditor/branches/prototype/_source/tests/core/dom/range.html	(revision 2615)
+++ /CKEditor/branches/prototype/_source/tests/core/dom/range.html	(revision 2616)
@@ -908,4 +908,24 @@
 		},
 
+		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_deleteContents_W3C_1 : function()
 		{
@@ -937,5 +957,5 @@
 			range.deleteContents();
 
-			assert.areSame( getInnerHtml( '_Para' ), 'this is <b id=_b>s</b>text.' );
+			assert.areSame( 'this is <b id="_b">s</b>text.', getInnerHtml( '_Para' ) );
 
 			assert.areSame( document.getElementById( '_Para' ), range.startContainer.$, 'range.startContainer' );
@@ -956,5 +976,5 @@
 			range.deleteContents();
 
-			assert.areSame( 't<b id=_b>ome</b> text.', getInnerHtml( '_Para' ) );
+			assert.areSame( 't<b id="_b">ome</b> text.', getInnerHtml( '_Para' ) );
 
 			assert.areSame( document.getElementById( '_Para' ), range.startContainer.$, 'range.startContainer' );
@@ -975,5 +995,5 @@
 			range.deleteContents();
 
-			assert.areSame( '<h1 id=_h1>f</h1><p>nother paragraph.</p>', getInnerHtml( 'playground' ) );
+			assert.areSame( '<h1 id="_h1">f</h1><p>nother paragraph.</p>', getInnerHtml( 'playground' ) );
 
 			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
@@ -992,5 +1012,5 @@
 			range.deleteContents();
 
-			assert.areSame( '<h1 id=_h1></h1><p></p>', getInnerHtml( 'playground' ) );
+			assert.areSame( '<h1 id="_h1"></h1><p></p>', getInnerHtml( 'playground' ) );
 
 			assert.areSame( document.getElementById( 'playground' ), range.startContainer.$, 'range.startContainer' );
@@ -1063,5 +1083,5 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( 'his is <b id=_b>some</b>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( 'his is <b id="_b">some</b>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
 			assert.areSame( 't text.', getInnerHtml( '_Para' ), 'HTML after extraction' );
 
@@ -1086,6 +1106,6 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( '<b id=_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( '<b id="_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' );
@@ -1109,6 +1129,6 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( 'his is <b id=_b>s</b>', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
-			assert.areSame( 't<b id=_b>ome</b> text.', getInnerHtml( '_Para' ), 'HTML after extraction' );
+			assert.areSame( 'his is <b id="_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' );
@@ -1132,6 +1152,6 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( '<h1 id=_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( '<h1 id="_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' );
@@ -1153,6 +1173,6 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( '<h1 id=_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( '<h1 id="_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' );
@@ -1174,5 +1194,5 @@
 			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( '<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' ) );
 
@@ -1216,5 +1236,5 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( 'this is <b id=_b>some</b> text.', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
+			assert.areSame( 'this is <b id="_b">some</b> text.', getInnerHtml( tmpDiv.$ ), 'Extracted HTML' );
 			assert.areSame( '', getInnerHtml('_Para'), 'HTML after extraction' );
 
@@ -1288,5 +1308,5 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( 'his is <b id=_b>some</b>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+			assert.areSame( 'his is <b id="_b">some</b>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
 
 			// The body HTML must remain unchanged.
@@ -1316,5 +1336,5 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( '<b id=_b>ome</b> t', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+			assert.areSame( '<b id="_b">ome</b> t', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
 
 			// The body HTML must remain unchanged.
@@ -1344,5 +1364,5 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( 'his is <b id=_b>s</b>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+			assert.areSame( 'his is <b id="_b">s</b>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
 
 			// The body HTML must remain unchanged.
@@ -1372,5 +1392,5 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( '<h1 id=_h1>ckw3crange test</h1><p id=_para>this is <b id=_b>some</b> text.</p><p>a</p>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+			assert.areSame( '<h1 id="_h1">ckw3crange test</h1><p id="_para">this is <b id="_b">some</b> text.</p><p>a</p>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
 
 			// The body HTML must remain unchanged.
@@ -1399,5 +1419,5 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( '<h1 id=_h1>fckw3crange test</h1><p id=_para>this is <b id=_b>some</b> text.</p><p>another paragraph.</p>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+			assert.areSame( '<h1 id="_h1">fckw3crange test</h1><p id="_para">this is <b id="_b">some</b> text.</p><p>another paragraph.</p>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
 
 			// The body HTML must remain unchanged.
@@ -1426,5 +1446,5 @@
 			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.$ ), 'Cloned HTML' );
+			assert.areSame( '<h1 id="_h1">fckw3crange test</h1><p id="_para">this is <b id="_b">some</b> text.</p>', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
 
 			// The body HTML must remain unchanged.
@@ -1477,5 +1497,5 @@
 			docFrag.appendTo( tmpDiv );
 
-			assert.areSame( 'this is <b id=_b>some</b> text.', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
+			assert.areSame( 'this is <b id="_b">some</b> text.', getInnerHtml( tmpDiv.$ ), 'Cloned HTML' );
 
 			// The body HTML must remain unchanged.
@@ -1509,23 +1529,11 @@
 //	var doc = new CKEDITOR.dom.document( document );
 
-//			// W3C DOM Range Specs - Section 2.7 - Example 1
+//			doc.getById( '_EnlargeP' ).setHtml( 'this <i>is some </i>sample text' );
 
 //			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' );
+//			range.setStart( doc.getById( '_EnlargeP' ), 0 );
+//			range.setEnd( doc.getById( '_EnlargeP' ).getChild( 1 ), 0 );
+
+//			range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
 //}
 
Index: /CKEditor/branches/prototype/_source/tests/core/dom/text.html
===================================================================
--- /CKEditor/branches/prototype/_source/tests/core/dom/text.html	(revision 2615)
+++ /CKEditor/branches/prototype/_source/tests/core/dom/text.html	(revision 2616)
@@ -18,5 +18,5 @@
 		{
 			var text = new CKEDITOR.dom.text( '0123456789' );
-			
+
 			assert.areSame( '123', text.substring( 1, 4 ) );
 		},
@@ -25,5 +25,5 @@
 		{
 			var text = new CKEDITOR.dom.text( '0123456789' );
-			
+
 			assert.areSame( '56789', text.substring( 5 ) );
 		},
@@ -32,5 +32,5 @@
 		{
 			var text = new CKEDITOR.dom.text( '0123456789' );
-			
+
 			assert.areSame( '', text.substring( 1,1 ) );
 		},
@@ -39,5 +39,5 @@
 		{
 			var text = new CKEDITOR.dom.text( '0123456789' );
-			
+
 			assert.areSame( '012', text.substring( -10,3 ) );
 		},
@@ -46,5 +46,5 @@
 		{
 			var text = new CKEDITOR.dom.text( '0123456789' );
-			
+
 			assert.areSame( '89', text.substring( 8,100 ) );
 		},
@@ -53,5 +53,5 @@
 		{
 			var text = new CKEDITOR.dom.text( '0123456789' );
-			
+
 			assert.areSame( '234', text.substring( 5,2 ) );
 		},
Index: /CKEditor/branches/prototype/_source/tests/plugins/styles/styles.html
===================================================================
--- /CKEditor/branches/prototype/_source/tests/plugins/styles/styles.html	(revision 2616)
+++ /CKEditor/branches/prototype/_source/tests/plugins/styles/styles.html	(revision 2616)
@@ -0,0 +1,297 @@
+<!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.styles Plugin</title>
+	<link rel="stylesheet" type="text/css" href="../../test.css" />
+	<script type="text/javascript" src="../../../../ckeditor_source.js"></script>
+	<script type="text/javascript" src="../../test.js"></script>
+	<script type="text/javascript">
+
+CKEDITOR.plugins.load( 'styles' );
+
+	</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;
+
+	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_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/">FCKeditor<\/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/">fckeditor<\/a>.', getInnerHtml( '_P1' ) );
+		},
+
+		name : document.title
+	};
+})() );
+
+//window.onload = function()
+//{
+//	testCase.test_inline4();
+//}
+
+	//]]>
+	</script>
+</head>
+<body>
+	<p id="_P1"></p>
+</body>
+</html>
Index: /CKEditor/branches/prototype/_source/tests/test.js
===================================================================
--- /CKEditor/branches/prototype/_source/tests/test.js	(revision 2615)
+++ /CKEditor/branches/prototype/_source/tests/test.js	(revision 2616)
@@ -75,13 +75,13 @@
 					if ( expected && expected.nodeType )
 						expected += ' (' + ( expected.nodeType == 1 ? expected.nodeName : expected.nodeValue ) + ')';
-					
+
 					var actual = data.error.actual;
 					if ( actual && actual.nodeType )
 						actual += ' (' + ( actual.nodeType == 1 ? actual.nodeName : actual.nodeValue ) + ')';
-					
-					outputResult( 
-						'<span class="testFail">FAIL</span> Test named "' + data.testName + 
-						'" failed with message: "' + htmlEncode( data.error.message ) + 
-						'".<div>Expected:</div><pre>' + htmlEncode( expected ) + 
+
+					outputResult(
+						'<span class="testFail">FAIL</span> Test named "' + data.testName +
+						'" failed with message: "' + htmlEncode( data.error.message ) +
+						'".<div>Expected:</div><pre>' + htmlEncode( expected ) +
 						'<br></pre><div>Actual:</div><pre>' + htmlEncode( actual ) + '<br></pre>' );
 					break;
Index: /CKEditor/branches/prototype/_source/tests/testall.html
===================================================================
--- /CKEditor/branches/prototype/_source/tests/testall.html	(revision 2615)
+++ /CKEditor/branches/prototype/_source/tests/testall.html	(revision 2616)
@@ -15,4 +15,5 @@
 	'core/dom/document',
 	'core/dom/element',
+	'core/dom/node',
 	'core/dom/range',
 	'core/dom/window',
@@ -21,5 +22,6 @@
 	'core/scriptloader',
 	'core/tools',
-	'core/xml'
+	'core/xml',
+	'plugins/styles/styles'
 ];
 
Index: /CKEditor/branches/prototype/ckeditor.pack
===================================================================
--- /CKEditor/branches/prototype/ckeditor.pack	(revision 2615)
+++ /CKEditor/branches/prototype/ckeditor.pack	(revision 2616)
@@ -23,9 +23,24 @@
 		'CKEDITOR.NODE_TEXT' : 3,
 		'CKEDITOR.NODE_COMMENT' : 8,
+		'CKEDITOR.NODE_DOCUMENT_FRAGMENT' : 11,
+		'CKEDITOR.POSITION_IDENTICAL' : 0,
+		'CKEDITOR.POSITION_DISCONNECTED' : 1,
+		'CKEDITOR.POSITION_FOLLOWING' : 2,
+		'CKEDITOR.POSITION_PRECEDING' : 4,
+		'CKEDITOR.POSITION_IS_CONTAINED' : 8,
+		'CKEDITOR.POSITION_CONTAINS' : 16,
+		'CKEDITOR.POSITION_AFTER_START' : 1,
+		'CKEDITOR.POSITION_BEFORE_END' : 2,
+		'CKEDITOR.POSITION_BEFORE_START' : 3,
+		'CKEDITOR.POSITION_AFTER_END' : 4,
+		'CKEDITOR.ENLARGE_ELEMENT' : 1,
 		'CKEDITOR.UI_BUTTON' : 1,
 		'CKEDITOR.DIALOG_RESIZE_NONE' : 0,
-		'CKEDITOR.DIALOG_RESIZE_RESIZE_WIDTH' : 1,
-		'CKEDITOR.DIALOG_RESIZE_RESIZE_HEIGHT' : 2,
-		'CKEDITOR.DIALOG_RESIZE_RESIZE_BOTH' : 3,
+		'CKEDITOR.DIALOG_RESIZE_WIDTH' : 1,
+		'CKEDITOR.DIALOG_RESIZE_HEIGHT' : 2,
+		'CKEDITOR.DIALOG_RESIZE_BOTH' : 3,
+		'CKEDITOR.STYLE_BLOCK' : 1,
+		'CKEDITOR.STYLE_INLINE' : 2,
+		'CKEDITOR.STYLE_OBJECT' : 3,
 		'CKEDITOR.SELECTION_NONE' : 1,
 		'CKEDITOR.SELECTION_TEXT' : 2,
@@ -66,4 +81,5 @@
 					'_source/core/dom/domobject.js',
 					'_source/core/dom/node.js',
+					'_source/core/dom/nodelist.js',
 					'_source/core/dom/element.js',
 					'_source/core/dom/window.js',
@@ -87,4 +103,6 @@
 					'_source/core/ckeditor.js',
 					'_source/core/dom/text.js',
+					'_source/core/dom/documentfragment.js',
+					'_source/core/dom/range.js',
 					'_source/core/_bootstrap.js',
 //					'_source/lang/en.js',
@@ -92,15 +110,18 @@
 					'_source/plugins/button/plugin.js',
 					'_source/plugins/dialog/plugin.js',
-					'_source/plugins/dialogui/plugin.js',
 					'_source/plugins/elementspath/plugin.js',
 					'_source/plugins/htmldataprocessor/plugin.js',
 					'_source/plugins/keystrokes/plugin.js',
+					'_source/plugins/smiley/plugin.js',
 					'_source/plugins/sourcearea/plugin.js',
 					'_source/plugins/tab/plugin.js',
 					'_source/plugins/toolbar/plugin.js',
 					'_source/plugins/wysiwygarea/plugin.js',
+					'_source/plugins/styles/plugin.js',
+					'_source/plugins/dialogui/plugin.js',
 					'_source/plugins/selection/plugin.js',
 					'_source/plugins/htmlwriter/plugin.js',
 					'_source/plugins/editingblock/plugin.js',
+//					'_source/plugins/smiley/lang/en.js',
 					'_source/skins/default/skin.js',
 					'_source/themes/default/theme.js'
