Skip to content

Commit c17735f

Browse files
committed
Merge branch 't/12008' into major
2 parents 3d0b46e + 2730297 commit c17735f

File tree

6 files changed

+123
-10
lines changed

6 files changed

+123
-10
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ CKEditor 4 Changelog
33

44
## CKEditor 4.5
55

6+
Fixed Issues:
7+
68
* [#12006](http://dev.ckeditor.com/ticket/12006): [Nested widgets] Drag and drop of nested block widgets.
9+
* [#12008](http://dev.ckeditor.com/ticket/12008): Fixed various cases of inserting single non-editable element using [`editor.insertHtml()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertHtml) method. Fixes pasting widget with nested editable inside other widget's nested editable.
710

811
## CKEditor 4.4.2
912

bender.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ var config = {
7474
'tests/plugins/widget/nestededitables#test pasting widget which was copied (d&d) when its nested editable was focused': 'env.ie && env.version == 8',
7575

7676
// Firefox (#11399)
77-
'tests/plugins/widget/nestededitables#test selection in nested editable is preserved after opening and closing dialog - inline editor': 'env.gecko'
77+
'tests/plugins/widget/nestededitables#test selection in nested editable is preserved after opening and closing dialog - inline editor': 'env.gecko',
78+
79+
// IE8 (http://dev.ckeditor.com/ticket/12008#comment:6)
80+
'tests/plugins/widget/nestedwidgets#test pasting widget with nested editable into nested editable': 'env.ie && env.version == 8',
7881
}
7982
},
8083

core/editable.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,14 @@
15191519
range.collapse();
15201520
}
15211521

1522+
// Rule 9. Non-editable content should be selected as a whole.
1523+
if ( isSingleNonEditableElement( nodesData ) ) {
1524+
dontMoveCaret = true;
1525+
node = nodesData[ 0 ].node;
1526+
range.setStartAt( node, CKEDITOR.POSITION_BEFORE_START );
1527+
range.setEndAt( node, CKEDITOR.POSITION_AFTER_END );
1528+
}
1529+
15221530
that.dontMoveCaret = dontMoveCaret;
15231531
that.bogusNeededBlocks = bogusNeededBlocks;
15241532
}
@@ -1747,6 +1755,16 @@
17471755
return node && checkIfElement( node ) && ( node.is( DTD.$removeEmpty ) || node.is( 'a' ) && !node.isBlockBoundary() );
17481756
}
17491757

1758+
// Checks if only non-editable element is being inserted.
1759+
function isSingleNonEditableElement( nodesData ) {
1760+
if ( nodesData.length != 1 )
1761+
return false;
1762+
1763+
var nodeData = nodesData[ 0 ];
1764+
1765+
return nodeData.isElement && ( nodeData.node.getAttribute( 'contenteditable' ) == 'false' );
1766+
}
1767+
17501768
var blockMergedTags = { p: 1, div: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, ul: 1, ol: 1, li: 1, pre: 1, dl: 1, blockquote: 1 };
17511769

17521770
// See rule 5. in TCs.
@@ -1836,7 +1854,8 @@
18361854

18371855
if ( dataWrapper.getChildCount() == 1 && // Only one node bein inserted.
18381856
checkIfElement( block = dataWrapper.getFirst() ) && // And it's an element.
1839-
block.is( stripSingleBlockTags ) ) // That's <p> or <div> or header.
1857+
block.is( stripSingleBlockTags ) && // That's <p> or <div> or header.
1858+
!block.hasAttribute( 'contenteditable' ) ) // It's not a non-editable block or nested editable.
18401859
{
18411860
// Check children not containing block.
18421861
children = block.getElementsByTag( '*' );

tests/core/editable/inserthtml.js

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@
8383
// "<p><b>x^y</b></p>" + "<p>abc</p><p>def</p>" => "<p><b>x</b></p><p><b>abc</b></p><p><b>def^</b></p><p><b>y</b></p>"
8484
// - in html mode:
8585
// "<p><b>x^y</b></p>" + "<p>abc</p><p>def</p>" => "<p><b>x</b></p><p>abc</p><p>def^</p><p><b>y</b></p>"
86+
// 9. Non-editable element must be selected as a whole, even if it has a nested editable.
87+
//
88+
// Example:
89+
// "<p>x^x</p>" + "<span contenteditable='false'>foo</span>" => "<p>x[<span contenteditable='false'>foo</span>]x</p>"
8690

8791
// TCs groups:
8892
// 1. text -> text
@@ -621,11 +625,11 @@
621625
},
622626

623627
//
624-
// TCs groups 7-9.
628+
// TCs groups 7-8.
625629
// block elements -> elements
626630
//
627631

628-
'G7-9. splitting' : function() {
632+
'G7-8. splitting' : function() {
629633
var a = this.createAssertInsertionFunction( 'body,div', '<p>bam</p><p>bar</p>' );
630634

631635
a( '<p>a^b</p>',
@@ -686,14 +690,14 @@
686690
a( '<h1>a^b</h1>', '<h1>a</h1><form>bam^</form><h1>b</h1>', 'case 8b' );
687691
},
688692

689-
'G7-9. splitting - reuse element' : function() {
693+
'G7-8. splitting - reuse element' : function() {
690694
var a = this.createAssertInsertionFunction( 'body,div', 'x<p title="1">bam</p>y', 'html' );
691695

692696
a( '<p title="2">a^b</p>',
693697
'<p title="2">ax</p><p title="1">bam</p><p title="2">y^b</p>', 'case 1a' );
694698
},
695699

696-
'G7-9. splitting - multi selection' : function() {
700+
'G7-8. splitting - multi selection' : function() {
697701
var a = this.createAssertInsertionFunction( 'body,div', 'x<p>bam</p>y' );
698702

699703
a( '<p>a[b</p><p>c]d</p>',
@@ -719,7 +723,7 @@
719723
},
720724

721725
// See _docs/blockselections.txt
722-
'G7-9. splitting - text + eol' : function() {
726+
'G7-8. splitting - text + eol' : function() {
723727
var a = this.createAssertInsertionFunction( 'body,div', '<br data-cke-eol="1" />bam' );
724728

725729
a( '<p>a^b</p>', '<p>a</p><p>bam^b</p>', 'case 1a' );
@@ -759,7 +763,7 @@
759763
},
760764

761765
// These cases were previously handled positively. Test for regressions.
762-
'G7-9. splitting - text + eol - reverted cases' : function() {
766+
'G7-8. splitting - text + eol - reverted cases' : function() {
763767
var a = this.createAssertInsertionFunction( 'body,div', '<br />bam' );
764768

765769
a( '<p>a^b</p>', '<p>a<br />bam^b</p>', 'case 1' );
@@ -786,7 +790,7 @@
786790
a( '<div>a^b</div>', '<div>a<br /><br />^b</div>', 'case 6d' );
787791
},
788792

789-
'G7-9. filtering content' : function() {
793+
'G7-8. filtering content' : function() {
790794
// Form chosen, so it's not stripped by rule 7.
791795
var a = this.createAssertInsertionFunction( 'h1', '<form>bam</form>', 'html' );
792796

@@ -814,6 +818,45 @@
814818
a( 'a^b', 'a<b>b</b>a<br />m^b', 'case 6a' );
815819
},
816820

821+
//
822+
// TCs group 9.
823+
// non-editable content
824+
//
825+
826+
'G9. non-editable inline element' : function() {
827+
var span = '<span contenteditable="false">xxx</span>',
828+
a = this.createAssertInsertionFunction( 'body,div', span, 'html' );
829+
830+
a( 'a^b', 'a[' + span + ']b', 'case 1a' );
831+
a( '<p>a^b</p>', '<p>a[' + span + ']b</p>', 'case 1b' );
832+
a( '<p>a[b</p><p>c]d</p>', '<p>a[' + span + ']d</p>', 'case 1c' );
833+
},
834+
835+
'G9. non-editable inline element with inline content' : function() {
836+
var span = '<span contenteditable="false"><b>xxx</b></span>',
837+
a = this.createAssertInsertionFunction( 'body,div', span, 'html' );
838+
839+
a( 'a^b', 'a[' + span + ']b', 'case 1a' );
840+
a( '<p>a^b</p>', '<p>a[' + span + ']b</p>', 'case 1b' );
841+
a( '<p>a[b</p><p>c]d</p>', '<p>a[' + span + ']d</p>', 'case 1c' );
842+
},
843+
844+
'G9. non-editable block element' : function() {
845+
var div = '<div contenteditable="false">xxx</div>',
846+
a = this.createAssertInsertionFunction( 'body,div', div, 'html' );
847+
848+
a( '<p>a^b</p>', '<p>a</p>[' + div + ']<p>b</p>', 'case 1a' );
849+
a( '<p>a[b</p><p>c]d</p>', '<p>a</p>[' + div + ']<p>d</p>', 'case 1b' );
850+
},
851+
852+
'G9. non-editable block element with nested editable' : function() {
853+
var div = '<div contenteditable="false">xx<p contenteditable="true">yy</p></div>',
854+
a = this.createAssertInsertionFunction( 'body,div', div, 'html' );
855+
856+
a( '<p>a^b</p>', '<p>a</p>[' + div + ']<p>b</p>', 'case 1a' );
857+
a( '<p>a[b</p><p>c]d</p>', '<p>a</p>[' + div + ']<p>d</p>', 'case 1b' );
858+
},
859+
817860
//
818861
// TCs group special.
819862
//

tests/plugins/widget/nestedwidgets.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,50 @@
299299
function log() {
300300
destroyed.push( this.element.$.id );
301301
}
302+
},
303+
304+
// #12008
305+
'test pasting widget with nested editable into nested editable': function() {
306+
var editor = this.editors.editor,
307+
bot = this.editorBots.editor;
308+
309+
editor.widgets.add( 'testpaste1', {
310+
editables: {
311+
ned2: '.ned2' // The name has to be different
312+
}
313+
} );
314+
315+
var widget2Data =
316+
'<div data-widget="testpaste1" id="wp-1">' +
317+
'<p class="ned2">foo</p>' +
318+
'</div>';
319+
320+
bot.setData( generateWidgetsData( 1 ) + '<p>xxx</p>' + widget2Data, function() {
321+
var w1 = getWidgetById( editor, 'wp-0' ),
322+
w2 = getWidgetById( editor, 'wp-1' ),
323+
html = w2.wrapper.getOuterHtml();
324+
325+
w2.wrapper.remove();
326+
editor.widgets.checkWidgets();
327+
328+
w1.editables.ned.focus();
329+
var range = editor.createRange();
330+
range.moveToPosition( editor.document.getById( 'p-0' ), CKEDITOR.POSITION_AFTER_START );
331+
editor.getSelection().selectRanges( [ range ] );
332+
333+
editor.on( 'afterPaste', function() {
334+
resume( function() {
335+
w2 = getWidgetById( editor, 'wp-1', true );
336+
337+
assert.isNotNull( w2, 'widget was pasted' );
338+
assert.areSame( w2, editor.widgets.focused, 'pasted widget is focused' );
339+
} );
340+
} );
341+
342+
wait( function() {
343+
editor.execCommand( 'paste', html );
344+
} );
345+
} );
302346
}
303347

304348
} );

tests/plugins/widget/widgetsintegration.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,8 @@
397397
data2Attr( { 'classes': null } ) +
398398
'" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="test_upcasted_pasting"><i class="upcasted_pasting">foo</i></span>' +
399399
widgetTestsTools.widgetDragHanlder +
400-
'</span>X?(<br />)?</p>$'
400+
'</span>X?(<br />)?</p>' +
401+
'(<div [^>]+>&nbsp;</div>)?$' // Hidden sel container.
401402
);
402403

403404
var editor = this.editor,

0 commit comments

Comments
 (0)