Skip to content

Commit

Permalink
Merge pull request #4733 from ckeditor/t/4728
Browse files Browse the repository at this point in the history
Fix: prevent duplicate anchors in text with styles
  • Loading branch information
sculpt0r authored Jul 23, 2021
2 parents 330e8c7 + 91f6da3 commit 25418a1
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ CKEditor 4 Changelog
## CKEditor 4.16.2

Fixed Issues:
* [#4733](https://github.com/ckeditor/ckeditor4/pull/4733): Fixed: [Link](https://ckeditor.com/cke4/addon/link) prevent duplicate anchors in text with styles.
* [#4728](https://github.com/ckeditor/ckeditor4/issues/4728): Fixed: Multiple anchors in one line and multi-line with text style.
* [#3863](https://github.com/ckeditor/ckeditor4/issues/3863): Fixed: Multiple anchors in single word with text style.
* [#3819](https://github.com/ckeditor/ckeditor4/issues/3819): [Chrome] Fixed: After removing one of the two consecutive spaces, the ` ` character appears in the editor instead of a space.
* [#4666](https://github.com/ckeditor/ckeditor4/pull/4666): [IE] Introduce CSS.escape polyfill. Thanks to [limingli0707](https://github.com/limingli0707)!
* [#681](https://github.com/ckeditor/ckeditor4/issues/681): Fixed: Table elements (td, tr, th, ..) with an id that starts with dot (.) causes javascript runtime err.
Expand Down
27 changes: 27 additions & 0 deletions plugins/link/dialogs/anchor.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,37 @@ CKEDITOR.dialog.add( 'anchor', function( editor ) {
element = element.getParent();
}

// If anchor exists and has any styles find the closest parent <a> tag. (#3863)
if ( element && !element.is( 'a' ) ) {
element = element.getAscendant( 'a' ) || element;
}

if ( element && element.type === CKEDITOR.NODE_ELEMENT &&
( element.data( 'cke-real-element-type' ) === 'anchor' || element.is( 'a' ) ) ) {
return element;
}
}

function removeAnchorsWithinRange( range ) {
var newRange = range.clone();
newRange.enlarge( CKEDITOR.ENLARGE_ELEMENT );

var walker = new CKEDITOR.dom.walker( newRange ),
element = newRange.collapsed ? newRange.startContainer : walker.next(),
bookmark = range.createBookmark();

while ( element ) {
if ( element.type === CKEDITOR.NODE_ELEMENT && element.getAttribute( 'data-cke-saved-name' ) ) {
element.remove( true );
// Reset the walker and start from beginning, to check if element has more nested anchors.
// Without it, next element is null, so there might be space to more nested elements.
walker.reset();
}
element = walker.next();
}
range.moveToBookmark( bookmark );
}

return {
title: editor.lang.link.anchor.title,
minWidth: 300,
Expand Down Expand Up @@ -76,6 +101,8 @@ CKEDITOR.dialog.add( 'anchor', function( editor ) {
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
attributes[ 'class' ] = 'cke_anchor';

// (#4728)
removeAnchorsWithinRange( range );
// Apply style.
var style = new CKEDITOR.style( { element: 'a', attributes: attributes } );
style.type = CKEDITOR.STYLE_INLINE;
Expand Down
130 changes: 129 additions & 1 deletion tests/plugins/link/anchor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* bender-tags: editor */
/* bender-ckeditor-plugins: link,toolbar */
/* bender-ckeditor-plugins: link,toolbar,basicstyles */

( function() {
'use strict';
Expand Down Expand Up @@ -79,6 +79,134 @@
assert.areSame( anchor, dialog.getModel( editor ) );
} );
} );
},

// (#3863)
'test prevent duplicated anchors after editing a word with custom style': function() {
var editor = this.editor,
bot = this.editorBot,
template = '[<p><a id="test" name="test"><strong>text</strong></a></p>]',
expected = '<p><a id="duplicate-test" name="duplicate-test"><strong>text</strong></a></p>';

bot.setHtmlWithSelection( template );
bot.dialog( 'anchor', function( dialog ) {
dialog.setValueOf( 'info', 'txtName', 'duplicate-test' );
dialog.getButton( 'ok' ).click();

assert.beautified.html( expected, editor.getData(), 'Prevent duplicated anchors failed after editing a word with custom style' );
} );
},

// (#4728)
'test prevent duplicated anchors': function() {
var editor = this.editor,
bot = this.editorBot,
template = '[<p>Hello <a id="hello" name="hello" data-cke-saved-name="hello"><strong>world!</strong></a></p>]',
expected = '<p><a id="helloWorld" name="helloWorld">Hello <strong>world!</strong></a></p>';

bot.setHtmlWithSelection( template );
bot.dialog( 'anchor', function( dialog ) {
dialog.setValueOf( 'info', 'txtName', 'helloWorld' );
dialog.getButton( 'ok' ).click();

assert.beautified.html( expected, editor.getData(), 'Prevent duplicated anchors failed' );
} );
},

// (#4728)
'test prevent duplicated anchors after editing multiple words with styles': function() {
var editor = this.editor,
bot = this.editorBot,
template = '[<p>Simple <a id="multiTest" name="multiTest" data-cke-saved-name="multiTest"><strong>text</strong></a></p>]',
expected = '<p><a id="multiTestResult" name="multiTestResult">Simple <strong>text</strong></a></p>';

bot.setHtmlWithSelection( template );
bot.dialog( 'anchor', function( dialog ) {
dialog.setValueOf( 'info', 'txtName', 'multiTestResult' );
dialog.getButton( 'ok' ).click();

assert.beautified.html( expected, editor.getData(), 'Prevent duplicated anchors failed after editing multiple words with styles' );
} );
},

// (#4728)
'test prevent duplicated anchors in the selection: strong > em': function() {
var editor = this.editor,
bot = this.editorBot,
template = '<p>[<strong>Simple<em>Test</em></strong>]</p>',
expected = '<p><a id="nested" name="nested"><strong>Simple<em>Test</em></strong></a></p>';

bot.setHtmlWithSelection( template );
bot.dialog( 'anchor', function( dialog ) {
dialog.setValueOf( 'info', 'txtName', 'nested' );
dialog.getButton( 'ok' ).click();

assert.beautified.html( expected, editor.getData(), 'Prevent duplicated anchors failed in the selection: strong > em' );
} );
},

// (#4728)
'test prevent duplicated anchors in the selection: strong > em > span': function() {
var editor = this.editor,
bot = this.editorBot,
template = '<p>[<strong><em><span>test</span></em></strong>]</p>',
expected = '<p><a id="emphasize" name="emphasize"><strong><em><span>test</span></em></strong></a></p>';

bot.setHtmlWithSelection( template );
bot.dialog( 'anchor', function( dialog ) {
dialog.setValueOf( 'info', 'txtName', 'emphasize' );
dialog.getButton( 'ok' ).click();

assert.beautified.html( expected, editor.getData(), 'Prevent duplicated anchors failed in the selection: strong > em > span' );
} );
},

// (#4728)
'test prevent duplicated anchors in the selection of multiline with styled words': function() {
var editor = this.editor,
bot = this.editorBot,
template = '[<p><em><strong>Simple</strong></em></p><p>test</p>]',
expected = '<p><a id="multiLine" name="multiLine"><em><strong>Simple</strong></em></a></p><p><a id="multiLine" name="multiLine">test</a></p>';

bot.setHtmlWithSelection( template );
bot.dialog( 'anchor', function( dialog ) {
dialog.setValueOf( 'info', 'txtName', 'multiLine' );
dialog.getButton( 'ok' ).click();

assert.beautified.html( expected, editor.getData(), 'Prevent duplicated anchors failed in the selection of multiline with styled words' );
} );
},

// (#4728)
'test prevent duplicated anchors in the unordered list with styled word': function() {
var editor = this.editor,
bot = this.editorBot,
template = '[<ul><li><s>test</s></li></ul>]',
expected = '<ul><li><a id="unorderedList" name="unorderedList"><s>test</s></a></li></ul>';

bot.setHtmlWithSelection( template );
bot.dialog( 'anchor', function( dialog ) {
dialog.setValueOf( 'info', 'txtName', 'unorderedList' );
dialog.getButton( 'ok' ).click();

assert.beautified.html( expected, editor.getData(), 'Prevent duplicated anchors failed in the unordered list with styled word' );
} );
},

// (#4728)
'test prevent duplicated anchors in the ordered list with styled word': function() {
var editor = this.editor,
bot = this.editorBot,
template = '[<ol><li><s>test</s></li></ol>]',
expected = '<ol><li><a id="orderedList" name="orderedList"><s>test</s></a></li></ol>';

bot.setHtmlWithSelection( template );
bot.dialog( 'anchor', function( dialog ) {
dialog.setValueOf( 'info', 'txtName', 'orderedList' );
dialog.getButton( 'ok' ).click();

assert.beautified.html( expected, editor.getData(), 'Prevent duplicated anchors failed in the ordered list with styled word' );
} );
}
} );
}() );

0 comments on commit 25418a1

Please sign in to comment.