Procházet zdrojové kódy

Merge branch 'master' into i/5802

# Conflicts:
#	src/restrictededitingmodeediting.js
#	tests/restrictededitingmodeediting.js
Maciej Gołaszewski před 6 roky
rodič
revize
8b5a83e239

+ 29 - 0
packages/ckeditor5-restricted-editing/src/restrictededitingmodeediting.js

@@ -159,6 +159,7 @@ export default class RestrictedEditingModeEditing extends Plugin {
 
 		doc.registerPostFixer( extendMarkerOnTypingPostFixer( editor ) );
 		doc.registerPostFixer( resurrectCollapsedMarkerPostFixer( editor ) );
+		model.markers.on( 'update:restrictedEditingException', ensureNewMarkerIsFlat( editor ) );
 
 		setupExceptionHighlighting( editor );
 	}
@@ -395,6 +396,34 @@ function isRangeInsideSingleMarker( editor, range ) {
 	return markerAtStart && markerAtEnd && markerAtEnd === markerAtStart;
 }
 
+// Checks if new marker range is flat. Non-flat ranges might appear during upcast conversion in nested structures, ie tables.
+//
+// Note: This marker fixer only consider case which is possible to create using StandardEditing mode plugin.
+// Markers created by developer in the data might break in many other ways.
+//
+// See #6003.
+function ensureNewMarkerIsFlat( editor ) {
+	const model = editor.model;
+
+	return ( evt, marker, oldRange, newRange ) => {
+		if ( !oldRange && !newRange.isFlat ) {
+			model.change( writer => {
+				const start = newRange.start;
+				const end = newRange.end;
+
+				const startIsHigherInTree = start.path.length > end.path.length;
+
+				const fixedStart = startIsHigherInTree ? newRange.start : writer.createPositionAt( end.parent, 0 );
+				const fixedEnd = startIsHigherInTree ? writer.createPositionAt( start.parent, 'end' ) : newRange.end;
+
+				writer.updateMarker( marker, {
+					range: writer.createRange( fixedStart, fixedEnd )
+				} );
+			} );
+		}
+	};
+}
+
 function onlyAllowAttributesFromList( allowedAttributes ) {
 	return ( context, attributeName ) => {
 		if ( isClipboardContext( context ) ) {

+ 9 - 0
packages/ckeditor5-restricted-editing/tests/manual/restrictedediting.html

@@ -9,6 +9,15 @@
 	<p>Paragraph <span class="restricted-editing-exception">it is editable</span></p>
 	<p>Exception on part of a word: <a href="ckeditor.com">coompi</a><span class="restricted-editing-exception"><a href="ckeditor.com">ter</a> (fix spelling in Firefox).</span></p>
 	<p><strong>Bold</strong> <i>Italic</i> <a href="foo">Link</a></p>
+	<figure class="table">
+		<table>
+			<tbody>
+				<tr>
+					<td><span class="restricted-editing-exception">foo bar</span></td>
+				</tr>
+			</tbody>
+		</table>
+	</figure>
 	<ul>
 		<li>UL List item 1</li>
 		<li>UL List item 2</li>

+ 100 - 3
packages/ckeditor5-restricted-editing/tests/restrictededitingmodeediting.js

@@ -20,6 +20,7 @@ import RestrictedEditingModeEditing from './../src/restrictededitingmodeediting'
 import RestrictedEditingModeNavigationCommand from '../src/restrictededitingmodenavigationcommand';
 import ItalicEditing from '@ckeditor/ckeditor5-basic-styles/src/italic/italicediting';
 import BlockQuoteEditing from '@ckeditor/ckeditor5-block-quote/src/blockquoteediting';
+import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting';
 
 describe( 'RestrictedEditingModeEditing', () => {
 	let editor, model;
@@ -63,12 +64,12 @@ describe( 'RestrictedEditingModeEditing', () => {
 
 	describe( 'conversion', () => {
 		beforeEach( async () => {
-			editor = await VirtualTestEditor.create( { plugins: [ Paragraph, RestrictedEditingModeEditing ] } );
+			editor = await VirtualTestEditor.create( { plugins: [ Paragraph, TableEditing, RestrictedEditingModeEditing ] } );
 			model = editor.model;
 		} );
 
-		afterEach( () => {
-			return editor.destroy();
+		afterEach( async () => {
+			await editor.destroy();
 		} );
 
 		describe( 'upcast', () => {
@@ -93,6 +94,21 @@ describe( 'RestrictedEditingModeEditing', () => {
 				assertMarkerRangePaths( [ 1, 6 ], [ 1, 11 ], 2 );
 			} );
 
+			it( 'should convert <span class="restricted-editing-exception"> inside table to marker', () => {
+				editor.setData(
+					'<figure class="table">' +
+						'<table><tbody><tr><td><span class="restricted-editing-exception">bar</span></td></tr></tbody></table>' +
+					'</figure>'
+				);
+
+				expect( model.markers.has( 'restrictedEditingException:1' ) ).to.be.true;
+
+				const marker = model.markers.get( 'restrictedEditingException:1' );
+
+				expect( marker.getStart().path ).to.deep.equal( [ 0, 0, 0, 0, 0 ] );
+				expect( marker.getEnd().path ).to.deep.equal( [ 0, 0, 0, 0, 3 ] );
+			} );
+
 			it( 'should not convert other <span> elements', () => {
 				editor.setData( '<p>foo <span class="foo bar">bar</span> baz</p>' );
 
@@ -161,6 +177,87 @@ describe( 'RestrictedEditingModeEditing', () => {
 					'</p>'
 				);
 			} );
+
+			it( 'converted <span> should be the outermost attribute element', () => {
+				editor.conversion.for( 'downcast' ).attributeToElement( { model: 'bold', view: 'b' } );
+				setModelData( model,
+					'<table><tableRow><tableCell>' +
+					'<paragraph><$text bold="true">foo bar baz</$text></paragraph>' +
+					'</tableCell></tableRow></table>'
+				);
+
+				const paragraph = model.document.getRoot().getChild( 0 ).getChild( 0 ).getChild( 0 ).getChild( 0 );
+
+				model.change( writer => {
+					writer.addMarker( 'restrictedEditingException:1', {
+						range: writer.createRange( writer.createPositionAt( paragraph, 0 ), writer.createPositionAt( paragraph, 'end' ) ),
+						usingOperation: true,
+						affectsData: true
+					} );
+				} );
+
+				assertEqualMarkup( editor.getData(),
+					'<figure class="table"><table><tbody><tr><td>' +
+					'<span class="restricted-editing-exception"><b>foo bar baz</b></span>' +
+					'</td></tr></tbody></table></figure>'
+				);
+				assertEqualMarkup( getViewData( editor.editing.view, { withoutSelection: true } ),
+					'<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
+					'<div class="ck ck-widget__selection-handle"></div>' +
+					'<table><tbody><tr><td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
+					'<span><span class="restricted-editing-exception"><b>foo bar baz</b></span></span>' +
+					'</td></tr></tbody></table>' +
+					'</figure>'
+				);
+			} );
+		} );
+
+		describe( 'flattening exception markers', () => {
+			it( 'should fix non-flat marker range (start is higher in tree)', () => {
+				setModelData( model, '<table><tableRow><tableCell><paragraph>foo bar baz</paragraph></tableCell></tableRow></table>' );
+				const tableCell = model.document.getRoot().getNodeByPath( [ 0, 0, 0 ] );
+				const paragraph = model.document.getRoot().getNodeByPath( [ 0, 0, 0, 0 ] );
+
+				model.change( writer => {
+					writer.addMarker( `restrictedEditingException:${ 1 }`, {
+						range: writer.createRange(
+							writer.createPositionAt( paragraph, 0 ),
+							writer.createPositionAt( tableCell, 'end' )
+						),
+						usingOperation: true,
+						affectsData: true
+					} );
+				} );
+
+				const marker = model.markers.get( 'restrictedEditingException:1' );
+
+				expect( marker.getStart().parent ).to.equal( marker.getEnd().parent );
+				expect( marker.getStart().path ).to.deep.equal( [ 0, 0, 0, 0, 0 ] );
+				expect( marker.getEnd().path ).to.deep.equal( [ 0, 0, 0, 0, 11 ] );
+			} );
+
+			it( 'should fix non-flat marker range (end is higher in tree)', () => {
+				setModelData( model, '<table><tableRow><tableCell><paragraph>foo bar baz</paragraph></tableCell></tableRow></table>' );
+				const tableCell = model.document.getRoot().getNodeByPath( [ 0, 0, 0 ] );
+				const paragraph = model.document.getRoot().getNodeByPath( [ 0, 0, 0, 0 ] );
+
+				model.change( writer => {
+					writer.addMarker( `restrictedEditingException:${ 1 }`, {
+						range: writer.createRange(
+							writer.createPositionAt( tableCell, 0 ),
+							writer.createPositionAt( paragraph, 'end' )
+						),
+						usingOperation: true,
+						affectsData: true
+					} );
+				} );
+
+				const marker = model.markers.get( 'restrictedEditingException:1' );
+
+				expect( marker.getStart().parent ).to.equal( marker.getEnd().parent );
+				expect( marker.getStart().path ).to.deep.equal( [ 0, 0, 0, 0, 0 ] );
+				expect( marker.getEnd().path ).to.deep.equal( [ 0, 0, 0, 0, 11 ] );
+			} );
 		} );
 	} );