docs: function( range, action, docFrag, mergeThen ) {
  			range.optimizeBookmark();
  
  			var startNode = range.startContainer;
  			var endNode = range.endContainer;
  
  			var startOffset = range.startOffset;
  			var endOffset = range.endOffset;
  
  			var removeStartNode;
  			var removeEndNode;
  
  			// For text containers, we must simply split the node and point to the
  			// second part. The removal will be handled by the rest of the code .
  			if ( endNode.type == CKEDITOR.NODE_TEXT )
  				endNode = endNode.split( endOffset );
  			else {
  				// If the end container has children and the offset is pointing
  				// to a child, then we should start from it.
  				if ( endNode.getChildCount() > 0 ) {
  					// If the offset points after the last node.
  					if ( endOffset >= endNode.getChildCount() ) {
  						// Let's create a temporary node and mark it for removal.
  						endNode = endNode.append( range.document.createText( '' ) );
  						removeEndNode = true;
  					} else {
  						endNode = endNode.getChild( endOffset );
  					}
  				}
  			}
  
  			// For text containers, we must simply split the node. The removal will
  			// be handled by the rest of the code .
  			if ( startNode.type == CKEDITOR.NODE_TEXT ) {
  				startNode.split( startOffset );
  
  				// In cases the end node is the same as the start node, the above
  				// splitting will also split the end, so me must move the end to
  				// the second part of the split.
  				if ( startNode.equals( endNode ) )
  					endNode = startNode.getNext();
  			} else {
  				// If the start container has children and the offset is pointing
  				// to a child, then we should start from its previous sibling.
  
  				// If the offset points to the first node, we don't have a
  				// sibling, so let's use the first one, but mark it for removal.
  				if ( !startOffset ) {
  					// Let's create a temporary node and mark it for removal.
  					startNode = startNode.append( range.document.createText( '' ), 1 );
  					removeStartNode = true;
  				} else if ( startOffset >= startNode.getChildCount() ) {
  					// Let's create a temporary node and mark it for removal.
  					startNode = startNode.append( range.document.createText( '' ) );
  					removeStartNode = true;
  				} else {
  					startNode = startNode.getChild( startOffset ).getPrevious();
  				}
  			}
  
  			// Get the parent nodes tree for the start and end boundaries.
  			var startParents = startNode.getParents();
  			var endParents = endNode.getParents();
  
  			// Compare them, to find the top most siblings.
  			var i, topStart, topEnd;
  
  			for ( i = 0; i < startParents.length; i++ ) {
  				topStart = startParents[ i ];
  				topEnd = endParents[ i ];
  
  				// The compared nodes will match until we find the top most
  				// siblings (different nodes that have the same parent).
  				// "i" will hold the index in the parents array for the top
  				// most element.
  				if ( !topStart.equals( topEnd ) )
  					break;
  			}
  
  			var clone = docFrag,
  				levelStartNode, levelClone, currentNode, currentSibling;
  
  			// Remove all successive sibling nodes for every node in the
  			// startParents tree.
  			for ( var j = i; j < startParents.length; j++ ) {
  				levelStartNode = startParents[ j ];
  
  				// For Extract and Clone, we must clone this level.
  				if ( clone && !levelStartNode.equals( startNode ) ) // action = 0 = Delete
  				levelClone = clone.append( levelStartNode.clone() );
  
  				currentNode = levelStartNode.getNext();
  
  				while ( currentNode ) {
  					// Stop processing when the current node matches a node in the
  					// endParents tree or if it is the endNode.
  					if ( currentNode.equals( endParents[ j ] ) || currentNode.equals( endNode ) )
  						break;
  
  					// Cache the next sibling.
  					currentSibling = currentNode.getNext();
  
  					// If cloning, just clone it.
  					if ( action == 2 ) // 2 = Clone
  					clone.append( currentNode.clone( true ) );
  					else {
  						// Both Delete and Extract will remove the node.
  						currentNode.remove();
  
  						// When Extracting, move the removed node to the docFrag.
  						if ( action == 1 ) // 1 = Extract
  						clone.append( currentNode );
  					}
  
  					currentNode = currentSibling;
  				}
  
  				if ( clone )
  					clone = levelClone;
  			}
  
  			clone = docFrag;
  
  			// Remove all previous sibling nodes for every node in the
  			// endParents tree.
  			for ( var k = i; k < endParents.length; k++ ) {
  				levelStartNode = endParents[ k ];
  
  				// For Extract and Clone, we must clone this level.
  				if ( action > 0 && !levelStartNode.equals( endNode ) ) // action = 0 = Delete
  				levelClone = clone.append( levelStartNode.clone() );
  
  				// The processing of siblings may have already been done by the parent.
  				if ( !startParents[ k ] || levelStartNode.$.parentNode != startParents[ k ].$.parentNode ) {
  					currentNode = levelStartNode.getPrevious();
  
  					while ( currentNode ) {
  						// Stop processing when the current node matches a node in the
  						// startParents tree or if it is the startNode.
  						if ( currentNode.equals( startParents[ k ] ) || currentNode.equals( startNode ) )
  							break;
  
  						// Cache the next sibling.
  						currentSibling = currentNode.getPrevious();
  
  						// If cloning, just clone it.
  						if ( action == 2 ) // 2 = Clone
  						clone.$.insertBefore( currentNode.$.cloneNode( true ), clone.$.firstChild );
  						else {
  							// Both Delete and Extract will remove the node.
  							currentNode.remove();
  
  							// When Extracting, mode the removed node to the docFrag.
  							if ( action == 1 ) // 1 = Extract
  							clone.$.insertBefore( currentNode.$, clone.$.firstChild );
  						}
  
  						currentNode = currentSibling;
  					}
  				}
  
  				if ( clone )
  					clone = levelClone;
  			}
  
  			if ( action == 2 ) // 2 = Clone.
  			{
  				// No changes in the DOM should be done, so fix the split text (if any).
  
  				var startTextNode = range.startContainer;
  				if ( startTextNode.type == CKEDITOR.NODE_TEXT ) {
  					startTextNode.$.data += startTextNode.$.nextSibling.data;
  					startTextNode.$.parentNode.removeChild( startTextNode.$.nextSibling );
  				}
  
  				var endTextNode = range.endContainer;
  				if ( endTextNode.type == CKEDITOR.NODE_TEXT && endTextNode.$.nextSibling ) {
  					endTextNode.$.data += endTextNode.$.nextSibling.data;
  					endTextNode.$.parentNode.removeChild( endTextNode.$.nextSibling );
  				}
  			} else {
  				// Collapse the range.
  
  				// If a node has been partially selected, collapse the range between
  				// topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs).
  				if ( topStart && topEnd && ( startNode.$.parentNode != topStart.$.parentNode || endNode.$.parentNode != topEnd.$.parentNode ) ) {
  					var endIndex = topEnd.getIndex();
  
  					// If the start node is to be removed, we must correct the
  					// index to reflect the removal.
  					if ( removeStartNode && topEnd.$.parentNode == startNode.$.parentNode )
  						endIndex--;
  
  					// Merge splitted parents.
  					if ( mergeThen && topStart.type == CKEDITOR.NODE_ELEMENT ) {
  						var span = CKEDITOR.dom.element.createFromHtml( '<span ' +
  							'data-cke-bookmark="1" style="display:none">&nbsp;</span>', range.document );
  						span.insertAfter( topStart );
  						topStart.mergeSiblings( false );
  						range.moveToBookmark( { startNode: span } );
  					} else {
  						range.setStart( topEnd.getParent(), endIndex );
  					}
  				}
  
  				// Collapse it to the start.
  				range.collapse( true );
  			}
  
  			// Cleanup any marked node.
  			if ( removeStartNode )
  				startNode.remove();
  
  			if ( removeEndNode && endNode.$.parentNode )
  				endNode.remove();
  		}