8
0
Просмотр исходного кода

Merge branch 'master' into t/1125

Szymon Cofalik 7 лет назад
Родитель
Сommit
0b4df26f00

+ 1 - 1
packages/ckeditor5-engine/package.json

@@ -25,7 +25,7 @@
     "eslint": "^4.15.0",
     "eslint-config-ckeditor5": "^1.0.7",
     "husky": "^0.14.3",
-    "lint-staged": "^6.0.0"
+    "lint-staged": "^7.0.0"
   },
   "engines": {
     "node": ">=6.0.0",

+ 3 - 0
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -184,6 +184,7 @@ export default class DataController {
 	 * @fires init
 	 * @param {String} data Input data.
 	 * @param {String} [rootName='main'] Root name.
+	 * @returns {Promise} Promise that is resolved after the data is set on the editor.
 	 */
 	init( data, rootName = 'main' ) {
 		if ( this.model.document.version ) {
@@ -202,6 +203,8 @@ export default class DataController {
 		this.model.enqueueChange( 'transparent', writer => {
 			writer.insert( this.parse( data, modelRoot ), modelRoot );
 		} );
+
+		return Promise.resolve();
 	}
 
 	/**

+ 8 - 1
packages/ckeditor5-engine/src/conversion/downcast-converters.js

@@ -427,7 +427,14 @@ function _normalizeToAttributeConfig( view ) {
 	if ( typeof view == 'string' ) {
 		return modelAttributeValue => ( { key: view, value: modelAttributeValue } );
 	} else if ( typeof view == 'object' ) {
-		return () => view;
+		// { key, value, ... }
+		if ( view.value ) {
+			return () => view;
+		}
+		// { key, ... }
+		else {
+			return modelAttributeValue => ( { key: view.key, value: modelAttributeValue } );
+		}
 	} else {
 		// function.
 		return view;

+ 2 - 2
packages/ckeditor5-engine/src/conversion/upcast-converters.js

@@ -424,13 +424,13 @@ function _normalizeViewAttributeKeyValueConfig( config ) {
 // `config.model` is an `Object` with `key` and `value` properties.
 //
 // @param {Object} config Conversion config.
-// @param {String} viewAttributeKeyToCopy Key of the  converted view attribute. If it is set, model attribute value
+// @param {String} viewAttributeKeyToCopy Key of the converted view attribute. If it is set, model attribute value
 // will be equal to view attribute value.
 function _normalizeModelAttributeConfig( config, viewAttributeKeyToCopy = null ) {
 	const defaultModelValue = viewAttributeKeyToCopy === null ? true : viewElement => viewElement.getAttribute( viewAttributeKeyToCopy );
 
 	const key = typeof config.model != 'object' ? config.model : config.model.key;
-	const value = typeof config.model != 'object' ? defaultModelValue : config.model.value;
+	const value = typeof config.model != 'object' || typeof config.model.value == 'undefined' ? defaultModelValue : config.model.value;
 
 	config.model = { key, value };
 }

+ 22 - 1
packages/ckeditor5-engine/src/model/differ.js

@@ -19,7 +19,20 @@ import Range from './range';
  * elements and new ones and returns a change set.
  */
 export default class Differ {
-	constructor() {
+	/**
+	 * Creates a `Differ` instance.
+	 *
+	 * @param {module:engine/model/markercollection~MarkerCollection} markerCollection Model's marker collection.
+	 */
+	constructor( markerCollection ) {
+		/**
+		 * Reference to the model's marker collection.
+		 *
+		 * @private
+		 * @type {module:engine/model/markercollection~MarkerCollection}
+		 */
+		this._markerCollection = markerCollection;
+
 		/**
 		 * A map that stores changes that happened in a given element.
 		 *
@@ -153,6 +166,14 @@ export default class Differ {
 				this._markRemove( operation.position.parent, operation.position.offset, 1 );
 				this._markInsert( operation.position.parent, operation.position.offset, 1 );
 
+				const range = Range.createFromPositionAndShift( operation.position, 1 );
+
+				for ( const marker of this._markerCollection.getMarkersIntersectingRange( range ) ) {
+					const markerRange = marker.getRange();
+
+					this.bufferMarkerChange( marker.name, markerRange, markerRange );
+				}
+
 				break;
 			}
 		}

+ 1 - 1
packages/ckeditor5-engine/src/model/document.js

@@ -87,7 +87,7 @@ export default class Document {
 		 * @readonly
 		 * @member {module:engine/model/differ~Differ}
 		 */
-		this.differ = new Differ();
+		this.differ = new Differ( model.markers );
 
 		/**
 		 * Post-fixer callbacks registered to the model document.

+ 14 - 0
packages/ckeditor5-engine/src/model/markercollection.js

@@ -163,6 +163,20 @@ export default class MarkerCollection {
 	}
 
 	/**
+	 * Returns iterator that iterates over all markers, which intersects with given {@link module:engine/model/range~Range range}.
+	 *
+	 * @param {module:engine/model/range~Range} range
+	 * @returns {Iterable.<module:engine/model/markercollection~Marker>}
+	 */
+	* getMarkersIntersectingRange( range ) {
+		for ( const marker of this ) {
+			if ( marker.getRange().getIntersection( range ) !== null ) {
+				yield marker;
+			}
+		}
+	}
+
+	/**
 	 * Destroys marker collection and all markers inside it.
 	 */
 	destroy() {

+ 8 - 0
packages/ckeditor5-engine/tests/controller/datacontroller.js

@@ -206,6 +206,14 @@ describe( 'DataController', () => {
 
 			expect( spy.calledOnce ).to.be.true;
 		} );
+
+		it( 'should return a resolved Promise', () => {
+			const promise = data.init( '<p>Foo</p>' );
+
+			expect( promise ).to.be.instanceof( Promise );
+
+			return promise;
+		} );
 	} );
 
 	describe( 'set()', () => {

+ 19 - 0
packages/ckeditor5-engine/tests/conversion/conversion.js

@@ -585,6 +585,25 @@ describe( 'Conversion', () => {
 					'<p class="align-center">Foo</p>'
 				);
 			} );
+
+			it( 'config.view and config.model have name and key set', () => {
+				schema.extend( 'image', {
+					allowAttributes: [ 'source' ]
+				} );
+
+				conversion.attributeToAttribute( {
+					model: {
+						name: 'image',
+						key: 'source'
+					},
+					view: {
+						name: 'img',
+						key: 'src'
+					}
+				} );
+
+				test( '<img src="foo.jpg"></img>', '<image source="foo.jpg"></image>' );
+			} );
 		} );
 
 		function test( input, expectedModel, expectedView = null ) {

+ 35 - 0
packages/ckeditor5-engine/tests/conversion/downcast-converters.js

@@ -390,6 +390,41 @@ describe( 'downcast-helpers', () => {
 			expectResult( '<p></p>' );
 		} );
 
+		it( 'config.view is an object, only name and key are provided', () => {
+			conversion.for( 'downcast' ).add( downcastElementToElement( { model: 'paragraph', view: 'p' } ) );
+
+			const helper = downcastAttributeToAttribute( {
+				model: {
+					name: 'paragraph',
+					key: 'class'
+				},
+				view: {
+					name: 'paragraph',
+					key: 'class'
+				}
+			} );
+
+			conversion.for( 'downcast' ).add( helper );
+
+			model.change( writer => {
+				writer.insertElement( 'paragraph', { class: 'dark' }, modelRoot, 0 );
+			} );
+
+			expectResult( '<p class="dark"></p>' );
+
+			model.change( writer => {
+				writer.setAttribute( 'class', 'light', modelRoot.getChild( 0 ) );
+			} );
+
+			expectResult( '<p class="light"></p>' );
+
+			model.change( writer => {
+				writer.removeAttribute( 'class', modelRoot.getChild( 0 ) );
+			} );
+
+			expectResult( '<p></p>' );
+		} );
+
 		it( 'config.view is a function', () => {
 			const helper = downcastAttributeToAttribute( {
 				model: 'styled',

+ 15 - 0
packages/ckeditor5-engine/tests/conversion/upcast-converters.js

@@ -384,6 +384,21 @@ describe( 'upcast-helpers', () => {
 			);
 		} );
 
+		it( 'config.view has only key and name set', () => {
+			schema.extend( 'image', {
+				allowAttributes: [ 'source' ]
+			} );
+
+			const helper = upcastAttributeToAttribute( { view: { name: 'img', key: 'src' }, model: { name: 'image', key: 'source' } } );
+
+			conversion.for( 'upcast' ).add( helper );
+
+			expectResult(
+				new ViewAttributeElement( 'img', { src: 'foo.jpg' } ),
+				'<image source="foo.jpg"></image>'
+			);
+		} );
+
 		it( 'can be overwritten using priority', () => {
 			schema.extend( 'image', {
 				allowAttributes: [ 'src', 'source' ]

+ 36 - 0
packages/ckeditor5-engine/tests/model/differ.js

@@ -653,6 +653,42 @@ describe( 'Differ', () => {
 				] );
 			} );
 		} );
+
+		it( 'markers refreshing', () => {
+			// Since rename is "translated" to remove+insert pair, we need to refresh all the markers that
+			// intersect with the renamed element.
+
+			model.change( () => {
+				// Renamed element contains marker.
+				model.markers._set( 'markerA', new Range( new Position( root, [ 1, 1 ] ), new Position( root, [ 1, 2 ] ) ) );
+
+				// Marker contains renamed element.
+				model.markers._set( 'markerB', new Range( new Position( root, [ 0 ] ), new Position( root, [ 2 ] ) ) );
+
+				// Intersecting.
+				model.markers._set( 'markerC', new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 1, 2 ] ) ) );
+
+				// Not intersecting.
+				model.markers._set( 'markerD', new Range( new Position( root, [ 0, 0 ] ), new Position( root, [ 1 ] ) ) );
+			} );
+
+			const markersToRefresh = [ 'markerA', 'markerB', 'markerC' ];
+
+			model.change( () => {
+				rename( root.getChild( 1 ), 'listItem' );
+
+				expectChanges( [
+					{ type: 'remove', name: 'paragraph', length: 1, position: new Position( root, [ 1 ] ) },
+					{ type: 'insert', name: 'listItem', length: 1, position: new Position( root, [ 1 ] ) }
+				] );
+
+				const markersToRemove = differ.getMarkersToRemove().map( entry => entry.name );
+				const markersToAdd = differ.getMarkersToAdd().map( entry => entry.name );
+
+				expect( markersToRefresh ).to.deep.equal( markersToRemove );
+				expect( markersToRefresh ).to.deep.equal( markersToAdd );
+			} );
+		} );
 	} );
 
 	describe( 'attribute', () => {

+ 7 - 0
packages/ckeditor5-engine/tests/model/range.js

@@ -788,6 +788,13 @@ describe( 'Range', () => {
 			expect( common.start.path ).to.deep.equal( [ 3, 2 ] );
 			expect( common.end.path ).to.deep.equal( [ 4, 7 ] );
 		} );
+
+		it( 'should return a range equal to both ranges if both ranges are equal', () => {
+			const otherRange = Range.createFromRange( range );
+			const common = range.getIntersection( otherRange );
+
+			expect( common.isEqual( range ) ).to.be.true;
+		} );
 	} );
 
 	// Note: We don't create model element structure in these tests because this method