Przeglądaj źródła

Added: Introduced marker-name-to-elements mapping in `conversion.Mapper`.

Szymon Cofalik 7 lat temu
rodzic
commit
abc0496315

+ 48 - 1
packages/ckeditor5-engine/src/conversion/mapper.js

@@ -18,7 +18,8 @@ import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 
 /**
- * Maps elements and positions between {@link module:engine/view/document~Document view} and {@link module:engine/model/model model}.
+ * Maps elements, positions and markers between {@link module:engine/view/document~Document the view} and
+ * {@link module:engine/model/model the model}.
  *
  * Mapper use bound elements to find corresponding elements and positions, so, to get proper results,
  * all model elements should be {@link module:engine/conversion/mapper~Mapper#bindElements bound}.
@@ -62,6 +63,17 @@ export default class Mapper {
 		 */
 		this._viewToModelLengthCallbacks = new Map();
 
+		/**
+		 * Model marker name to view elements mapping.
+		 *
+		 * Keys are `String`s while values are `Set`s with {@link module:engine/view/element~Element view elements}.
+		 * One marker (name) can be mapped to multiple elements.
+		 *
+		 * @private
+		 * @member {Map}
+		 */
+		this._markerNameToElements = new Map();
+
 		// Default mapper algorithm for mapping model position to view position.
 		this.on( 'modelToViewPosition', ( evt, data ) => {
 			if ( data.viewPosition ) {
@@ -149,12 +161,37 @@ export default class Mapper {
 		}
 	}
 
+	/**
+	 * Binds given marker name with given {@link module:engine/view/element~Element view element}. The element
+	 * will be added to the current set of elements bound with given marker name.
+	 *
+	 * @param {module:engine/view/element~Element} element Element to bind.
+	 * @param {String} name Marker name.
+	 */
+	bindElementToMarker( element, name ) {
+		const elements = this._markerNameToElements.get( name ) || new Set();
+
+		elements.add( element );
+
+		this._markerNameToElements.set( name, elements );
+	}
+
+	/**
+	 * Unbinds all elements from given marker name.
+	 *
+	 * @param {String} name Marker name.
+	 */
+	unbindElementsFromMarkerName( name ) {
+		this._markerNameToElements.delete( name );
+	}
+
 	/**
 	 * Removes all model to view and view to model bindings.
 	 */
 	clearBindings() {
 		this._modelToViewMapping = new WeakMap();
 		this._viewToModelMapping = new WeakMap();
+		this._markerNameToElements = new Map();
 	}
 
 	/**
@@ -239,6 +276,16 @@ export default class Mapper {
 		return data.viewPosition;
 	}
 
+	/**
+	 * Gets all view elements bound to the given marker name.
+	 *
+	 * @param {String} name Marker name.
+	 * @returns {Set.<module:engine/view/element~Element>} View elements bound with given marker name.
+	 */
+	markerNameToElements( name ) {
+		return this._markerNameToElements.get( name );
+	}
+
 	/**
 	 * Registers a callback that evaluates the length in the model of a view element with given name.
 	 *

+ 50 - 0
packages/ckeditor5-engine/tests/conversion/mapper.js

@@ -610,6 +610,56 @@ describe( 'Mapper', () => {
 		}
 	} );
 
+	describe( 'Markers mapping', () => {
+		let mapper;
+
+		beforeEach( () => {
+			mapper = new Mapper();
+		} );
+
+		it( 'should bind element to a marker name', () => {
+			const view = new ViewElement( 'a' );
+
+			mapper.bindElementToMarker( view, 'marker' );
+
+			const elements = mapper.markerNameToElements( 'marker' );
+
+			expect( elements ).to.be.instanceof( Set );
+			expect( elements.size ).to.equal( 1 );
+			expect( elements.has( view ) ).to.be.true;
+		} );
+
+		it( 'should bind multiple elements to a marker name', () => {
+			const viewA = new ViewElement( 'a' );
+			const viewB = new ViewElement( 'b' );
+			const viewC = new ViewElement( 'c' );
+
+			mapper.bindElementToMarker( viewA, 'marker' );
+			mapper.bindElementToMarker( viewB, 'marker' );
+			mapper.bindElementToMarker( viewC, 'marker' );
+
+			const elements = Array.from( mapper.markerNameToElements( 'marker' ) );
+
+			expect( elements ).to.deep.equal( [ viewA, viewB, viewC ] );
+		} );
+
+		it( 'should unbind all elements from a marker name', () => {
+			const viewA = new ViewElement( 'a' );
+			const viewB = new ViewElement( 'b' );
+			const viewC = new ViewElement( 'c' );
+
+			mapper.bindElementToMarker( viewA, 'marker' );
+			mapper.bindElementToMarker( viewB, 'marker' );
+			mapper.bindElementToMarker( viewC, 'marker' );
+
+			mapper.unbindElementsFromMarkerName( 'marker' );
+
+			const elements = mapper.markerNameToElements( 'marker' );
+
+			expect( elements ).to.be.undefined;
+		} );
+	} );
+
 	it( 'should pass isPhantom flag to model-to-view position mapping callback', () => {
 		const mapper = new Mapper();