Index: /CKEditor/trunk/_source/core/dom/documentFragment.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/documentFragment.js	(revision 3049)
+++ /CKEditor/trunk/_source/core/dom/documentFragment.js	(revision 3050)
@@ -18,4 +18,8 @@
 
 		append : elementPrototype.append,
+
+		getFirst : elementPrototype.getFirst,
+
+		getLast : elementPrototype.getLast,
 
 		appendTo : function( targetElement )
Index: /CKEditor/trunk/_source/core/dom/domwalker.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/domwalker.js	(revision 3049)
+++ /CKEditor/trunk/_source/core/dom/domwalker.js	(revision 3050)
@@ -180,23 +180,43 @@
 	};
 
+	/*
+	 * Anything whose display computed style is block, list-item, table,
+	 * table-row-group, table-header-group, table-footer-group, table-row,
+	 * table-column-group, table-column, table-cell, table-caption, or whose node
+	 * name is hr, br (when enterMode is br only) is a block boundary.
+	 */
+	var blockBoundaryDisplayMatch =
+	{
+		block : 1,
+		'list-item' : 1,
+		table : 1,
+		'table-row-group' : 1,
+		'table-header-group' : 1,
+		'table-footer-group' : 1,
+		'table-row' : 1,
+		'table-column-group' : 1,
+		'table-column' : 1,
+		'table-cell' : 1,
+		'table-caption' : 1
+	},
+		blockBoundaryNodeNameMatch = { hr : 1 };
+
+	CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames )
+	{
+		var nodeNameMatches = CKEDITOR.tools.extend( {}, blockBoundaryNodeNameMatch, customNodeNames || {} );
+
+		return blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ] ||
+			nodeNameMatches[ this.getName() ];
+	};
+
 	CKEDITOR.dom.domWalker.blockBoundary = function( customNodeNames )
 	{
 		return function( evt )
 		{
-			/*
-			 * Anything whose display computed style is block, list-item, table,
-			 * table-row-group, table-header-group, table-footer-group, table-row,
-			 * table-column-group, table-column, table-cell, table-caption, or whose node
-			 * name is hr, br (when enterMode is br only) is a block boundary.
-			 */
-			var displayMatches = { block : 1, 'list-item' : 1, table : 1, 'table-row-group' : 1, 'table-header-group' : 1,
-				'table-footer-group' : 1, 'table-row' : 1, 'table-column-group' : 1, 'table-column' : 1, 'table-cell' : 1,
-				'table-caption' : 1 },
-				nodeNameMatches = CKEDITOR.tools.extend( { hr : 1 }, customNodeNames || {} ),
-				to = evt.data.to,
+			var to = evt.data.to,
 				from = evt.data.from;
 			if ( to && to.type == CKEDITOR.NODE_ELEMENT )
 			{
-				if ( displayMatches[ to.getComputedStyle( 'display' ) ] || nodeNameMatches[ to.getName() ] )
+				if ( to.isBlockBoundary( customNodeNames ) )
 				{
 					evt.stop();
@@ -207,5 +227,5 @@
 			if ( ( evt.data.type == 'up' || evt.data.type == 'sibling' ) && from && from.type == CKEDITOR.NODE_ELEMENT )
 			{
-				if ( displayMatches[ from.getComputedStyle( 'display' ) ] || nodeNameMatches[ from.getName() ] )
+				if ( from.isBlockBoundary( customNodeNames ) )
 				{
 					evt.stop();
Index: /CKEditor/trunk/_source/core/dom/node.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/node.js	(revision 3049)
+++ /CKEditor/trunk/_source/core/dom/node.js	(revision 3050)
@@ -423,4 +423,76 @@
 			this.insertBefore( nodeToReplace );
 			nodeToReplace.remove();
+		},
+
+		trim : function()
+		{
+			this.ltrim();
+			this.rtrim();
+		},
+
+		ltrim : function()
+		{
+			var child;
+			while ( this.getFirst && ( child = this.getFirst() ) )
+			{
+				if ( child.type == CKEDITOR.NODE_TEXT )
+				{
+					var trimmed = CKEDITOR.tools.ltrim( child.getText() ),
+						originalLength = child.getLength();
+
+					if ( trimmed.length == 0 )
+					{
+						child.remove();
+						continue;
+					}
+					else if ( trimmed.length < originalLength )
+					{
+						child.split( originalLength - trimmed.length );
+
+						// IE BUG: child.remove() may raise JavaScript errors here. (#81)
+						this.$.removeChild( this.$.firstChild );
+					}
+				}
+				break;
+			}
+		},
+
+		rtrim : function()
+		{
+			var child;
+			while ( this.getLast && ( child = this.getLast() ) )
+			{
+				if ( child.type == CKEDITOR.NODE_TEXT )
+				{
+					var trimmed = CKEDITOR.tools.rtrim( child.getText() ),
+						originalLength = child.getLength();
+
+					if ( trimmed.length == 0 )
+					{
+						child.remove();
+						continue;
+					}
+					else if ( trimmed.length < originalLength )
+					{
+						child.split( trimmed.length );
+
+						// IE BUG: child.getNext().remove() may raise JavaScript errors here.
+						// (#81)
+						this.$.lastChild.parentNode.removeChild( this.$.lastChild );
+					}
+				}
+				break;
+			}
+
+			if ( !CKEDITOR.env.ie && !CKEDITOR.env.opera )
+			{
+				child = this.$.lastChild;
+
+				if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' )
+				{
+					// Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#324).
+					child.parentNode.removeChild( child ) ;
+				}
+			}
 		}
 	}
Index: /CKEditor/trunk/_source/core/dom/range.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/range.js	(revision 3049)
+++ /CKEditor/trunk/_source/core/dom/range.js	(revision 3050)
@@ -260,53 +260,4 @@
 	var inlineChildReqElements = { abbr:1,acronym:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1 };
 
-	var getBoundaryNodes = function()
-	{
-		var startNode = this.startContainer,
-			endNode = this.endContainer,
-			startOffset = this.startOffset,
-			endOffset = this.endOffset,
-			childCount;
-
-		if ( startNode.type == CKEDITOR.NODE_ELEMENT )
-		{
-			childCount = startNode.getChildCount();
-			if ( childCount > startOffset )
-				startNode = startNode.getChild( startOffset );
-			else if ( childCount < 1 )
-				startNode = startNode.getPreviousSourceNode();
-			else		// startOffset > childCount but childCount is not 0
-			{
-				// Try to take the node just after the current position.
-				startNode = startNode.$;
-				while ( startNode.lastChild )
-					startNode = startNode.lastChild;
-				startNode = new CKEDITOR.dom.node( startNode );
-
-				// Normally we should take the next node in DFS order. But it
-				// is also possible that we've already reached the end of
-				// document.
-				startNode = startNode.getNextSourceNode() || startNode;
-			}
-		}
-		if ( endNode.type == CKEDITOR.NODE_ELEMENT )
-		{
-			childCount = endNode.getChildCount();
-			if ( childCount > endOffset )
-				endNode = endNode.getChild( endOffset ).getPreviousSourceNode();
-			else if ( childCount < 1 )
-				endNode = endNode.getPreviousSourceNode();
-			else		// endOffset > childCount but childCount is not 0
-			{
-				// Try to take the node just before the current position.
-				endNode = endNode.$;
-				while ( endNode.lastChild )
-					endNode = endNode.lastChild;
-				endNode = new CKEDITOR.dom.node( endNode );
-			}
-		}
-
-		return { startNode : startNode, endNode : endNode };
-	};
-
 	// Check every node between the block boundary and the startNode or endNode.
 	var getCheckStartEndBlockFunction = function( isStart )
@@ -477,4 +428,53 @@
 			else
 				this.collapse( true );
+		},
+
+		getBoundaryNodes : function()
+		{
+			var startNode = this.startContainer,
+				endNode = this.endContainer,
+				startOffset = this.startOffset,
+				endOffset = this.endOffset,
+				childCount;
+
+			if ( startNode.type == CKEDITOR.NODE_ELEMENT )
+			{
+				childCount = startNode.getChildCount();
+				if ( childCount > startOffset )
+					startNode = startNode.getChild( startOffset );
+				else if ( childCount < 1 )
+					startNode = startNode.getPreviousSourceNode();
+				else		// startOffset > childCount but childCount is not 0
+				{
+					// Try to take the node just after the current position.
+					startNode = startNode.$;
+					while ( startNode.lastChild )
+						startNode = startNode.lastChild;
+					startNode = new CKEDITOR.dom.node( startNode );
+
+					// Normally we should take the next node in DFS order. But it
+					// is also possible that we've already reached the end of
+					// document.
+					startNode = startNode.getNextSourceNode() || startNode;
+				}
+			}
+			if ( endNode.type == CKEDITOR.NODE_ELEMENT )
+			{
+				childCount = endNode.getChildCount();
+				if ( childCount > endOffset )
+					endNode = endNode.getChild( endOffset ).getPreviousSourceNode();
+				else if ( childCount < 1 )
+					endNode = endNode.getPreviousSourceNode();
+				else		// endOffset > childCount but childCount is not 0
+				{
+					// Try to take the node just before the current position.
+					endNode = endNode.$;
+					while ( endNode.lastChild )
+						endNode = endNode.lastChild;
+					endNode = new CKEDITOR.dom.node( endNode );
+				}
+			}
+
+			return { startNode : startNode, endNode : endNode };
 		},
 
@@ -970,5 +970,5 @@
 				case CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:
 					// DFS backward to get the block/list item boundary at or before the start.
-					var boundaryNodes = getBoundaryNodes.apply( this ),
+					var boundaryNodes = this.getBoundaryNodes(),
 						startNode = boundaryNodes.startNode,
 						endNode = boundaryNodes.endNode,
@@ -1156,5 +1156,4 @@
 		},
 
-		// TODO: The fixed block isn't trimmed, does not work for <pre>.
 		// TODO: Does not add bogus <br> to empty fixed blocks.
 		fixBlock : function( isStart, blockTag )
@@ -1165,4 +1164,5 @@
 			this.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
 			this.extractContents().appendTo( fixedBlock );
+			fixedBlock.trim();
 			this.insertNode( fixedBlock );
 			this.moveToBookmark( bookmark );
@@ -1261,5 +1261,5 @@
 			}
 
-			var startNode = getBoundaryNodes.apply( this ).startNode,
+			var startNode = this.getBoundaryNodes().startNode,
 				walker = new CKEDITOR.dom.domWalker( startNode );
 
@@ -1285,5 +1285,5 @@
 			}
 
-			var endNode = getBoundaryNodes.apply( this ).endNode,
+			var endNode = this.getBoundaryNodes().endNode,
 				walker = new CKEDITOR.dom.domWalker( endNode );
 
Index: /CKEditor/trunk/_source/plugins/domiterator/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/domiterator/plugin.js	(revision 3050)
+++ /CKEditor/trunk/_source/plugins/domiterator/plugin.js	(revision 3050)
@@ -0,0 +1,343 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @file DOM iterator, which iterates over list items, lines and paragraphs.
+ */
+
+CKEDITOR.plugins.add( 'domiterator' );
+
+(function()
+{
+	// Functions ported over from v2.
+	function getTouchedStartNode( range )
+	{
+		var container = range.startContainer;
+
+		if ( range.collapsed || container.type != CKEDITOR.NODE_ELEMENT )
+			return container;
+
+		return container.getChildCount() > range.startOffset ? container.getChild( range.startOffset ) : container;
+	}
+
+	function getTouchedEndNode( range )
+	{
+		var container = range.endContainer;
+
+		if ( range.collapsed || container.type != CKEDITOR.NODE_ELEMENT )
+			return container;
+
+		return container.getChildCount() > range.endOffset ? container.getChild( range.endOffset ) : container;
+	}
+	
+	function getNextSourceNode( currentNode, startFromSibling, nodeType, stopSearchNode )
+	{
+		if ( !currentNode )
+			return null;
+
+		var node;
+
+		if ( !startFromSibling && currentNode.getFirst && currentNode.getFirst() )
+			node = currentNode.getFirst();
+		else
+		{
+			if ( stopSearchNode && currentNode.equals( stopSearchNode ) )
+				return null;
+
+			node = currentNode.getNext();
+
+			if ( !node && ( !stopSearchNode || !stopSearchNode.equals( currentNode.parentNode ) ) )
+				return getNextSourceNode( currentNode.getParent(), true, nodeType, stopSearchNode );
+		}
+
+		if ( nodeType && node && node.type != nodeType )
+			return getNextSourceNode( node, false, nodeType, stopSearchNode );
+
+		return node;
+	}
+
+	var iterator = function( range )
+	{
+		if ( arguments.length < 1 )
+			return;
+
+		this.range = range;
+		this.forceBrBreak = false;
+		this.enforceRealBlocks = false;
+
+		this._ || ( this._ = {} );
+	},
+		beginWhitespaceRegex = /^[\r\n\t ]+$/;
+
+
+	iterator.prototype = {
+		getNextParagraph : function( blockTag )
+		{
+			// The block element to be returned.
+			var block;
+
+			// The range object used to identify the paragraph contents.
+			var range;
+
+			// Indicats that the current element in the loop is the last one.
+			var isLast;
+
+			// Instructs to cleanup remaining BRs.
+			var removePreviousBr, removeLastBr;
+
+			// This is the first iteration. Let's initialize it.
+			if ( !this._.lastNode )
+			{
+				range = this.range.clone();
+				range.enlarge( this.forceBrBreak ? CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS : CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+
+				this._.nextNode = getTouchedStartNode( range );
+				this._.lastNode = getTouchedEndNode( range );
+
+				// Let's reuse this variable.
+				range = null;
+			}
+
+			var currentNode = this._.nextNode,
+				lastNode = this._.lastNode;
+
+			this._.nextNode = null;
+
+			while ( currentNode )
+			{
+				// closeRange indicates that a paragraph boundary has been found,
+				// so the range can be closed.
+				var closeRange = false;
+
+				// includeNode indicates that the current node is good to be part
+				// of the range. By default, any non-element node is ok for it.
+				var includeNode = ( currentNode.type != CKEDITOR.NODE_ELEMENT ),
+					continueFromSibling = false;
+
+				// If it is an element node, let's check if it can be part of the
+				// range.
+				if ( !includeNode )
+				{
+					var nodeName = currentNode.getName();
+
+					if ( currentNode.isBlockBoundary( this.forceBrBreak && { br : 1 } ) )
+					{
+						// <br> boundaries must be part of the range. It will
+						// happen only if ForceBrBreak.
+						if ( nodeName == 'br' )
+							includeNode = true;
+						else if ( !range && currentNode.getChildCount() == 0 && nodeName != 'hr' )
+						{
+							// If we have found an empty block, and haven't started
+							// the range yet, it means we must return this block.
+							block = currentNode;
+							isLast = currentNode.equals( lastNode );
+							break;
+						}
+
+						// The range must finish right before the boundary,
+						// including possibly skipped empty spaces. (#1603)
+						if ( range )
+						{
+							range.setEndAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
+
+							// The found boundary must be set as the next one at this
+							// point. (#1717)
+							if ( nodeName != 'br' )
+								this._.nextNode = getNextSourceNode( currentNode, true, null, lastNode ) || currentNode; 
+						}
+
+						closeRange = true;
+					}
+					else
+					{
+						// If we have child nodes, let's check them.
+						if ( currentNode.getFirst() )
+						{
+							// If we don't have a range yet, let's start it.
+							if ( !range )
+							{
+								range = new CKEDITOR.dom.range( this.range.document );
+								range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
+							}
+
+							currentNode = currentNode.getFirst();
+							continue;
+						}
+						includeNode = true;
+					}
+				}
+				else if ( currentNode.type == CKEDITOR.NODE_TEXT )
+				{
+					// Ignore normal whitespaces (i.e. not including &nbsp; or
+					// other unicode whitespaces) before/after a block node.
+					if ( beginWhitespaceRegex.test( currentNode.getText() ) )
+						includeNode = false;
+				}
+
+				// The current node is good to be part of the range and we are
+				// starting a new range, initialize it first.
+				if ( includeNode && !range )
+				{
+					range = new CKEDITOR.dom.range( this.range.document );
+					range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
+				}
+
+				// The last node has been found.
+				isLast = ( ( !closeRange || includeNode ) && currentNode.equals( lastNode ) );
+
+				// If we are in an element boundary, let's check if it is time
+				// to close the range, otherwise we include the parent within it.
+				if ( range && !closeRange )
+				{
+					while ( !currentNode.getNext() && !isLast )
+					{
+						var parentNode = currentNode.getParent();
+
+						if ( parentNode.isBlockBoundary( this.forceBrBreak && { br : 1 } ) )
+						{
+							closeRange = true;
+							isLast = isLast || ( parentNode.equals( lastNode) );
+							break;
+						}
+
+						currentNode = parentNode;
+						includeNode = true;
+						isLast = ( currentNode.equals( lastNode ) );
+						continueFromSibling = true;
+					}
+				}
+
+				// Now finally include the node.
+				if ( includeNode )
+					range.setEndAt( currentNode, CKEDITOR.POSITION_AFTER_END );
+
+				// We have found a block boundary. Let's close the range and move out of the
+				// loop.
+				if ( ( closeRange || isLast ) && range )
+				{
+					var boundaryNodes = range.getBoundaryNodes(),
+						startPath = new CKEDITOR.dom.elementPath( range.startContainer ),
+						endPath = new CKEDITOR.dom.elementPath( range.endContainer );
+					if ( boundaryNodes.startNode.equals( boundaryNodes.endNode )
+							&& boundaryNodes.startNode.getParent().equals( startPath.blockLimit )
+							&& boundaryNodes.startNode.type == CKEDITOR.NODE_ELEMENT && boundaryNodes.startNode.getAttribute( '_fck_bookmark' ) )
+						range = null;
+					else
+						break;
+				}
+
+				if ( isLast )
+					break;
+
+				currentNode = getNextSourceNode( currentNode, continueFromSibling, null, lastNode );
+			}
+
+			// Now, based on the processed range, look for (or create) the block to be returned.
+			if ( !block )
+			{
+				// If no range has been found, this is the end.
+				if ( !range )
+				{
+					this._.nextNode = null;
+					return null;
+				}
+
+				var startPath = new CKEDITOR.dom.elementPath( range.startContainer ),
+					startBlockLimit = startPath.blockLimit,
+					checkLimits = { div : 1, th : 1, td : 1 };
+				block = startPath.block;
+
+				if ( !block
+						&& !this.enforceRealBlocks 
+						&& checkLimits[ startBlockLimit.getName() ]
+						&& range.checkStartOfBlock()
+						&& range.checkEndOfBlock() )
+					block = startBlockLimit;
+				else if ( !block || ( this.enforceRealBlocks && block.getName() == 'li' ) )
+				{
+					// Create the fixed block.
+					block = this.range.document.createElement( blockTag || 'p' );
+
+					// Move the contents of the temporary range to the fixed block.
+					range.extractContents().appendTo( block );
+					block.trim();
+
+					// Insert the fixed block into the DOM.
+					range.insertNode( block );
+
+					removePreviousBr = removeLastBr = true;
+				}
+				else if ( block.getName() != 'li' )
+				{
+					// If the range doesn't includes the entire contents of the
+					// block, we must split it, isolating the range in a dedicated
+					// block.
+					if ( !range.checkStartOfBlock() || !range.checkEndOfBlock() )
+					{
+						// The resulting block will be a clone of the current one.
+						block = block.clone( false );
+
+						// Extract the range contents, moving it to the new block.
+						range.extractContents().appendTo( block );
+						block.trim();
+
+						// Split the block. At this point, the range will be in the
+						// right position for our intents.
+						var splitInfo = range.splitBlock();
+
+						removePreviousBr = !splitInfo.wasStartOfBlock;
+						removeLastBr = !splitInfo.wasEndOfBlock;
+
+						// Insert the new block into the DOM.
+						range.insertNode( block );
+					}
+				}
+				else if ( !isLast )
+				{
+					// LIs are returned as is, with all their children (due to the
+					// nested lists). But, the next node is the node right after
+					// the current range, which could be an <li> child (nested
+					// lists) or the next sibling <li>.
+
+					this._.nextNode = ( block.equals( lastNode ) ? null :
+							getNextSourceNode( range.getBoundaryNodes().endNode, true, null, lastNode ) );
+				}
+			}
+
+			if ( removePreviousBr )
+			{
+				var previousSibling = block.getPrevious();
+				if ( previousSibling && previousSibling.type == CKEDITOR.NODE_ELEMENT )
+				{
+					if ( previousSibling.getName() == 'br' )
+						previousSibling.remove();
+					else if ( previousSibling.getLast() && previousSibling.getLast().$.nodeName.toLowerCase() == 'br' )
+						previousSibling.getLast().remove();
+				}
+			}
+
+			if ( removeLastBr )
+			{
+				var lastChild = block.getLast();
+				if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.getName() == 'br' )
+					lastChild.remove();
+			}
+
+			// Get a reference for the next element. This is important because the
+			// above block can be removed or changed, so we can rely on it for the
+			// next interation.
+			if ( !this._.nextNode )
+				this._.nextNode = ( isLast || block.equals( lastNode ) ) ? null : getNextSourceNode( block, true, null, lastNode );
+
+			return block;
+		}
+	};
+
+	CKEDITOR.dom.range.prototype.createIterator = function()
+	{
+		return new iterator( this );
+	};
+})();
