Przeglądaj źródła

Merge pull request #729 from ckeditor/t/728

Added getSelectedElement to model selection.
Piotrek Koszuliński 9 lat temu
rodzic
commit
332962737f

+ 20 - 0
packages/ckeditor5-engine/src/model/selection.js

@@ -8,6 +8,7 @@
  */
  */
 
 
 import Position from './position.js';
 import Position from './position.js';
+import Element from './element.js';
 import Range from './range.js';
 import Range from './range.js';
 import EmitterMixin from '../../utils/emittermixin.js';
 import EmitterMixin from '../../utils/emittermixin.js';
 import CKEditorError from '../../utils/ckeditorerror.js';
 import CKEditorError from '../../utils/ckeditorerror.js';
@@ -541,6 +542,25 @@ export default class Selection {
 		}
 		}
 	}
 	}
 
 
+	/**
+	 * Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only
+	 * one range in the selection, and that range contains exactly one element.
+	 * Returns `null` if there is no selected element.
+	 *
+	 * @returns {module:engine/model/element~Element|null}
+	 */
+	getSelectedElement() {
+		if ( this.rangeCount !== 1 ) {
+			return null;
+		}
+
+		const range = this.getFirstRange();
+		const nodeAfterStart = range.start.nodeAfter;
+		const nodeBeforeEnd = range.end.nodeBefore;
+
+		return ( nodeAfterStart instanceof Element && nodeAfterStart == nodeBeforeEnd ) ? nodeAfterStart : null;
+	}
+
 	/**
 	/**
 	 * Creates and returns an instance of `Selection` that is a clone of given selection, meaning that it has same
 	 * Creates and returns an instance of `Selection` that is a clone of given selection, meaning that it has same
 	 * ranges and same direction as this selection.
 	 * ranges and same direction as this selection.

+ 39 - 0
packages/ckeditor5-engine/tests/model/selection.js

@@ -15,6 +15,8 @@ import Selection from 'ckeditor5/engine/model/selection.js';
 import CKEditorError from 'ckeditor5/utils/ckeditorerror.js';
 import CKEditorError from 'ckeditor5/utils/ckeditorerror.js';
 import testUtils from 'tests/core/_utils/utils.js';
 import testUtils from 'tests/core/_utils/utils.js';
 import count from 'ckeditor5/utils/count.js';
 import count from 'ckeditor5/utils/count.js';
+import { parse } from 'ckeditor5/engine/dev-utils/model.js';
+import Schema from 'ckeditor5/engine/model/schema.js';
 
 
 testUtils.createSinonSandbox();
 testUtils.createSinonSandbox();
 
 
@@ -986,4 +988,41 @@ describe( 'Selection', () => {
 			} );
 			} );
 		} );
 		} );
 	} );
 	} );
+
+	describe( 'getSelectedElement', () => {
+		let schema;
+
+		beforeEach( () => {
+			schema = new Schema();
+			schema.registerItem( 'p', '$block' );
+		} );
+		it( 'should return selected element', () => {
+			const { selection, model } = parse( '<p>foo</p>[<p>bar</p>]<p>baz</p>', schema );
+			const p = model.getChild( 1 );
+
+			expect( selection.getSelectedElement() ).to.equal( p );
+		} );
+
+		it( 'should return null if there is more than one range', () => {
+			const { selection } = parse( '[<p>foo</p>][<p>bar</p>]<p>baz</p>', schema );
+
+			expect( selection.getSelectedElement() ).to.be.null;
+		} );
+
+		it( 'should return null if there is no selection', () => {
+			expect( selection.getSelectedElement() ).to.be.null;
+		} );
+
+		it( 'should return null if selection is not over single element #1', () => {
+			const { selection } = parse( '<p>foo</p>[<p>bar</p><p>baz}</p>', schema );
+
+			expect( selection.getSelectedElement() ).to.be.null;
+		} );
+
+		it( 'should return null if selection is not over single element #2', () => {
+			const { selection } = parse( '<p>{bar}</p>', schema );
+
+			expect( selection.getSelectedElement() ).to.be.null;
+		} );
+	} );
 } );
 } );