Skip to content

Commit

Permalink
Merge pull request #9693 from ckeditor/ck/8462
Browse files Browse the repository at this point in the history
Fix (link): All text attributes starting their names with `link` will be removed when typing over a link or clicking at the end of the link. Closes #8462.
  • Loading branch information
psmyrek committed May 14, 2021
2 parents 6b4c948 + 66a6b68 commit 5a2fbb2
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 18 deletions.
36 changes: 22 additions & 14 deletions packages/ckeditor5-link/src/linkediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ export default class LinkEditing extends Plugin {
const editor = this.editor;
const model = editor.model;
const selection = model.document.selection;
const linkCommand = editor.commands.get( 'link' );

this.listenTo( model, 'insertContent', () => {
const nodeBefore = selection.anchor.nodeBefore;
Expand Down Expand Up @@ -295,7 +294,7 @@ export default class LinkEditing extends Plugin {
}

model.change( writer => {
removeLinkAttributesFromSelection( writer, linkCommand.manualDecorators );
removeLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );
} );
}, { priority: 'low' } );
}
Expand All @@ -313,7 +312,7 @@ export default class LinkEditing extends Plugin {
*/
_enableClickingAfterLink() {
const editor = this.editor;
const linkCommand = editor.commands.get( 'link' );
const model = editor.model;

editor.editing.view.addObserver( MouseObserver );

Expand All @@ -333,7 +332,7 @@ export default class LinkEditing extends Plugin {
// ...and it was caused by the click...
clicked = false;

const selection = editor.model.document.selection;
const selection = model.document.selection;

// ...and no text is selected...
if ( !selection.isCollapsed ) {
Expand All @@ -346,13 +345,13 @@ export default class LinkEditing extends Plugin {
}

const position = selection.getFirstPosition();
const linkRange = findAttributeRange( position, 'linkHref', selection.getAttribute( 'linkHref' ), editor.model );
const linkRange = findAttributeRange( position, 'linkHref', selection.getAttribute( 'linkHref' ), model );

// ...check whether clicked start/end boundary of the link.
// If so, remove the `linkHref` attribute.
if ( position.isTouching( linkRange.start ) || position.isTouching( linkRange.end ) ) {
editor.model.change( writer => {
removeLinkAttributesFromSelection( writer, linkCommand.manualDecorators );
model.change( writer => {
removeLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );
} );
}
} );
Expand Down Expand Up @@ -453,7 +452,6 @@ export default class LinkEditing extends Plugin {
const model = editor.model;
const selection = model.document.selection;
const view = editor.editing.view;
const linkCommand = editor.commands.get( 'link' );

// A flag whether attributes `linkHref` attribute should be preserved.
let shouldPreserveAttributes = false;
Expand Down Expand Up @@ -502,23 +500,23 @@ export default class LinkEditing extends Plugin {

// Use `model.enqueueChange()` in order to execute the callback at the end of the changes process.
editor.model.enqueueChange( writer => {
removeLinkAttributesFromSelection( writer, linkCommand.manualDecorators );
removeLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );
} );
}, { priority: 'low' } );
}
}

// Make the selection free of link-related model attributes.
// All link-related model attributes start with "link". That includes not only "linkHref"
// but also all decorator attributes (they have dynamic names).
// but also all decorator attributes (they have dynamic names), or even custom plugins.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:utils/collection~Collection} manualDecorators
function removeLinkAttributesFromSelection( writer, manualDecorators ) {
// @param {Array.<String>} linkAttributes
function removeLinkAttributesFromSelection( writer, linkAttributes ) {
writer.removeSelectionAttribute( 'linkHref' );

for ( const decorator of manualDecorators ) {
writer.removeSelectionAttribute( decorator.id );
for ( const attribute of linkAttributes ) {
writer.removeSelectionAttribute( attribute );
}
}

Expand Down Expand Up @@ -573,3 +571,13 @@ function isTyping( editor ) {

return input.isInput( editor.model.change( writer => writer.batch ) );
}

// Returns an array containing names of attributes allowed on `$text` that describes the link item.
//
// @param {module:engine/model/schema~Schema} schema
// @returns {Array.<String>}
function getLinkAttributesAllowedOnText( schema ) {
const textAttributes = schema.getDefinition( '$text' ).allowAttributes;

return textAttributes.filter( attribute => attribute.startsWith( 'link' ) );
}
27 changes: 23 additions & 4 deletions packages/ckeditor5-link/tests/linkediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -1093,24 +1093,43 @@ describe( 'LinkEditing', () => {
expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bar[]</$text></paragraph>' );
} );

it( 'should remove manual decorators', () => {
setModelData( model, '<paragraph><$text linkIsFoo="true" linkIsBar="true" linkHref="url">Bar[]</$text></paragraph>' );
it( 'should remove all `link*` attributes', () => {
allowLinkTarget( editor );

setModelData(
model,
'<paragraph><$text linkIsFoo="true" linkTarget="_blank" linkHref="https://ckeditor.com">Bar[]</$text></paragraph>'
);

editor.editing.view.document.fire( 'mousedown' );
editor.editing.view.document.fire( 'selectionChange', {
newSelection: view.document.selection
} );

expect( getModelData( model ) ).to.equal(
'<paragraph><$text linkHref="url" linkIsBar="true" linkIsFoo="true">Bar</$text>[]</paragraph>'
'<paragraph><$text linkHref="https://ckeditor.com" linkIsFoo="true" linkTarget="_blank">Bar</$text>[]</paragraph>'
);

editor.execute( 'input', { text: 'Foo' } );

expect( getModelData( model ) ).to.equal(
'<paragraph><$text linkHref="url" linkIsBar="true" linkIsFoo="true">Bar</$text>Foo[]</paragraph>'
'<paragraph><$text linkHref="https://ckeditor.com" linkIsFoo="true" linkTarget="_blank">Bar</$text>Foo[]</paragraph>'
);
} );

// Based on `packages/ckeditor5-engine/docs/_snippets/framework/extending-content-allow-link-target.js`.
// And covers #8462.
function allowLinkTarget( editor ) {
editor.model.schema.extend( '$text', { allowAttributes: 'linkTarget' } );

editor.conversion.for( 'downcast' ).attributeToElement( {
model: 'linkTarget',
view: ( attributeValue, { writer } ) => {
return writer.createAttributeElement( 'a', { target: attributeValue }, { priority: 5 } );
},
converterPriority: 'low'
} );
}
} );

// https://github.com/ckeditor/ckeditor5/issues/4762
Expand Down

0 comments on commit 5a2fbb2

Please sign in to comment.