浏览代码

Added `isEmpty()` method and integrated it with `DataController.get()` method.

Krzysztof Krztoń 7 年之前
父节点
当前提交
aabe1b8e86

+ 22 - 8
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -109,10 +109,17 @@ export default class DataController {
 	 * Returns the model's data converted by downcast dispatchers attached to {@link #downcastDispatcher} and
 	 * formatted by the {@link #processor data processor}.
 	 *
-	 * @param {String} [rootName='main'] Root name.
+	 * @param {Object} [options]
+	 * @param {String} [options.rootName='main'] Root name.
+	 * @param {String} [options.trim='empty'] Whether returned data should be trimmed. This option is set to `empty` by default,
+	 * which means whenever editor content is considered empty, the empty string will be returned. To turn off trimming completely
+	 * use `none`. In such cases exact content will be returned (for example `<p>&nbsp;</p>` for empty editor).
 	 * @returns {String} Output data.
 	 */
-	get( rootName = 'main' ) {
+	get( options ) {
+		const rootName = ( options || {} ).rootName || 'main';
+		const trim = ( options || {} ).trim || 'empty';
+
 		if ( !this._checkIfRootsExists( [ rootName ] ) ) {
 			/**
 			 * Cannot get data from a non-existing root. This error is thrown when {@link #get DataController#get() method}
@@ -129,7 +136,7 @@ export default class DataController {
 		}
 
 		// Get model range.
-		return this.stringify( this.model.document.getRoot( rootName ) );
+		return this.stringify( this.model.document.getRoot( rootName ), trim === 'empty' );
 	}
 
 	/**
@@ -139,14 +146,21 @@ export default class DataController {
 	 *
 	 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} modelElementOrFragment
 	 * Element whose content will be stringified.
+	 * @param {Boolean} [skipEmpty=false] Whether content considered empty should be skipped. The method
+	 * will return an empty string in such cases.
 	 * @returns {String} Output data.
 	 */
-	stringify( modelElementOrFragment ) {
-		// Model -> view.
-		const viewDocumentFragment = this.toView( modelElementOrFragment );
+	stringify( modelElementOrFragment, skipEmpty = false ) {
+		if ( skipEmpty && this.model.isEmpty( modelElementOrFragment ) ) {
+			// If trimEmpty is true && model is considered empty return empty string.
+			return '';
+		} else {
+			// Model -> view.
+			const viewDocumentFragment = this.toView( modelElementOrFragment );
 
-		// View -> data.
-		return this.processor.toData( viewDocumentFragment );
+			// View -> data.
+			return this.processor.toData( viewDocumentFragment );
+		}
 	}
 
 	/**

+ 41 - 0
packages/ckeditor5-engine/src/model/model.js

@@ -450,6 +450,8 @@ export default class Model {
 	 *
 	 * Content is any text node or element which is registered in the {@link module:engine/model/schema~Schema schema}.
 	 *
+	 * **Note**: To check if editor or any part of the content contains meaningful data, use {@link #isEmpty}.
+	 *
 	 * @param {module:engine/model/range~Range|module:engine/model/element~Element} rangeOrElement Range or element to check.
 	 * @returns {Boolean}
 	 */
@@ -472,6 +474,45 @@ export default class Model {
 		return false;
 	}
 
+	/**
+	 * Checks whether the given {@link module:engine/model/range~Range range} or
+	 * {@link module:engine/model/element~Element element} is considered empty.
+	 *
+	 * The range or element is considered non-empty if it contains any:
+	 * 		* EmptyElement
+	 * 		* Text node containing at least one non-whitepsace character
+	 * 		* Non-plain `ContainerElement` (for example widget)
+	 * 		* Non-plain `AttributeElement` (for example comment)
+	 *
+	 * 	This method should be used to check if the element/range/editor contains any printable/meaningful content.
+	 * 	It is the proper method to check if editor is empty.
+	 *
+	 * @param {module:engine/model/range~Range|module:engine/model/element~Element} rangeOrElement Range or element to check.
+	 * @returns {Boolean}
+	 */
+	isEmpty( rangeOrElement ) {
+		if ( rangeOrElement instanceof ModelElement ) {
+			rangeOrElement = ModelRange._createIn( rangeOrElement );
+		}
+
+		if ( rangeOrElement.isCollapsed ) {
+			return true;
+		}
+
+		for ( const item of rangeOrElement.getItems() ) {
+			// Remember, `TreeWalker` returns always `textProxy` nodes.
+			if ( item.is( 'textProxy' ) && item.data.match( /\S+/gi ) !== null ) {
+				return false;
+			} else if ( this.schema.isObject( item ) || item.is( 'emptyElement' ) ) {
+				return false;
+			}
+			// Check for Non-plain `ContainerElement`
+			// Check for Non-plain `AttributeElement`
+		}
+
+		return true;
+	}
+
 	/**
 	 * Creates a position from the given root and path in that root.
 	 *