Skip to content

Commit 824cbc6

Browse files
committed
Merge branch 't/13429d'
2 parents e61966b + 5658931 commit 824cbc6

File tree

4 files changed

+137
-6
lines changed

4 files changed

+137
-6
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Fixed Issues:
1919
* [#13453](http://dev.ckeditor.com/ticket/13453): Fixed: Drag&drop of a whole content of the editor throws an error.
2020
* [#13465](http://dev.ckeditor.com/ticket/13465): Fixed: Error is thrown and widget is lost when drag&drop if it is the only content of the editor.
2121
* [#13414](http://dev.ckeditor.com/ticket/13414): Fixed: Content auto paragraphing in a nested editable despite editor configuration.
22+
* [#13429](http://dev.ckeditor.com/ticket/13429): Fixed: Wrong selection after content insertion by [Auto Embed](http://ckeditor.com/addon/autoembed) plugin.
2223

2324
Other Changes:
2425

plugins/autoembed/plugin.js

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,53 @@
8484
noNotifications: true,
8585
callback: function() {
8686
// DOM might be invalidated in the meantime, so find the anchor again.
87-
var anchor = editor.editable().findOne( 'a[data-cke-autoembed="' + id + '"]' ),
88-
range = editor.createRange();
87+
var anchor = editor.editable().findOne( 'a[data-cke-autoembed="' + id + '"]' );
8988

9089
// Anchor might be removed in the meantime.
9190
if ( anchor ) {
92-
range.setStartAt( anchor, CKEDITOR.POSITION_BEFORE_START );
93-
range.setEndAt( anchor, CKEDITOR.POSITION_AFTER_END );
91+
var selection = editor.getSelection(),
92+
insertRange = editor.createRange(),
93+
editable = editor.editable();
94+
95+
// Save the changes in editor contents that happened *after* the link was pasted
96+
// but before it gets embedded (i.e. user pasted and typed).
97+
editor.fire( 'saveSnapshot' );
98+
99+
// Lock snapshot so we don't make unnecessary undo steps in
100+
// editable.insertElement() below, which would include bookmarks. (#13429)
101+
editor.fire( 'lockSnapshot', { dontUpdate: true } );
102+
103+
// Bookmark current selection. (#13429)
104+
var bookmark = selection.createBookmarks( false )[ 0 ],
105+
startNode = bookmark.startNode,
106+
endNode = bookmark.endNode || startNode;
107+
108+
// When url is pasted, IE8 sets the caret after <a> element instead of inside it.
109+
// So, if user hasn't changed selection, bookmark is inserted right after <a>.
110+
// Then, after pasting embedded content, bookmark is still in DOM but it is
111+
// inside the original element. After selection recreation it would end up before widget:
112+
// <p>A <a /><bm /></p><p>B</p> --> <p>A <bm /></p><widget /><p>B</p> --> <p>A ^</p><widget /><p>B</p>
113+
// We have to fix this IE8 behavior so it is the same as on other browsers.
114+
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 && !bookmark.endNode && startNode.equals( anchor.getNext() ) ) {
115+
anchor.append( startNode );
116+
}
117+
118+
insertRange.setStartBefore( anchor );
119+
insertRange.setEndAfter( anchor );
120+
121+
editable.insertElement( wrapper, insertRange );
122+
123+
// If both bookmarks are still in DOM, it means that selection was not inside
124+
// an anchor that got substituted. We can safely recreate that selection. (#13429)
125+
if ( editable.contains( startNode ) && editable.contains( endNode ) ) {
126+
selection.selectBookmarks( [ bookmark ] );
127+
} else {
128+
// If one of bookmarks is not in DOM, clean up leftovers.
129+
startNode.remove();
130+
endNode.remove();
131+
}
94132

95-
editor.editable().insertElementIntoRange( wrapper, range );
133+
editor.fire( 'unlockSnapshot' );
96134
}
97135

98136
notification.hide();

tests/plugins/autoembed/autoembed.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,5 +375,95 @@ bender.test( {
375375
editor.execCommand( 'paste', 'https://foo.bar/g/notification/test/1' );
376376
wait();
377377
} );
378+
},
379+
380+
// #13429.
381+
'test selection after auto embedding - empty editor': function() {
382+
var bot = this.editorBot,
383+
editor = bot.editor,
384+
pastedText = 'https://foo.bar/g/200/382';
385+
386+
bot.setData( '', function() {
387+
editor.focus();
388+
editor.execCommand( 'paste', pastedText );
389+
390+
wait( function() {
391+
// Check if there is exactly one additional <p> created after the widget.
392+
assert.areSame( '<div data-oembed-url="' + pastedText + '"><img src="' + pastedText + '" /></div><p>\u00A0</p>', editor.getData(), 'right editor data after paste' );
393+
var p = editor.editable().findOne( 'p' );
394+
395+
// Check if caret is inside newly created <p>.
396+
var range = editor.getSelection().getRanges()[ 0 ];
397+
assert.isTrue( range.collapsed, 'selection after paste is collapsed' );
398+
assert.areSame( 0, range.startOffset, 'selection at the beginning of the paragraph' );
399+
assert.isTrue( range.startContainer.equals( p ), 'selection inside correct p element' );
400+
}, 200 );
401+
} );
402+
},
403+
404+
// #13429.
405+
'test selection after auto embedding - inside content': function() {
406+
var bot = this.editorBot,
407+
editor = bot.editor,
408+
pastedText = 'https://foo.bar/g/200/382';
409+
410+
bot.setData( '<p>foo</p><p>bar</p>', function() {
411+
editor.focus();
412+
413+
// Set caret at the end of the first <p>.
414+
var range = editor.createRange();
415+
range.setStart( this.editor.editable().find( 'p' ).getItem( 0 ).getFirst(), 3 );
416+
range.collapse();
417+
// '<p>foo^</p><p>bar</p>'
418+
editor.getSelection().selectRanges( [ range ] );
419+
420+
editor.execCommand( 'paste', pastedText );
421+
422+
wait( function() {
423+
// Get the second <p>.
424+
var p = editor.editable().find( 'p' ).getItem( 1 );
425+
var range = editor.getSelection().getRanges()[ 0 ];
426+
var container = range.startContainer;
427+
428+
// Check if caret is inside second <p>.
429+
// Different browsers set startContainer differently,
430+
// so we check if it is in <p> or in a text node inside that <p>.
431+
assert.isTrue( range.collapsed, 'selection after paste is collapsed' );
432+
assert.areSame( 0, range.startOffset, 'selection anchored at the beginning of the paragraph' );
433+
assert.isTrue( !!new CKEDITOR.dom.elementPath( container ).contains( p ), 'selection inside correct p element' );
434+
assert.isInnerHtmlMatching( 'bar@', ( container.type == CKEDITOR.NODE_TEXT ? container.getParent() : container ).getHtml(), 'selection inside correct p element' );
435+
}, 200 );
436+
} );
437+
},
438+
439+
// #13429.
440+
'test selection after auto embedding - content and selection change before insert': function() {
441+
var bot = this.editorBot,
442+
editor = bot.editor,
443+
editable = editor.editable(),
444+
pastedText = 'https://foo.bar/g/200/382';
445+
446+
bot.setData( '', function() {
447+
editor.focus();
448+
editor.execCommand( 'paste', pastedText );
449+
450+
var pastedAnchor = editable.findOne( 'a' );
451+
452+
// After link has been pasted, "type" some text...
453+
var text = new CKEDITOR.dom.text( 'foo' );
454+
text.insertAfter( pastedAnchor );
455+
456+
// ..and make a selection on that text.
457+
// "[foo]"
458+
var range = editor.createRange();
459+
range.setStartBefore( text );
460+
range.setEndAfter( text );
461+
range.select();
462+
463+
wait( function() {
464+
assert.areSame( '<div data-oembed-url="' + pastedText + '"><img src="' + pastedText + '" /></div><p>foo</p>', editor.getData(), 'right editor data after paste' );
465+
assert.isMatching( /[{\[]\u200b?foo[}\]]/, bender.tools.selection.getWithHtml( editor ), 'selection anchored at the right position' );
466+
}, 200 );
467+
} );
378468
}
379469
} );

tests/plugins/autoembed/manual/autoembed.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ Things to check:
1111
* Other content changes.
1212
* Pasting more complex content (only single links should be embedded).
1313
* Undo/redo. Note: There should be two steps &ndash; one reverting autoembed and one reverting link paste.
14-
* That in the 1st editor `embed` is used and in the 2nd editor `embedsemantic` (check the data).
14+
* After pasting a link write some text **before** the content is embedded. Alternatively, simply change the selection. Check if the selection is **exactly the same** as before the insertion.
15+
* Check if all operations are saved in undo steps (paste link, write text, insert content, write text.)
16+
* That in the 1st editor `embed` is used and in the 2nd editor `embedsemantic` (check the data).

0 commit comments

Comments
 (0)