docs: function( fragment, toHtml, transformOnly, enterMode ) {
  			if ( this.disabled )
  				return false;
  
  			var that = this,
  				toBeRemoved = [],
  				protectedRegexs = this.editor && this.editor.config.protectedSource,
  				processRetVal,
  				isModified = false,
  				filterOpts = {
  					doFilter: !transformOnly,
  					doTransform: true,
  					doCallbacks: true,
  					toHtml: toHtml
  				};
  
  			// Filter all children, skip root (fragment or editable-like wrapper used by data processor).
  			fragment.forEach( function( el ) {
  				if ( el.type == CKEDITOR.NODE_ELEMENT ) {
  					// Do not filter element with data-cke-filter="off" and all their descendants.
  					if ( el.attributes[ 'data-cke-filter' ] == 'off' )
  						return false;
  
  					// (#10260) Don't touch elements like spans with data-cke-* attribute since they're
  					// responsible e.g. for placing markers, bookmarks, odds and stuff.
  					// We love 'em and we don't wanna lose anything during the filtering.
  					// '|' is to avoid tricky joints like data-="foo" + cke-="bar". Yes, they're possible.
  					//
  					// NOTE: data-cke-* assigned elements are preserved only when filter is used with
  					//       htmlDataProcessor.toHtml because we don't want to protect them when outputting data
  					//       (toDataFormat).
  					if ( toHtml && el.name == 'span' && ~CKEDITOR.tools.objectKeys( el.attributes ).join( '|' ).indexOf( 'data-cke-' ) )
  						return;
  
  					processRetVal = processElement( that, el, toBeRemoved, filterOpts );
  					if ( processRetVal & FILTER_ELEMENT_MODIFIED )
  						isModified = true;
  					else if ( processRetVal & FILTER_SKIP_TREE )
  						return false;
  				}
  				else if ( el.type == CKEDITOR.NODE_COMMENT && el.value.match( /^\{cke_protected\}(?!\{C\})/ ) ) {
  					if ( !processProtectedElement( that, el, protectedRegexs, filterOpts ) )
  						toBeRemoved.push( el );
  				}
  			}, null, true );
  
  			if ( toBeRemoved.length )
  				isModified = true;
  
  			var node, element, check,
  				toBeChecked = [],
  				enterTag = enterModeTags[ enterMode || ( this.editor ? this.editor.enterMode : CKEDITOR.ENTER_P ) ];
  
  			// Remove elements in reverse order - from leaves to root, to avoid conflicts.
  			while ( ( node = toBeRemoved.pop() ) ) {
  				if ( node.type == CKEDITOR.NODE_ELEMENT )
  					removeElement( node, enterTag, toBeChecked );
  				// This is a comment securing rejected element - remove it completely.
  				else
  					node.remove();
  			}
  
  			// Check elements that have been marked as possibly invalid.
  			while ( ( check = toBeChecked.pop() ) ) {
  				element = check.el;
  				// Element has been already removed.
  				if ( !element.parent )
  					continue;
  
  				switch ( check.check ) {
  					// Check if element itself is correct.
  					case 'it':
  						// Check if element included in $removeEmpty has no children.
  						if ( DTD.$removeEmpty[ element.name ] && !element.children.length )
  							removeElement( element, enterTag, toBeChecked );
  						// Check if that is invalid element.
  						else if ( !validateElement( element ) )
  							removeElement( element, enterTag, toBeChecked );
  						break;
  
  					// Check if element is in correct context. If not - remove element.
  					case 'el-up':
  						// Check if e.g. li is a child of body after ul has been removed.
  						if ( element.parent.type != CKEDITOR.NODE_DOCUMENT_FRAGMENT &&
  							!DTD[ element.parent.name ][ element.name ]
  						)
  							removeElement( element, enterTag, toBeChecked );
  						break;
  
  					// Check if element is in correct context. If not - remove parent.
  					case 'parent-down':
  						if ( element.parent.type != CKEDITOR.NODE_DOCUMENT_FRAGMENT &&
  							!DTD[ element.parent.name ][ element.name ]
  						)
  							removeElement( element.parent, enterTag, toBeChecked );
  						break;
  				}
  			}
  
  			return isModified;
  		}