diff --git a/CHANGES.md b/CHANGES.md index 1427781a96b..5041456f501 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,7 @@ Fixed Issues: * [#12009](http://dev.ckeditor.com/ticket/12009): [Nested widgets] Integration with Magicline plugin. * [#11387](http://dev.ckeditor.com/ticket/11387): Fixed: `role="radiogroup"` should be applied only to radio inputs' container. * [#7975](http://dev.ckeditor.com/ticket/7975): [IE8] Fixed: Errors when trying to select empty table cell on IE8. +* [#11947](http://dev.ckeditor.com/ticket/11947): [FF+IE11] Fixed: Shift+Enter in lists produces two line breaks. ## CKEditor 4.4.1 diff --git a/plugins/enterkey/plugin.js b/plugins/enterkey/plugin.js index 0c5a94696e9..6d7ca808a21 100644 --- a/plugins/enterkey/plugin.js +++ b/plugins/enterkey/plugin.js @@ -453,8 +453,11 @@ doc.createText( '\ufeff' ).insertAfter( lineBreak ); // If we are at the end of a block, we must be sure the bogus node is available in that block. - if ( isEndOfBlock ) - lineBreak.getParent().appendBogus(); + if ( isEndOfBlock ) { + // In most situations we've got an elementPath.block (e.g.

), but in a + // blockless editor or when autoP is false that needs to be a block limit. + ( startBlock || elementPath.blockLimit ).appendBogus(); + } // Now we can remove the text node contents, so the caret doesn't // stop on it. diff --git a/tests/plugins/enter/enterkey.js b/tests/plugins/enter/enterkey.js index a35a5165ce1..58cfd68cb90 100644 --- a/tests/plugins/enter/enterkey.js +++ b/tests/plugins/enter/enterkey.js @@ -1,97 +1,147 @@ /* bender-tags: editor,unit */ /* bender-ckeditor-plugins: entities,enterkey */ -bender.editor = { - config: { - enterMode: CKEDITOR.ENTER_P, - allowedContent: true +( function() { + 'use strict'; + + var selectionTools = bender.tools.selection, + matchOpts = { + compareSelection: true, + normalizeSelection: true + }; + + function se( editorName, htmlWithSeleciton, expectedHtmlWithSelection ) { + return function() { + var editor = this.editors[ editorName ]; + + selectionTools.setWithHtml( editor, htmlWithSeleciton ); + editor.execCommand( 'shiftEnter' ); + + var output = selectionTools.getWithHtml( editor ); + + assert.isInnerHtmlMatching( expectedHtmlWithSelection, output, matchOpts ); + }; } -}; - -bender.test( -{ - // #7912 - 'test enterkey after invisible element': function() { - // IE restrain making selection in invisible element. - if ( CKEDITOR.env.ie ) - assert.ignore(); - - var bot = this.editorBot; - bot.setHtmlWithSelection( '

foobar^

' ); - bot.execCommand( 'enter' ); - this.editor.insertText( 'baz' ); - - var output = bender.tools.getHtmlWithSelection( bot.editor ); - output = bot.editor.dataProcessor.toDataFormat( output ); - - var expected = - CKEDITOR.env.safari ? - '

foo

baz^bar

' : - '

foobar

baz^

'; - - assert.areSame( expected, bender.tools.fixHtml( output ) ); - }, - - // #8321 - 'test enter at the end of block with inline styles' : function() { - var bot = this.editorBot; - bot.setHtmlWithSelection( '

foo^

' ); - bot.execCommand( 'enter' ); - bot.editor.insertText( 'bar' ); - assert.areSame( '

foo

bar

', bot.getData( false, true ) ); - }, - - // #7946 TODO: Add editor doc quirks mode tests. - 'test enter key scrolls document' : function() { - var bot = this.editorBot; - - bot.editor.focus(); - bot.setHtmlWithSelection( '^' ); - - // Press enough enter key in order overflow the content area. - var i = 0; - while ( i++ < 20 ) bot.execCommand( 'enter' ); - var start = bot.editor.getSelection().getStartElement(); - var rect = start.$.getBoundingClientRect(); - var viewport = bot.editor.window.getViewPaneSize(); - - // Make sure the cursor is inside of viewport. - assert.isTrue( rect.top < viewport.height && rect.top > 0 ); - }, - - // Start of #8812 - 'test Enter key at the end of contents with comment' : function() { - var bot = this.editorBot; - bot.setHtmlWithSelection( 'test ^ ' ); - bot.execCommand( 'enter' ); - assert.areSame( '

test

 

', bot.getData( false, true ) ); - }, - - 'test Enter key in the middle of contents with comments' : function() { - var bot = this.editorBot; - bot.setHtmlWithSelection( 'foo^bar' ); - bot.execCommand( 'enter' ); - - // IE9+Compat looses the first comment, so we remove it from the assertion (not related to #8812). - assert.areSame( '

foo

bar

', bot.getData( false, true ).replace( /]+>/g, '' ) ); - }, - - 'test Enter key in the middle of contents with comments (2)' : function() { - var bot = this.editorBot; - bot.setHtmlWithSelection( 'foobar^bazqux' ); - bot.execCommand( 'enter' ); - - assert.areSame( '

foobar

bazqux

', bot.getData( false, true ) ); - }, - // End of #8812 - - 'test Enter key uses editor.activeEnterMode': function() { - bender.editorBot.create( { - name: 'test_enter_editor_enter_mode', - config: { - autoParagraph: false + + bender.test( { + _should: { + ignore: { + 'test shift+enter key - end of block, inside inline element followed by bogus br': !CKEDITOR.env.needsBrFiller, + 'test shift+enter key - end of list item, inside inline element followed by bogus br': !CKEDITOR.env.needsBrFiller, } - }, function( bot ) { + }, + + 'async:init': function() { + var that = this; + + bender.tools.setUpEditors( { + editor: { + name: 'editor1', + config: { + enterMode: CKEDITOR.ENTER_P, + allowedContent: true + } + }, + + editorNoAutoParagraph: { + name: 'editor2', + config: { + autoParagraph: false + } + } + }, function( editors, bots ) { + that.editorBots = bots; + that.editors = editors; + that.callback(); + } ); + }, + + // #7912 + 'test enter key after invisible element': function() { + // IE restrain making selection in invisible element. + if ( CKEDITOR.env.ie ) + assert.ignore(); + + var bot = this.editorBots.editor, + editor = bot.editor; + + bot.setHtmlWithSelection( '

foobar^

' ); + bot.execCommand( 'enter' ); + editor.insertText( 'baz' ); + + var output = bender.tools.getHtmlWithSelection( editor ); + output = editor.dataProcessor.toDataFormat( output ); + + var expected = + CKEDITOR.env.safari ? + '

foo

baz^bar

' : + '

foobar

baz^

'; + + assert.areSame( expected, bender.tools.fixHtml( output ) ); + }, + + // #8321 + 'test enter key at the end of block with inline styles' : function() { + var bot = this.editorBots.editor, + editor = bot.editor; + + bot.setHtmlWithSelection( '

foo^

' ); + bot.execCommand( 'enter' ); + editor.insertText( 'bar' ); + assert.areSame( '

foo

bar

', bot.getData( false, true ) ); + }, + + // #7946 TODO: Add editor doc quirks mode tests. + 'test enter key key scrolls document' : function() { + var bot = this.editorBots.editor, + editor = bot.editor; + + editor.focus(); + bot.setHtmlWithSelection( '^' ); + + // Press enough enter key in order overflow the content area. + var i = 0; + while ( i++ < 20 ) bot.execCommand( 'enter' ); + var start = editor.getSelection().getStartElement(); + var rect = start.$.getBoundingClientRect(); + var viewport = bot.editor.window.getViewPaneSize(); + + // Make sure the cursor is inside of viewport. + assert.isTrue( rect.top < viewport.height && rect.top > 0 ); + }, + + // Start of #8812 + 'test ener key at the end of contents with comment' : function() { + var bot = this.editorBots.editor; + + bot.setHtmlWithSelection( 'test ^ ' ); + bot.execCommand( 'enter' ); + assert.areSame( '

test

 

', bot.getData( false, true ) ); + }, + + 'test enter key in the middle of contents with comments' : function() { + var bot = this.editorBots.editor; + + bot.setHtmlWithSelection( 'foo^bar' ); + bot.execCommand( 'enter' ); + + // IE9+Compat looses the first comment, so we remove it from the assertion (not related to #8812). + assert.areSame( '

foo

bar

', bot.getData( false, true ).replace( /]+>/g, '' ) ); + }, + + 'test enter key in the middle of contents with comments (2)' : function() { + var bot = this.editorBots.editor; + + bot.setHtmlWithSelection( 'foobar^bazqux' ); + bot.execCommand( 'enter' ); + + assert.areSame( '

foobar

bazqux

', bot.getData( false, true ) ); + }, + // End of #8812 + + 'test enter key uses editor.activeEnterMode': function() { + var bot = this.editorBots.editorNoAutoParagraph; + bot.editor.setActiveEnterMode( CKEDITOR.ENTER_BR, CKEDITOR.ENTER_DIV ); try { @@ -112,16 +162,11 @@ bender.test( bot.setHtmlWithSelection( 'foo^bar' ); bot.execCommand( 'enter' ); assert.areSame( '

foo

bar

', bot.getData(), 'main mode was used' ); - } ); - }, - - 'test Enter key is influenced by the active filter': function() { - bender.editorBot.create( { - name: 'test_enter_active_filter', - config: { - autoParagraph: false - } - }, function( bot ) { + }, + + 'test enter key is influenced by the active filter': function() { + var bot = this.editorBots.editorNoAutoParagraph; + bot.setHtmlWithSelection( 'foo^bar' ); var filter = new CKEDITOR.filter( 'div' ); @@ -140,27 +185,40 @@ bender.test( bot.setHtmlWithSelection( 'foo^bar' ); bot.execCommand( 'enter' ); assert.areSame( '

foo

bar

', bot.getData(), 'main mode was used' ); - } ); - }, + }, - /* - // Commented out until we decide whether we want to block enter key completely and how. - 'test Enter key is completely blocked if neither p nor br are allowed': function() { - var bot = this.editorBot; - bot.setHtmlWithSelection( '

foo^bar

' ); + /* + // Commented out until we decide whether we want to block enter key completely and how. + 'test enter key is completely blocked if neither p nor br are allowed': function() { + var bot = this.editorBot; + bot.setHtmlWithSelection( '

foo^bar

' ); - var filter = new CKEDITOR.filter( 'x' ); - this.editor.setActiveFilter( filter ); + var filter = new CKEDITOR.filter( 'x' ); + this.editor.setActiveFilter( filter ); - try { - bot.execCommand( 'enter' ); - assert.areSame( '

foobar

', bot.getData(), 'enter is blocked' ); - } catch ( e ) { - throw e; - } finally { - // Always reset filter - even if previous test failed. - this.editor.setActiveFilter( null ); - } - } - */ -} ); \ No newline at end of file + try { + bot.execCommand( 'enter' ); + assert.areSame( '

foobar

', bot.getData(), 'enter is blocked' ); + } catch ( e ) { + throw e; + } finally { + // Always reset filter - even if previous test failed. + this.editor.setActiveFilter( null ); + } + }, + */ + + 'test shift+enter key - middle of block': se( 'editor', '

foo{}bar

', '

foo
^bar@

' ), + 'test shift+enter key - list item': se( 'editor', '', '' ), + 'test shift+enter key - start of block': se( 'editor', '

{}foobar

', '


^foobar@

' ), + 'test shift+enter key - end of block': se( 'editor', '

foobar{}

', '

foobar
^@

' ), + 'test shift+enter key - before br': se( 'editor', '

foo{}
bar

', '

foo
^
bar@

' ), + 'test shift+enter key - after br': se( 'editor', '

foo
{}bar

', '

foo

^bar@

' ), + // #11947 + 'test shift+enter key - end of block, inside inline element followed by bogus br': + se( 'editor', '

foo{}

', '

foo
^

' ), + 'test shift+enter key - end of list item, inside inline element followed by bogus br': + se( 'editor', '', '' ), + } ); + +} )(); \ No newline at end of file diff --git a/tests/utils/range/setgetwithhtml.js b/tests/utils/range/setgetwithhtml.js index ce76b81f26f..7ac5fae03f7 100644 --- a/tests/utils/range/setgetwithhtml.js +++ b/tests/utils/range/setgetwithhtml.js @@ -13,7 +13,7 @@ failIE8 = CKEDITOR.env.ie && CKEDITOR.env.version < 9; // Asserts setRange and getRange. - function s( html, collapsed, startAddress, startOffset, endAddress, endOffset, htmlWithRange ) { + function s( html, collapsed, startAddress, startOffset, endAddress, endOffset, htmlWithRangeOrCallback ) { return function() { var range = setRange( playground, html ), startContainer, endContainer, range; @@ -39,8 +39,14 @@ assert.isNull( range, 'No ranges returned' ); } - assert.areSame( html.replace( /[\{\}\[\]]/g, '' ), bender.tools.fixHtml( playground.getHtml(), 1, 1 ), 'Markers cleaned - setRange' ); - assert.areSame( htmlWithRange == undefined ? html : htmlWithRange, bender.tools.fixHtml( getRange( playground, range ), 1, 1 ), 'getRange' ); + if ( typeof htmlWithRangeOrCallback == 'function' ) { + htmlWithRangeOrCallback( playground, range ); + } else { + assert.areSame( html.replace( /[\{\}\[\]]/g, '' ), bender.tools.fixHtml( playground.getHtml(), 1, 1 ), 'Markers cleaned - setRange' ); + if ( htmlWithRangeOrCallback == undefined ) + htmlWithRangeOrCallback = html; + assert.areSame( htmlWithRangeOrCallback, bender.tools.fixHtml( getRange( playground, range ), 1, 1 ), 'getRange' ); + } }; } @@ -63,7 +69,7 @@ } }, - // Collapsed Start Address Start Offset End Address End offset HtmlWithRange + // Collapsed Start Address Start Offset End Address End offset HtmlWithRange/callback 'test text #0': s( '' ), 'test text #1': s( '{}', null, null, null, null, null, '' ), 'test text #2': s( '

x

' ), @@ -112,6 +118,18 @@ 'test mixed #5': s( '

x{]

', false, [ 0, 0 ], 1, [ 0 ], 1 ), 'test mixed #6': s( '

[x

y}

', false, [ 0 ], 0, [ 1, 0 ], 1 ), 'test mixed #7': s( '

{ ]

', false, [ 0, 0 ], 0, [ 0 ], 1 ), - 'test mixed #8': s( '

{ ]

', false, [ 0, 0 ], 1, [ 0 ], 1 ) + 'test mixed #8': s( '

{ ]

', false, [ 0, 0 ], 1, [ 0 ], 1 ), + + // IE9-11 aren't any better than IE8 and lose empty text nodes between elements + // when cloning a tree. We need special clone method to workaround this. + 'test special #1': s( '

x
{}y

', true, [ 0, 2 ], 0, null, null, + function( playground, range ) { + // Let's remove "y". + var text = playground.getChild( [ 0, 2 ] ); + assert.areSame( 'y', text.getText(), 'we found the right node' ); + text.setText( '' ); + + assert.areSame( '

x
{}

', bender.tools.fixHtml( getRange( playground, range ), 1, 1 ), 'getRange' ); + } ), } ); } )(); \ No newline at end of file