Преглед на файлове

"Schema#checkMerge()" will work with an instance of Position class.

Kamil Piechaczek преди 8 години
родител
ревизия
3fc72d7710
променени са 2 файла, в които са добавени 88 реда и са изтрити 6 реда
  1. 37 6
      packages/ckeditor5-engine/src/model/schema.js
  2. 51 0
      packages/ckeditor5-engine/tests/model/schema.js

+ 37 - 6
packages/ckeditor5-engine/src/model/schema.js

@@ -12,6 +12,8 @@ import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 
 import Range from './range';
+import Position from './position';
+import Element from './element';
 
 /**
  * The model's schema. It defines allowed and disallowed structures of nodes as well as nodes' attributes.
@@ -411,20 +413,49 @@ export default class Schema {
 	}
 
 	/**
-	 * Checks whether the given element (`elementToMerge`) can be merged with the specified base element (`baseElement`).
+	 * Checks whether the given element (`elementToMerge`) can be merged with the specified base element (`positionOrBaseElement`).
 	 *
-	 * In other words – whether `elementToMerge`'s children {@link #checkChild are allowed} in the `baseElement`.
+	 * In other words – whether `elementToMerge`'s children {@link #checkChild are allowed} in the `positionOrBaseElement`.
 	 *
 	 * This check ensures that elements merged with {@link module:engine/model/writer~Writer#merge `Writer#merge()`}
 	 * will be valid.
 	 *
-	 * @param {module:engine/model/Element~Element} baseElement The base element to which the `elementToMerge` will be merged.
-	 * @param {module:engine/model/Element~Element} elementToMerge The element to merge.
+	 * Instead of elements, you can pass the instance of {@link module:engine/model/position~Position} class as the `positionOrBaseElement`.
+	 * It means that the elements before and after the position will be checked whether they can be merged.
+	 *
+	 * @param {module:engine/model/position~Position|module:engine/model/Element~Element} positionOrBaseElement The position or base
+	 * element to which the `elementToMerge` will be merged.
+	 * @param {module:engine/model/Element~Element} elementToMerge The element to merge. Required if `positionOrBaseElement` is a element.
 	 * @returns {Boolean}
 	 */
-	checkMerge( baseElement, elementToMerge ) {
+	checkMerge( positionOrBaseElement, elementToMerge = null ) {
+		if ( positionOrBaseElement instanceof Position ) {
+			const nodeBefore = positionOrBaseElement.nodeBefore;
+			const nodeAfter = positionOrBaseElement.nodeAfter;
+
+			if ( !( nodeBefore instanceof Element ) ) {
+				/**
+				 * Node before merge position must be an element.
+				 *
+				 * @error schema-check-merge-no-element-before
+				 */
+				throw new CKEditorError( 'schema-check-merge-no-element-before: Node before merge position must be an element.' );
+			}
+
+			if ( !( nodeAfter instanceof Element ) ) {
+				/**
+				 * Node after merge position must be an element.
+				 *
+				 * @error schema-check-merge-no-element-after
+				 */
+				throw new CKEditorError( 'schema-check-merge-no-element-after: Node after merge position must be an element.' );
+			}
+
+			return this.checkMerge( nodeBefore, nodeAfter );
+		}
+
 		for ( const child of elementToMerge.getChildren() ) {
-			if ( !this.checkChild( baseElement, child ) ) {
+			if ( !this.checkChild( positionOrBaseElement, child ) ) {
 				return false;
 			}
 		}

+ 51 - 0
packages/ckeditor5-engine/tests/model/schema.js

@@ -696,6 +696,57 @@ describe( 'Schema', () => {
 			expect( schema.checkMerge( listItem, listItemToMerge ) ).to.be.true;
 		} );
 
+		it( 'return true if two elements between the position can be merged', () => {
+			const listItem = new Element( 'listItem', null, [
+				new Text( 'foo' )
+			] );
+			const listItemToMerge = new Element( 'listItem', null, [
+				new Text( 'bar' )
+			] );
+
+			// eslint-disable-next-line no-new
+			new Element( '$root', null, [
+				listItem, listItemToMerge
+			] );
+			const position = Position.createAfter( listItem );
+
+			expect( schema.checkMerge( position ) ).to.be.true;
+		} );
+
+		it( 'throws an error if there is no element before the position', () => {
+			const listItem = new Element( 'listItem', null, [
+				new Text( 'foo' )
+			] );
+
+			// eslint-disable-next-line no-new
+			new Element( '$root', null, [
+				listItem
+			] );
+
+			const position = Position.createBefore( listItem );
+
+			expect( () => {
+				expect( schema.checkMerge( position ) );
+			} ).to.throw( CKEditorError, /^schema-check-merge-no-element-before:/ );
+		} );
+
+		it( 'throws an error if there is no element after the position', () => {
+			const listItem = new Element( 'listItem', null, [
+				new Text( 'foo' )
+			] );
+
+			// eslint-disable-next-line no-new
+			new Element( '$root', null, [
+				listItem
+			] );
+
+			const position = Position.createAfter( listItem );
+
+			expect( () => {
+				expect( schema.checkMerge( position ) );
+			} ).to.throw( CKEditorError, /^schema-check-merge-no-element-after:/ );
+		} );
+
 		// This is an invalid case by definition – the baseElement should not contain disallowed elements
 		// in the first place. However, the check is focused on the elementToMerge's children so let's make sure
 		// that only them counts.