docs: function createBogusAndFillerRules( editor, type ) {
  		function createFiller( isOutput ) {
  			return isOutput || CKEDITOR.env.needsNbspFiller ?
  				new CKEDITOR.htmlParser.text( '\xa0' ) :
  				new CKEDITOR.htmlParser.element( 'br', { 'data-cke-bogus': 1 } );
  		}
  
  		// This text block filter, remove any bogus and create the filler on demand.
  		function blockFilter( isOutput, fillEmptyBlock ) {
  
  			return function( block ) {
  				// DO NOT apply the filler if it's a fragment node.
  				if ( block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT )
  					return;
  
  				cleanBogus( block );
  
  				var shouldFillBlock = typeof fillEmptyBlock == 'function' ? fillEmptyBlock( block ) : fillEmptyBlock;
  
  				if ( shouldFillBlock !== false && isEmptyBlockNeedFiller( block ) ) {
  					block.add( createFiller( isOutput ) );
  				}
  			};
  		}
  
  		// Append a filler right after the last line-break BR, found at the end of block.
  		function brFilter( isOutput ) {
  			return function( br ) {
  				// DO NOT apply the filer if parent's a fragment node.
  				if ( br.parent.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT )
  					return;
  
  				var attrs = br.attributes;
  				// Dismiss BRs that are either bogus or eol marker.
  				if ( 'data-cke-bogus' in attrs || 'data-cke-eol' in attrs ) {
  					delete attrs [ 'data-cke-bogus' ];
  					return;
  				}
  
  				// Judge the tail line-break BR, and to insert bogus after it.
  				var next = getNext( br ), previous = getPrevious( br );
  
  				if ( !next && isBlockBoundary( br.parent ) )
  					append( br.parent, createFiller( isOutput ) );
  				else if ( isBlockBoundary( next ) && previous && !isBlockBoundary( previous ) )
  					createFiller( isOutput ).insertBefore( next );
  			};
  		}
  
  		// Determinate whether this node is potentially a bogus node.
  		function maybeBogus( node, atBlockEnd ) {
  
  			// BR that's not from IE<11 DOM, except for a EOL marker.
  			if ( !( isOutput && !CKEDITOR.env.needsBrFiller ) &&
  					node.type == CKEDITOR.NODE_ELEMENT && node.name == 'br' &&
  					!node.attributes[ 'data-cke-eol' ] ) {
  				return true;
  			}
  
  			var match;
  
  			// NBSP, possibly.
  			if ( node.type == CKEDITOR.NODE_TEXT && ( match = node.value.match( tailNbspRegex ) ) ) {
  				// We need to separate tail NBSP out of a text node, for later removal.
  				if ( match.index ) {
  					( new CKEDITOR.htmlParser.text( node.value.substring( 0, match.index ) ) ).insertBefore( node );
  					node.value = match[ 0 ];
  				}
  
  				// From IE<11 DOM, at the end of a text block, or before block boundary.
  				if ( !CKEDITOR.env.needsBrFiller && isOutput && ( !atBlockEnd || node.parent.name in textBlockTags ) )
  					return true;
  
  				// From the output.
  				if ( !isOutput ) {
  					var previous = node.previous;
  
  					// Following a line-break at the end of block.
  					if ( previous && previous.name == 'br' )
  						return true;
  
  					// Or a single NBSP between two blocks.
  					if ( !previous || isBlockBoundary( previous ) )
  						return true;
  				}
  			}
  
  			return false;
  		}
  
  		// Removes all bogus inside of this block, and to convert fillers into the proper form.
  		function cleanBogus( block ) {
  			var bogus = [];
  			var last = getLast( block ), node, previous;
  
  			if ( last ) {
  				// Check for bogus at the end of this block.
  				// e.g. <p>foo<br /></p>
  				maybeBogus( last, 1 ) && bogus.push( last );
  
  				while ( last ) {
  					// Check for bogus at the end of any pseudo block contained.
  					if ( isBlockBoundary( last ) && ( node = getPrevious( last ) ) && maybeBogus( node ) ) {
  						// Bogus must have inline proceeding, instead single BR between two blocks,
  						// is considered as filler, e.g. <hr /><br /><hr />
  						if ( ( previous = getPrevious( node ) ) && !isBlockBoundary( previous ) )
  							bogus.push( node );
  						// Convert the filler into appropriate form.
  						else {
  							createFiller( isOutput ).insertAfter( node );
  							node.remove();
  						}
  					}
  
  					last = last.previous;
  				}
  			}
  
  			// Now remove all bogus collected from above.
  			for ( var i = 0 ; i < bogus.length ; i++ )
  				bogus[ i ].remove();
  		}
  
  		// Judge whether it's an empty block that requires a filler node.
  		function isEmptyBlockNeedFiller( block ) {
  
  			// DO NOT fill empty editable in IE<11.
  			if ( !isOutput && !CKEDITOR.env.needsBrFiller && block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT )
  				return false;
  
  			// 1. For IE version >=8,  empty blocks are displayed correctly themself in wysiwiyg;
  			// 2. For the rest, at least table cell and list item need no filler space. (#6248)
  			if ( !isOutput && !CKEDITOR.env.needsBrFiller &&
  				( document.documentMode > 7 ||
  					block.name in CKEDITOR.dtd.tr ||
  					block.name in CKEDITOR.dtd.$listItem ) ) {
  				return false;
  			}
  
  			var last = getLast( block );
  			return !last || block.name == 'form' && last.name == 'input' ;
  		}
  
  		var rules = { elements: {} },
  			isOutput = type == 'html',
  			textBlockTags = CKEDITOR.tools.extend( {}, blockLikeTags );
  
  		// Build the list of text blocks.
  		for ( var i in textBlockTags ) {
  			if ( !( '#' in dtd[ i ] ) )
  				delete textBlockTags[ i ];
  		}
  
  		for ( i in textBlockTags )
  			rules.elements[ i ] = blockFilter( isOutput, editor.config.fillEmptyBlocks );
  
  		// Editable element has to be checked separately.
  		rules.root = blockFilter( isOutput, false );
  		rules.elements.br = brFilter( isOutput );
  		return rules;
  	}