Index: _source/core/editor.js
===================================================================
--- _source/core/editor.js	(revision 2957)
+++ _source/core/editor.js	(working copy)
@@ -345,8 +345,24 @@
 		execCommand : function( commandName, data )
 		{
 			var command = this.getCommand( commandName );
+			var exeResult;
+			var commandEvent =
+	
+			/**
+			 * The event data for this command
+			 * 
+			 * @type FCKEDITOR.command.Event
+			 */
+			{
+				name :commandName,
+				command :command
+			};
 			if ( command && command.state != CKEDITOR.TRISTATE_DISABLED )
-				return command.exec( this, data );
+			{
+				exeResult = command.exec( this , data );
+				this.fire( 'afterCommandExec' , commandEvent );
+				return exeResult;
+			}
 
 			// throw 'Unknown command name "' + commandName + '"';
 			return false;
Index: _source/core/tools.js
===================================================================
--- _source/core/tools.js	(revision 2957)
+++ _source/core/tools.js	(working copy)
@@ -310,6 +310,22 @@
 				return i;
 		}
 		return -1;
+	},
+	/**
+	 * Binds a function with an object as its this reference.
+	 * 
+	 * @param {Function}
+	 *            func The function to be bounded.
+	 * @param {Object}
+	 *            obj This this reference to bind to the function.
+	 * @returns {Function} The bound function.
+	 */
+	bind : function ( func, obj )
+	{
+		return function ( )
+		{
+			return func.apply( obj , arguments );
+		}
 	}
 };
 
Index: _source/core/dom/documentFragment.js
===================================================================
--- _source/core/dom/documentFragment.js	(revision 2957)
+++ _source/core/dom/documentFragment.js	(working copy)
@@ -8,12 +8,21 @@
 	this.$ = CKEDITOR.env.ie ? ownerDocument.$.createElement( 'div' ) : ownerDocument.$.createDocumentFragment();
 };
 
+/*
+ * A DocumentFragment behaves like a conventional {@link CKEDITOR.dom.Node} with all of the same
+ * methods, except that when a DocumentFragment is inserted into a Document (or
+ * indeed any other Node that may take children) the children of the
+ * DocumentFragment and not the DocumentFragment itself are inserted into the
+ * Node.
+ */
+CKEDITOR.dom.documentFragment.prototype = CKEDITOR.tools
+		.prototypedCopy( CKEDITOR.dom.node.prototype );
+
 (function()
 {
 	var elementPrototype = CKEDITOR.dom.element.prototype;
 
-	CKEDITOR.dom.documentFragment.prototype =
-	{
+	CKEDITOR.tools.extend( CKEDITOR.dom.documentFragment.prototype , {
 		type : CKEDITOR.NODE_DOCUMENT_FRAGMENT,
 
 		append : elementPrototype.append,
@@ -40,5 +49,5 @@
 			else
 				$parent.insertBefore( $, $node.nextSibling );
 		}
-	};
+	} , true );
 })();
Index: _source/core/dom/range.js
===================================================================
--- _source/core/dom/range.js	(revision 2957)
+++ _source/core/dom/range.js	(working copy)
@@ -457,24 +457,30 @@
 			};
 		},
 
-		moveToBookmark : function( bookmark )
+		moveToBookmark : function( bookmark, isPreserve )
 		{
-			// Set the range start at the bookmark start node position.
-			this.setStartBefore( bookmark.startNode );
+			var st = bookmark.startNode , ed = bookmark.endNode;
 
-			// Remove it, because it may interfere in the setEndBefore call.
-			bookmark.startNode.remove();
+            // Set the range start at the bookmark start node position.
+            this.setStartBefore( typeof st === 'string' ? ( st = this.document
+                    .getById( st ) ) : st );
 
-			// Set the range end at the bookmark end node position, or simply
-			// collapse it if it is not available.
-			var endNode = bookmark.endNode;
-			if ( endNode )
-			{
-				this.setEndBefore( endNode );
-				endNode.remove();
-			}
-			else
-				this.collapse( true );
+            // Remove it, because it may interfere in the setEndBefore call.
+            if(!isPreserve)
+                st.remove();
+
+            // Set the range end at the bookmark end node position, or simply
+            // collapse it if it is not available.
+            if ( ed )
+            {
+                this.setEndBefore( typeof ed === 'string' ? 
+                        ( ed = this.document.getById( ed ) )
+                        : ed );
+                if(!isPreserve)
+                    ed.remove();
+            }
+            else
+                this.collapse( true );
 		},
 
 		getCommonAncestor : function( includeSelf )
@@ -994,7 +1000,8 @@
 
 		/**
 		 * Inserts a node at the start of the range. The range will be expanded
-		 * the contain the node.
+		 * to contain the node.
+		 * @param {CKEDITOR.dom.node}
 		 */
 		insertNode : function( node )
 		{
@@ -1002,7 +1009,16 @@
 
 			var startContainer = this.startContainer;
 			var startOffset = this.startOffset;
+			var isDomFrag = false , firstChild , childCounts = 0;
 
+			// Inserted node might be {@link CKEDITOR.dom.documentFragment}
+			if ( node.type === CKEDITOR.NODE_DOCUMENT_FRAGMENT )
+			{
+				isDomFrag = true;
+				firstChild = node.getChild( 0 );
+				childCounts = node.getChildCount();
+			}
+
 			var nextNode = startContainer.getChild( startOffset );
 
 			if ( nextNode )
@@ -1011,7 +1027,13 @@
 				startContainer.append( node );
 
 			// Check if we need to update the end boundary.
-			if ( node.getParent().equals( this.endContainer ) )
+			if ( isDomFrag && firstChild.getParent().equals( this.endContainer ) )
+			{
+				this.endOffset += childCounts;
+				// Expand the range to embrace the new nodes.
+				this.setStartBefore( firstChild );
+			}
+			else if ( node.getParent().equals( this.endContainer ) )
 				this.endOffset++;
 
 			// Expand the range to embrace the new node.
Index: _source/plugins/selection/plugin.js
===================================================================
--- _source/plugins/selection/plugin.js	(revision 2957)
+++ _source/plugins/selection/plugin.js	(working copy)
@@ -83,6 +83,9 @@
 	{
 		init : function( editor, pluginPath )
 		{
+			// Listen to non-user generated selection, e.g. selection changes produced by commands.
+			editor.on( 'afterCommandExec' , checkSelectionChangeTimeout ,
+					editor );
 			editor.on( 'contentDom', function()
 				{
 					if ( CKEDITOR.env.ie )
Index: _source/plugins/styles/plugin.js
===================================================================
--- _source/plugins/styles/plugin.js	(revision 2957)
+++ _source/plugins/styles/plugin.js	(working copy)
@@ -5,81 +5,13 @@
 
 CKEDITOR.plugins.add( 'styles',
 {
-	requires : [ 'selection' ]
-});
-
-/**
- * Registers a function to be called whenever a style changes its state in the
- * editing area. The current state is passed to the function. The possible
- * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}.
- * @param {CKEDITOR.style} The style to be watched.
- * @param {Function} The function to be called when the style state changes.
- * @example
- * // Create a style object for the <b> element.
- * var style = new CKEDITOR.style( { element : 'b' } );
- * var editor = CKEDITOR.instances.editor1;
- * editor.attachStyleStateChange( style, function( state )
- *     {
- *         if ( state == CKEDITOR.TRISTATE_ON )
- *             alert( 'The current state for the B element is ON' );
- *         else
- *             alert( 'The current state for the B element is OFF' );
- *     });
- */
-CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback )
-{
-	// Try to get the list of attached callbacks.
-	var styleStateChangeCallbacks = this._.styleStateChangeCallbacks;
-
-	// If it doesn't exist, it means this is the first call. So, let's create
-	// all the structure to manage the style checks and the callback calls.
-	if ( !styleStateChangeCallbacks )
+	requires : [ 'selection' ],
+	beforeInit : function( editor, pluginPath )
 	{
-		// Create the callbacks array.
-		styleStateChangeCallbacks = this._.styleStateChangeCallbacks = [];
+		
+	var blockElements   = { address:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1 };
+	var objectElements  = { a:1,embed:1,hr:1,img:1,li:1,object:1,ol:1,table:1,td:1,tr:1,ul:1 };
 
-		// Attach to the selectionChange event, so we can check the styles at
-		// that point.
-		this.on( 'selectionChange', function( ev )
-			{
-				// Loop throw all registered callbacks.
-				for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ )
-				{
-					var callback = styleStateChangeCallbacks[ i ];
-
-					// Check the current state for the style defined for that
-					// callback.
-					var currentState = callback.style.checkActive( ev.data.path ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
-
-					// If the state changed since the last check.
-					if ( callback.state !== currentState )
-					{
-						// Call the callback function, passing the current
-						// state to it.
-						callback.fn.call( this, currentState );
-
-						// Save the current state, so it can be compared next
-						// time.
-						callback.state !== currentState;
-					}
-				}
-			});
-	}
-
-	// Save the callback info, so it can be checked on the next occurence of
-	// selectionChange.
-	styleStateChangeCallbacks.push( { style : style, fn : callback } );
-};
-
-CKEDITOR.STYLE_BLOCK = 1;
-CKEDITOR.STYLE_INLINE = 2;
-CKEDITOR.STYLE_OBJECT = 3;
-
-(function()
-{
-	var blockElements	= { address:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1 };
-	var objectElements	= { a:1,embed:1,hr:1,img:1,li:1,object:1,ol:1,table:1,td:1,tr:1,ul:1 };
-
 	CKEDITOR.style = function( styleDefinition )
 	{
 		var element = this.element = ( styleDefinition.element || '*' ).toLowerCase();
@@ -100,19 +32,28 @@
 
 	CKEDITOR.style.prototype =
 	{
-		apply : function( document )
-		{
+		/**
+		 * @param {CKEDITOR.dom.document} document The target document the style applies to.
+		 * @param {Boolean} isReverse Whether it's a reverse apply (remove).
+		 */
+		apply : function( document , isReverse)
+		{ 
 			// Get all ranges from the selection.
 			var selection = document.getSelection();
 			var ranges = selection.getRanges();
-
+			var styleFuncName = isReverse? 'removeFromRange':'applyToRange'; 
 			// Apply the style to the ranges.
 			for ( var i = 0 ; i < ranges.length ; i++ )
-				this.applyToRange( ranges[ i ] );
-
+			{
+					this[styleFuncName].call( this, ranges[i]);
+			}
 			// Select the ranges again.
 			selection.selectRanges( ranges );
 		},
+		
+		reverse: function (document) {
+		  this.apply(document, true);
+		},
 
 		applyToRange : function( range )
 		{
@@ -123,6 +64,57 @@
 							applyBlockStyle
 						: null ).call( this, range );
 		},
+		
+		removeFromRange: function (range) {
+		  
+			return (this.type == CKEDITOR.STYLE_INLINE ? removeInlineStyle :
+			 this.type == CKEDITOR.STYLE_BLOCK ? removeBlockStyle : 
+			 null).call(this, range);
+		},
+		
+		/**
+		 * Remove all related attributes, styles on the element, or even intrinsic semantics(the element itself) which related with this style
+		 * @param {CKEditor.dom.element} element The target element
+		 */
+		removeFromElement: function(element)
+		{
+			if(element.$.nodeType !== 1||element.getName() !== this.element)
+				return;
+			var def = this._.definition;
+			var attribs = def.attributes;
+			var styles = def.styles;
+			
+			for (var attName in attribs) 
+			{
+				// The 'class' element value must match (#1318).
+				if (attName == 'class' && element.getAttribute('class') != attribs[attName]) 
+					continue;
+				
+				element.removeAttribute(attName);
+			}
+			
+			for (var styleName in styles) 
+			{
+				element.removeStyle(styleName);
+			}
+			
+			removeNoAttribsElement(element);
+		},
+		
+		/**
+		 * Remove related style from all the childs of this element
+		 * @param {CKEditor.dom.element} element The target element
+		 * @see CKEditor.style::removeFromElement
+		 */
+		 removeFromAllChilds : function( element )
+		{
+			var innerElements = element.getElementsByTag( this.element );
+			for ( var i = innerElements.count() ; --i >= 0 ; )
+			{
+				var innerElement = innerElements.getItem( i );
+				this.removeFromElement(innerElements);
+			}
+		},
 
 		/**
 		 * Get the style state inside an element path. Returns "true" if the
@@ -153,8 +145,10 @@
 			return false;
 		},
 
-		// Checks if an element, or any of its attributes, is removable by the
-		// current style definition.
+		/**
+         * @param {CKEDITOR.dom.element} element The target element to check.
+         * @param {Boolean} fullMatch Whether require the attribute and styles are fully matched.
+         */
 		checkElementRemovable : function( element, fullMatch )
 		{
 			if ( !element || element.getName() != this.element )
@@ -359,7 +353,7 @@
 
 					// Here we do some cleanup, removing all duplicated
 					// elements from the style element.
-					removeFromElement( this, styleNode );
+					this.removeFromAllChilds( styleNode );
 
 					// Insert it into the range position (it is collapsed after
 					// extractContents.
@@ -384,68 +378,169 @@
 			}
 		}
 
-//		this._FixBookmarkStart( startNode );
+//      this._FixBookmarkStart( startNode );
 
 		range.moveToBookmark( bookmark );
 	};
+	
+	/**
+	 * Remove the style from the specified {@param range}. 
+	 * @param {CKEDITOR.dom.range} range    The range to be processed. 
+	 */
+	function removeInlineStyle(range) {
+	   
+		var 
+        self = this,
+        //bookmark start and end boundaries 
+        bookmark,  
+        startNode, endNode, 
+        walker,
+        //the topmost parent node which conflicts with this style removing
+		matchedTopParent;		
 
+        var isCollapsed =  range.collapsed,
+        // A placeholder for cheat with collapsed range
+        marker;  
+         
+        // Expand the range, if inside inline element boundaries.
+        if(!isCollapsed)
+            range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+         
+        //Cheat by insert a placeholder node which make the range no longer collapsed
+        else         
+        {
+            marker = editor.document.createText( '&nbsp;' );
+            range.insertNode(marker);
+            range.setStartBefore(marker);
+            range.setEndAfter(marker);
+        }
+
+        // Bookmark the range, bookmark nodes are used to establish boundaries.
+        bookmark = range.createBookmark(),
+        startNode   = bookmark.startNode,
+        endNode = bookmark.endNode;
+
+        if ( matchedTopParent = checkPath( startNode , CKEDITOR.tools.bind(
+        		this.checkElementRemovable , this ) ) )
+        	branch( startNode , matchedTopParent , true );
+        if ( matchedTopParent = checkPath( endNode , CKEDITOR.tools.bind(
+        		this.checkElementRemovable , this ) ) )
+        	branch( endNode , matchedTopParent , true );
+        
+		walker = new CKEDITOR.dom.domWalker( startNode );
+		walker.forward( function ( walkerEvt )
+		{
+			var currentNode = walkerEvt.data.from;
+			if ( currentNode.equals( startNode ) )
+				return;
+			else if ( currentNode.equals( endNode ) )
+				this.stop();
+			else
+				self.removeFromElement( currentNode );
+		} );
+        
+		if ( isCollapsed ) // remove the marker node
+		{
+			marker.remove();
+		}
+		else
+		{
+			if ( !CKEDITOR.env.ie ) // normalize text nodes for non-IE
+			{
+				var frag;
+				range.moveToBookmark( bookmark , true );
+				frag = range.extractContents();
+				frag.$.normalize();
+				range.insertNode( frag );
+			}
+		}
+		
+		//TODO: Anchor range boundaries inside element if possible
+		
+		range.moveToBookmark(bookmark);
+	}
 	var applyBlockStyle = function( range )
 	{
 	};
-
-	// Removes a style from inside an element.
-	var removeFromElement = function( style, element )
+	
+	/**
+	 * Create a new branch of the dom tree which rooted by {@param parentNode} and start from the {@param boundaryNode}, after then the {@param boundaryNode} would be placed between these two branches.
+	 * For example in the following dom tree, if we specified '<span/>' as the boundaryNode,
+	 * <pre> 
+	 *      <b>This <i>is some<span /> sample</i> test text</b>
+	 *      </pre>
+	 *The dom tree will end up with:
+	 *<pre>      
+	 *      <b>This <i>is some</i><span /><i> sample</i> test text</b>          <!-- (If parent = <i>) -->
+	 *      <b>This <i>is some</i></b><span /><b><i> sample</i> test text</b>   <!-- (If parent = <b>) -->
+	 *<pre>      
+	 * @param {CKEDITOR.dom.node} boundaryNode The left-most node which used as boundary of this branch.
+	 * @param {CKEDITOR.dom.node} parentNode The first node which will be the root of this branch.
+	 * @see FCKDomTools.BreakParent The v2 similiar method.
+	 */
+	function branch ( boundaryNode, parentNode )
 	{
-		var def = style._.definition;
-		var attribs = def.attributes;
-		var styles = def.styles;
 
-		var innerElements = element.getElementsByTag( style.element );
+		var range = new CKEDITOR.dom.range( editor.document );
 
-		for ( var i = innerElements.count() ; --i >= 0 ; )
-		{
-			var innerElement = innerElements.getItem( i );
+		// We'll be extracting part of this boundaryNode, so let's use our
+		// range to get the correct piece.
+		range.setStartAfter( boundaryNode );
+		range.setEndAfter( parentNode );
 
-			for ( var attName in attribs )
-			{
-				// The 'class' element value must match (#1318).
-				if ( attName == 'class' && innerElement.getAttribute( 'class' ) != attribs[ attName ] )
-					continue;
+		// Extract it, range should be collapsed after the first branch
+		var docFrag = range.extractContents();
 
-				innerElement.removeAttribute( attName );
-			}
+		// Place the boundary node right after first branch 
+		range.insertNode( boundaryNode.remove() );
 
-			for ( var styleName in styles )
+		// Insert the second branch in following
+		docFrag.insertAfterNode( boundaryNode );
+	}
+	
+	
+	/**
+	 * Reversely seaching on the element path which started by {@param pathStartNode}, return the first path element which satisfy {@param testFunction}.
+	 * @param {CKEditor.dom.node} pathStartNode
+	 * @param {Functoin} testFunction The function to test whether the element is the root of branch.
+	 * @param {Boolean} skipBlock Whether ignore all the block level elements included in this path.
+	 */
+	function checkPath(pathStartNode, testFunction, skipBlock) {
+		
+		// Let's start checking the start boundary.
+		var path = new CKEDITOR.dom.elementPath( pathStartNode ) , currentElements = path.elements;
+		var currentElement;
+		var i = skipBlock ? CKEDITOR.tools.indexOf( currentElements , path.block ) - 1
+				: currentElements.length - 1;
+		
+		for ( ; i > 0 ; i-- ) // skip the beginning node
+		{
+			currentElement = currentElements[ i ];
+
+			if ( testFunction( currentElement ) )
 			{
-				innerElement.removeStyle( styleName );
+				return currentElement;
 			}
-
-			removeNoAttribsElement( innerElement );
 		}
 	};
+	
 
-	// If the element has no more attributes, remove it.
-	var removeNoAttribsElement = function( element )
+	
+
+	/**
+	 * If no more attributes remained in the element, remove it, but leaving its
+	 * children.
+	 * 
+	 * @param {CKEDITOR.dom.element}
+	 *            The target element to remove.
+	 */
+	function removeNoAttribsElement( element )
 	{
 		// If no more attributes remained in the element, remove it,
 		// leaving its children.
 		if ( !element.hasAttributes() )
 		{
-			// Removing elements may open points where merging is possible,
-			// so let's cache the first and last nodes for later checking.
-			var firstChild	= element.getFirst();
-			var lastChild	= element.getLast();
-
 			element.remove( true );
-
-			if ( firstChild )
-			{
-				// Check the cached nodes for merging.
-				mergeSiblings( firstChild );
-
-				if ( lastChild && !firstChild.equals( lastChild ) )
-					mergeSiblings( lastChild );
-			}
 		}
 	};
 
@@ -586,8 +681,77 @@
 		// Save the created element. It will be reused on future calls.
 		return ( style._.element = el );
 	};
-})();
+	}
+});
 
+/**
+ * Registers a function to be called whenever a style changes its state in the
+ * editing area. The current state is passed to the function. The possible
+ * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}.
+ * @param {CKEDITOR.style} The style to be watched.
+ * @param {Function} The function to be called when the style state changes.
+ * @example
+ * // Create a style object for the <b> element.
+ * var style = new CKEDITOR.style( { element : 'b' } );
+ * var editor = CKEDITOR.instances.editor1;
+ * editor.attachStyleStateChange( style, function( state )
+ *     {
+ *         if ( state == CKEDITOR.TRISTATE_ON )
+ *             alert( 'The current state for the B element is ON' );
+ *         else
+ *             alert( 'The current state for the B element is OFF' );
+ *     });
+ */
+CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback )
+{
+	// Try to get the list of attached callbacks.
+	var styleStateChangeCallbacks = this._.styleStateChangeCallbacks;
+
+	// If it doesn't exist, it means this is the first call. So, let's create
+	// all the structure to manage the style checks and the callback calls.
+	if ( !styleStateChangeCallbacks )
+	{
+		// Create the callbacks array.
+		styleStateChangeCallbacks = this._.styleStateChangeCallbacks = [];
+
+		// Attach to the selectionChange event, so we can check the styles at
+		// that point.
+		this.on( 'selectionChange', function( ev )
+			{
+				// Loop throw all registered callbacks.
+				for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ )
+				{
+					var callback = styleStateChangeCallbacks[ i ];
+
+					// Check the current state for the style defined for that
+					// callback.
+					var currentState = callback.style.checkActive( ev.data.path ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
+
+					// If the state changed since the last check.
+					if ( callback.state !== currentState )
+					{
+						// Call the callback function, passing the current
+						// state to it.
+						callback.fn.call( this, currentState );
+
+						// Save the current state, so it can be compared next
+						// time.
+						callback.state !== currentState;
+					}
+				}
+			});
+	}
+
+	// Save the callback info, so it can be checked on the next occurence of
+	// selectionChange.
+	styleStateChangeCallbacks.push( { style : style, fn : callback } );
+};
+
+CKEDITOR.STYLE_BLOCK = 1;
+CKEDITOR.STYLE_INLINE = 2;
+CKEDITOR.STYLE_OBJECT = 3;
+
+
 CKEDITOR.styleCommand = function( style )
 {
 	this.style = style;
@@ -600,7 +764,16 @@
 	var doc = editor.document;
 
 	if ( doc )
-		this.style.apply( doc );
+	{
+		if(this.state === CKEDITOR.TRISTATE_OFF)        
+		{
+			this.style.apply( doc );
+		}
+		else if(this.state === CKEDITOR.TRISTATE_ON)
+		{
+			this.style.reverse(doc);
+		}
+	}
 
 	return !!doc;
 };
