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

Changed: introduced selection direction handling in DOM<->view selection conversion.

Szymon Cofalik 9 лет назад
Родитель
Сommit
e31de61a8a

+ 29 - 1
packages/ckeditor5-engine/src/view/domconverter.js

@@ -360,12 +360,15 @@ export default class DomConverter {
 	domSelectionToView( domSelection ) {
 		const viewSelection = new ViewSelection();
 
+		const isBackward = this.isDomSelectionBackward( domSelection );
+
 		for ( let i = 0; i < domSelection.rangeCount; i++ ) {
+			// DOM Range have correct start and end, no matter what is the DOM Selection direction. So we don't have to fix anything.
 			const domRange = domSelection.getRangeAt( i );
 			const viewRange = this.domRangeToView( domRange );
 
 			if ( viewRange ) {
-				viewSelection.addRange( viewRange );
+				viewSelection.addRange( viewRange, isBackward );
 			}
 		}
 
@@ -675,6 +678,31 @@ export default class DomConverter {
 	}
 
 	/**
+	 * Returns `true` if given selection is a backward selection, that is, if it's `focus` is before `anchor`.
+	 *
+	 * @param {Selection} DOM Selection instance to check.
+	 * @return {Boolean}
+	 */
+	isDomSelectionBackward( selection ) {
+		if ( selection.isCollapsed ) {
+			return false;
+		}
+
+		// Since it takes multiple lines of code to check whether a "DOM Position" is before/after another "DOM Position",
+		// we will use the fact that range will collapse if it's end is before it's start.
+		const range = new Range();
+
+		range.setStart( selection.anchorNode, selection.anchorOffset );
+		range.setEnd( selection.focusNode, selection.focusOffset );
+
+		const backward = range.collapsed;
+
+		range.detach();
+
+		return backward;
+	}
+
+	/**
 	 * Takes text data from given {@link engine.view.Text#data} and processes it so it is correctly displayed in DOM.
 	 *
 	 * Following changes are done:

+ 12 - 15
packages/ckeditor5-engine/src/view/renderer.js

@@ -422,7 +422,7 @@ export default class Renderer {
 	_updateSelection() {
 		// If there is no selection - remove it from DOM elements that belongs to the editor.
 		if ( this.selection.rangeCount === 0 ) {
-			this._removeDomSelction();
+			this._removeDomSelection();
 
 			return;
 		}
@@ -445,22 +445,19 @@ export default class Renderer {
 			return;
 		}
 
-		domSelection.removeAllRanges();
-
-		for ( let range of this.selection.getRanges() ) {
-			// Update ranges only in currently selected editable.
-			if ( range.start.parent.root == selectedEditable ) {
-				const domRangeStart = this.domConverter.viewPositionToDom( range.start );
-				const domRangeEnd = this.domConverter.viewPositionToDom( range.end );
-				const domRange = new Range();
-				domRange.setStart( domRangeStart.parent, domRangeStart.offset );
-				domRange.setEnd( domRangeEnd.parent, domRangeEnd.offset );
-				domSelection.addRange( domRange );
-			}
-		}
+		// Multi-range selection is not available in most browsers, and, at least in Chrome, trying to
+		// set such selection, that is not continuous, throws an error. Because of that, we will just use anchor
+		// and focus of view selection.
+		// Since we are not supporting multi-range selection, we also do not need to check if proper editable is
+		// selected. If there is any editable selected, it is okay (editable is taken from selection anchor).
+		const anchor = this.domConverter.viewPositionToDom( this.selection.anchor );
+		const focus = this.domConverter.viewPositionToDom( this.selection.focus );
+
+		domSelection.collapse( anchor.parent, anchor.offset );
+		domSelection.extend( focus.parent, focus.offset );
 	}
 
-	_removeDomSelction() {
+	_removeDomSelection() {
 		for ( let doc of this.domDocuments ) {
 			const domSelection = doc.getSelection();
 

+ 2 - 2
packages/ckeditor5-engine/src/view/selection.js

@@ -117,8 +117,8 @@ export default class Selection {
 	 * @type {engine.view.EditableElement|null}
 	 */
 	get editableElement() {
-		if ( this.rangeCount ) {
-			return this.getFirstPosition().editableElement;
+		if ( this.anchor ) {
+			return this.anchor.editableElement;
 		}
 
 		return null;

+ 2 - 20
packages/ckeditor5-engine/tests/view/renderer.js

@@ -291,6 +291,8 @@ describe( 'Renderer', () => {
 		} );
 
 		it( 'should not care about filler if there is no DOM', () => {
+			selectionEditable = null;
+
 			const { view: viewP, selection: newSelection } = parse(
 				'<container:p>foo<attribute:b>[]</attribute:b>bar</container:p>' );
 
@@ -918,26 +920,6 @@ describe( 'Renderer', () => {
 			expect( domSelection.getRangeAt( 0 ).collapsed ).to.equal( true );
 		} );
 
-		it( 'should not add ranges if different editable is selected', () => {
-			const domHeader = document.createElement( 'h1' );
-			const viewHeader = new ViewElement( 'h1' );
-			document.body.appendChild( domHeader );
-
-			domConverter.bindElements( domHeader, viewHeader );
-
-			selectionEditable = viewHeader;
-
-			const { view: viewP, selection: newSelection } = parse( '<container:p>fo{o}</container:p>' );
-
-			viewRoot.appendChildren( viewP );
-			selection.setTo( newSelection );
-
-			renderer.render();
-
-			const domSelection = document.getSelection();
-			expect( domSelection.rangeCount ).to.equal( 0 );
-		} );
-
 		it( 'should not add inline filler after text node', () => {
 			const domSelection = document.getSelection();