浏览代码

Merge branch 'master' into t/1210

Szymon Kupś 7 年之前
父节点
当前提交
ca2680febe

+ 5 - 5
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -202,7 +202,7 @@ export default class DataController {
 			writer.removeSelectionAttribute( this.model.document.selection.getAttributeKeys() );
 
 			writer.remove( ModelRange.createIn( modelRoot ) );
-			writer.insert( this.parse( data ), modelRoot );
+			writer.insert( this.parse( data, modelRoot ), modelRoot );
 		} );
 	}
 
@@ -212,11 +212,11 @@ export default class DataController {
 	 *
 	 * @see #set
 	 * @param {String} data Data to parse.
-	 * @param {module:engine/model/schema~SchemaContextDefinition} [context=['$root']] Base context in which the view will
+	 * @param {module:engine/model/schema~SchemaContextDefinition} [context='$root'] Base context in which the view will
 	 * be converted to the model. See: {@link module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#convert}.
 	 * @returns {module:engine/model/documentfragment~DocumentFragment} Parsed data.
 	 */
-	parse( data, context = [ '$root' ] ) {
+	parse( data, context = '$root' ) {
 		// data -> view
 		const viewDocumentFragment = this.processor.toView( data );
 
@@ -234,11 +234,11 @@ export default class DataController {
 	 *
 	 * @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} viewElementOrFragment
 	 * Element or document fragment whose content will be converted.
-	 * @param {module:engine/model/schema~SchemaContextDefinition} [context=['$root']] Base context in which the view will
+	 * @param {module:engine/model/schema~SchemaContextDefinition} [context='$root'] Base context in which the view will
 	 * be converted to the model. See: {@link module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher#convert}.
 	 * @returns {module:engine/model/documentfragment~DocumentFragment} Output document fragment.
 	 */
-	toModel( viewElementOrFragment, context = [ '$root' ] ) {
+	toModel( viewElementOrFragment, context = '$root' ) {
 		return this.viewToModel.convert( viewElementOrFragment, context );
 	}
 

+ 4 - 2
packages/ckeditor5-engine/src/conversion/view-to-model-converters.js

@@ -31,8 +31,10 @@ export function convertToModelFragment() {
 	return ( evt, data, conversionApi ) => {
 		// Second argument in `consumable.consume` is discarded for ViewDocumentFragment but is needed for ViewElement.
 		if ( !data.modelRange && conversionApi.consumable.consume( data.viewItem, { name: true } ) ) {
-			data = Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );
-			data.modelCursor = data.modelRange.end;
+			const { modelRange, modelCursor } = conversionApi.convertChildren( data.viewItem, data.modelCursor );
+
+			data.modelRange = modelRange;
+			data.modelCursor = modelCursor;
 		}
 	};
 }

+ 1 - 1
packages/ckeditor5-engine/src/conversion/viewconversiondispatcher.js

@@ -82,7 +82,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *		// Fire conversion.
  *		// Always take care where the converted model structure will be appended to. If this `viewDocumentFragment`
  *		// is going to be appended directly to a '$root' element, use that in `context`.
- *		viewDispatcher.convert( viewDocumentFragment, { context: [ '$root' ] } );
+ *		viewDispatcher.convert( viewDocumentFragment, '$root' );
  *
  * Before each conversion process, `ViewConversionDispatcher` fires {@link ~ViewConversionDispatcher#event:viewCleanup}
  * event which can be used to prepare tree view for conversion.

+ 3 - 3
packages/ckeditor5-engine/src/dev-utils/model.js

@@ -243,8 +243,8 @@ export function stringify( node, selectionOrPositionOrRange = null ) {
  * @param {Object} [options={}] Additional configuration.
  * @param {Array<Object>} [options.selectionAttributes] A list of attributes which will be passed to the selection.
  * @param {Boolean} [options.lastRangeBackward=false] If set to `true`, the last range will be added as backward.
- * @param {module:engine/model/schema~SchemaContextDefinition} [options.context=[ '$root' ]] The conversion context.
- * If not provided, the default `[ '$root' ]` will be used.
+ * @param {module:engine/model/schema~SchemaContextDefinition} [options.context='$root'] The conversion context.
+ * If not provided, the default `'$root'` will be used.
  * @returns {module:engine/model/element~Element|module:engine/model/text~Text|
  * module:engine/model/documentfragment~DocumentFragment|Object} Returns the parsed model node or
  * an object with two fields: `model` and `selection`, when selection ranges were included in the data to parse.
@@ -282,7 +282,7 @@ export function parse( data, schema, options = {} ) {
 	viewToModel.isDebug = true;
 
 	// Convert view to model.
-	let model = viewToModel.convert( viewDocumentFragment.root, options.context || [ '$root' ] );
+	let model = viewToModel.convert( viewDocumentFragment.root, options.context || '$root' );
 
 	mapper.bindElements( model, viewDocumentFragment.root );
 

+ 13 - 8
packages/ckeditor5-engine/src/model/schema.js

@@ -1114,14 +1114,16 @@ export class SchemaContext {
 	/**
 	 * Creates an instance of the context.
 	 *
-	 * @param {module:engine/model/schema~SchemaContextDefinition|module:engine/model/schema~SchemaContext} context
+	 * @param {module:engine/model/schema~SchemaContextDefinition} context
 	 */
 	constructor( context ) {
 		if ( context instanceof SchemaContext ) {
 			return context;
 		}
 
-		if ( !Array.isArray( context ) ) {
+		if ( typeof context == 'string' ) {
+			context = [ context ];
+		} else if ( !Array.isArray( context ) ) {
 			// `context` is item or position.
 			// Position#getAncestors() doesn't accept any parameters but it works just fine here.
 			context = context.getAncestors( { includeSelf: true } );
@@ -1239,11 +1241,11 @@ export class SchemaContext {
  * * By defining a **node** – in this cases this node and all its ancestors will be used.
  * * By defining a **position** in the document – in this case all its ancestors will be used.
  * * By defining an **array of nodes** – in this case this array defines the entire context.
- * * By defining an **array of node names** (potentially, mixed with real nodes) – in this case
- * nodes definied by strings will be "mocked". Using strings is not recommended as it
- * means that the context will be unrealistic (e.g. attributes of these nodes are not specified).
- * However, at times this may be the only way to define the context (e.g. when checking some
- * hypothetical situation).
+ * * By defining a **name of node** - in this case node will be "mocked". It is not recommended because context
+ * will be unrealistic (e.g. attributes of these nodes are not specified). However, at times this may be the only
+ * way to define the context (e.g. when checking some hypothetical situation).
+ * * By defining an **array of node names** (potentially, mixed with real nodes) – The same as **name of node**
+ * but it is possible to create a path.
  * * By defining a {@link module:engine/model/schema~SchemaContext} instance - in this case the same instance as provided
  * will be return.
  *
@@ -1263,6 +1265,9 @@ export class SchemaContext {
  *		// Check in [ rootElement, paragraphElement ].
  *		schema.checkChild( [ rootElement, paragraphElement ], 'foo' );
  *
+ *		// Check only fakeParagraphElement.
+ *		schema.checkChild( 'paragraph', 'foo' );
+ *
  *		// Check in [ fakeRootElement, fakeBarElement, paragraphElement ].
  *		schema.checkChild( [ '$root', 'bar', paragraphElement ], 'foo' );
  *
@@ -1294,7 +1299,7 @@ export class SchemaContext {
  *		schema.checkChild( [ ...positionInParagraph.getAncestors(), '$text' ], 'bold' );
  *
  * @typedef {module:engine/model/node~Node|module:engine/model/position~Position|module:engine/model/schema~SchemaContext|
- * Array.<String|module:engine/model/node~Node>} module:engine/model/schema~SchemaContextDefinition
+ * String|Array.<String|module:engine/model/node~Node>} module:engine/model/schema~SchemaContextDefinition
  */
 
 /**

+ 2 - 5
packages/ckeditor5-engine/src/model/writer.js

@@ -873,7 +873,6 @@ export default class Writer {
 	 * it will be destroyed using operation.
 	 *
 	 * @param {module:engine/model/markercollection~Marker|String} markerOrName Marker or marker name to remove.
-	 * @param {Object} [options]
 	 */
 	removeMarker( markerOrName ) {
 		this._assertWriterUsedCorrectly();
@@ -908,8 +907,6 @@ export default class Writer {
 	 * {@link module:engine/model/element~Element element}, {@link module:engine/model/position~Position position},
 	 * {@link module:engine/model/range~Range range}, an iterable of {@link module:engine/model/range~Range ranges} or null.
 	 *
-	 * Uses internally {@link module:engine/model/documentselection~DocumentSelection#_setTo}.
-	 *
 	 *		// Sets ranges from the given range.
 	 *		const range = new Range( start, end );
 	 *		writer.setSelection( range, isBackwardSelection );
@@ -926,11 +923,11 @@ export default class Writer {
 	 *		const documentSelection = new DocumentSelection( doc );
 	 *		writer.setSelection( documentSelection );
 	 *
-	 * 		// Sets range at the given position.
+	 * 		// Sets collapsed range at the given position.
 	 *		const position = new Position( root, path );
 	 *		writer.setSelection( position );
 	 *
-	 * 		// Sets range at the given element.
+	 * 		// Sets collapsed range at the given offset in element.
 	 *		const paragraph = writer.createElement( 'paragraph' );
 	 *		writer.setSelection( paragraph, offset );
 	 *

+ 18 - 4
packages/ckeditor5-engine/tests/controller/datacontroller.js

@@ -25,15 +25,18 @@ describe( 'DataController', () => {
 
 	beforeEach( () => {
 		model = new Model();
+
+		schema = model.schema;
 		modelDocument = model.document;
+
 		modelDocument.createRoot();
-		modelDocument.createRoot( '$root', 'title' );
+		modelDocument.createRoot( '$title', 'title' );
+
+		schema.register( '$title', { inheritAllFrom: '$root' } );
 
 		htmlDataProcessor = new HtmlDataProcessor();
 
 		data = new DataController( model, htmlDataProcessor );
-
-		schema = model.schema;
 	} );
 
 	describe( 'constructor()', () => {
@@ -143,7 +146,7 @@ describe( 'DataController', () => {
 	} );
 
 	describe( 'set()', () => {
-		it( 'should set data to root', () => {
+		it( 'should set data to default main root', () => {
 			schema.extend( '$text', { allowIn: '$root' } );
 			data.set( 'foo' );
 
@@ -179,6 +182,17 @@ describe( 'DataController', () => {
 			expect( count( modelDocument.history.getDeltas() ) ).to.equal( 2 );
 		} );
 
+		it( 'should parse given data before set in a context of correct root', () => {
+			schema.extend( '$text', { allowIn: '$title', disallowIn: '$root' } );
+			data.set( 'foo', 'main' );
+			data.set( 'Bar', 'title' );
+
+			expect( getData( model, { withoutSelection: true, rootName: 'main' } ) ).to.equal( '' );
+			expect( getData( model, { withoutSelection: true, rootName: 'title' } ) ).to.equal( 'Bar' );
+
+			expect( count( modelDocument.history.getDeltas() ) ).to.equal( 2 );
+		} );
+
 		// This case was added when order of params was different and it really didn't work. Let's keep it
 		// if anyone will ever try to change this.
 		it( 'should allow setting empty data', () => {

+ 30 - 0
packages/ckeditor5-engine/tests/conversion/view-to-model-converters.js

@@ -151,5 +151,35 @@ describe( 'view-to-model-converters', () => {
 			expect( conversionResult.getChild( 0 ).maxOffset ).to.equal( 3 );
 			expect( conversionResult.getChild( 0 ).getChild( 0 ).data ).to.equal( 'foo' );
 		} );
+
+		it( 'should forward correct modelCursor', () => {
+			const spy = sinon.spy();
+			const view = new ViewDocumentFragment( [
+				new ViewContainerElement( 'div', null, [ new ViewText( 'abc' ), new ViewContainerElement( 'foo' ) ] ),
+				new ViewContainerElement( 'bar' )
+			] );
+			const position = ModelPosition.createAt( new ModelElement( 'element' ) );
+
+			dispatcher.on( 'documentFragment', convertToModelFragment() );
+			dispatcher.on( 'element', convertToModelFragment(), { priority: 'lowest' } );
+			dispatcher.on( 'element:foo', ( evt, data ) => {
+				// Be sure that current cursor is not the same as custom.
+				expect( data.modelCursor ).to.not.equal( position );
+				// Set custom cursor as a result of docFrag last child conversion.
+				// This cursor should be forwarded by a documentFragment converter.
+				data.modelCursor = position;
+				// Be sure that callback was fired.
+				spy();
+			} );
+
+			dispatcher.on( 'element:bar', ( evt, data ) => {
+				expect( data.modelCursor ).to.equal( position );
+				spy();
+			} );
+
+			dispatcher.convert( view );
+
+			sinon.assert.calledTwice( spy );
+		} );
 	} );
 } );

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

@@ -2690,6 +2690,12 @@ describe( 'SchemaContext', () => {
 			expect( Array.from( ctx.getItem( 2 ).getAttributeKeys() ).sort() ).to.deep.equal( [ 'align' ] );
 		} );
 
+		it( 'creates context based on a string', () => {
+			const ctx = new SchemaContext( 'paragraph' );
+
+			expect( Array.from( ctx.getNames() ) ).to.deep.equal( [ 'paragraph' ] );
+		} );
+
 		it( 'creates context based on a SchemaContext instance', () => {
 			const previousCtx = new SchemaContext( [ 'a', 'b', 'c' ] );