Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #10 from ckeditor/i/6003
Browse files Browse the repository at this point in the history
Fix: Fix non-flat exception markers. Closes ckeditor/ckeditor5#6003.
  • Loading branch information
Reinmar authored Jan 2, 2020
2 parents c97ae3b + 13fdcd1 commit 70e3e12
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 3 deletions.
29 changes: 29 additions & 0 deletions src/restrictededitingmodeediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export default class RestrictedEditingModeEditing extends Plugin {

doc.registerPostFixer( extendMarkerOnTypingPostFixer( editor ) );
doc.registerPostFixer( resurrectCollapsedMarkerPostFixer( editor ) );
model.markers.on( 'update:restrictedEditingException', ensureNewMarkerIsFlat( editor ) );

setupExceptionHighlighting( editor );
}
Expand Down Expand Up @@ -377,3 +378,31 @@ 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 )
} );
} );
}
};
}
9 changes: 9 additions & 0 deletions tests/manual/restrictedediting.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ <h2>Heading 1</h2>
<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>
Expand Down
103 changes: 100 additions & 3 deletions tests/restrictededitingmodeediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';

import RestrictedEditingModeEditing from './../src/restrictededitingmodeediting';
import RestrictedEditingModeNavigationCommand from '../src/restrictededitingmodenavigationcommand';
import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting';

describe( 'RestrictedEditingModeEditing', () => {
let editor, model;
Expand Down Expand Up @@ -60,12 +61,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', () => {
Expand Down Expand Up @@ -96,6 +97,21 @@ describe( 'RestrictedEditingModeEditing', () => {
expect( secondMarker.getEnd().path ).to.deep.equal( [ 1, 11 ] );
} );

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>' );

Expand Down Expand Up @@ -164,6 +180,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 ] );
} );
} );
} );

Expand Down

0 comments on commit 70e3e12

Please sign in to comment.