Skip to content

Commit ac1285f

Browse files
committed
Merge branch 't/10907' into major
2 parents a2d9130 + 947c510 commit ac1285f

File tree

17 files changed

+148
-87
lines changed

17 files changed

+148
-87
lines changed

core/dom/element.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,16 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, {
250250
},
251251

252252
/**
253-
* @todo
253+
* Appends a `<br>` filler element to this element if the filler is not present already.
254+
* By default filler is appended only if {@link CKEDITOR.env#needsBrFiller} is `true`,
255+
* however when `force` is set to `true` filler will be appended regardless of the environment.
256+
*
257+
* @param {Boolean} [force] Append filler regardless of the environment.
254258
*/
255-
appendBogus: function() {
259+
appendBogus: function( force ) {
260+
if ( !force && !( CKEDITOR.env.needsBrFiller || CKEDITOR.env.opera ) )
261+
return;
262+
256263
var lastChild = this.getLast();
257264

258265
// Ignore empty/spaces text.

core/dom/iterator.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@
339339
if ( removeLastBr ) {
340340
var lastChild = block.getLast();
341341
if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.getName() == 'br' ) {
342-
// Take care not to remove the block expanding <br> in non-IE browsers.
343-
if ( CKEDITOR.env.ie || lastChild.getPrevious( bookmarkGuard ) || lastChild.getNext( bookmarkGuard ) )
342+
// Remove br filler on browser which do not need it.
343+
if ( !CKEDITOR.env.needsBrFiller || lastChild.getPrevious( bookmarkGuard ) || lastChild.getNext( bookmarkGuard ) )
344344
lastChild.remove();
345345
}
346346
}

core/dom/node.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, {
686686
break;
687687
}
688688

689-
if ( !CKEDITOR.env.ie && !CKEDITOR.env.opera ) {
689+
if ( CKEDITOR.env.needsBrFiller ) {
690690
child = this.$.lastChild;
691691

692692
if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' ) {

core/dom/range.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,8 +1660,7 @@ CKEDITOR.dom.range = function( root ) {
16601660
this.extractContents().appendTo( fixedBlock );
16611661
fixedBlock.trim();
16621662

1663-
if ( !CKEDITOR.env.ie )
1664-
fixedBlock.appendBogus();
1663+
fixedBlock.appendBogus();
16651664

16661665
this.insertNode( fixedBlock );
16671666

@@ -1722,7 +1721,7 @@ CKEDITOR.dom.range = function( root ) {
17221721
// In Gecko, the last child node must be a bogus <br>.
17231722
// Note: bogus <br> added under <ul> or <ol> would cause
17241723
// lists to be incorrectly rendered.
1725-
if ( !CKEDITOR.env.ie && !startBlock.is( 'ul', 'ol' ) )
1724+
if ( !startBlock.is( 'ul', 'ol' ) )
17261725
startBlock.appendBogus();
17271726
}
17281727
}

core/dom/walker.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@
451451
}
452452

453453
return function( node ) {
454-
var isBogus = !CKEDITOR.env.ie ? node.is && node.is( 'br' ) : node.getText && tailNbspRegex.test( node.getText() );
454+
var isBogus = CKEDITOR.env.needsBrFiller ? node.is && node.is( 'br' ) : node.getText && tailNbspRegex.test( node.getText() );
455455

456456
if ( isBogus ) {
457457
var parent = node.getParent(),
@@ -560,7 +560,7 @@
560560
return true;
561561

562562
// Empty blocks are editable on IE.
563-
if ( CKEDITOR.env.ie && node.is( dtdTextBlock ) && isEmpty( node ) )
563+
if ( !CKEDITOR.env.needsBrFiller && node.is( dtdTextBlock ) && isEmpty( node ) )
564564
return true;
565565
}
566566

@@ -606,7 +606,7 @@
606606
}
607607
while ( toSkip( tail ) )
608608

609-
if ( tail && ( !CKEDITOR.env.ie ? tail.is && tail.is( 'br' ) : tail.getText && tailNbspRegex.test( tail.getText() ) ) ) {
609+
if ( tail && ( CKEDITOR.env.needsBrFiller ? tail.is && tail.is( 'br' ) : tail.getText && tailNbspRegex.test( tail.getText() ) ) ) {
610610
return tail;
611611
}
612612
return false;

core/editable.js

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,8 @@
587587
evt.preventDefault();
588588
} );
589589

590+
var backspaceOrDelete = { 8:1,46:1 };
591+
590592
// Override keystrokes which should have deletion behavior
591593
// on fully selected element . (#4047) (#7645)
592594
this.attachListener( editor, 'key', function( evt ) {
@@ -596,7 +598,7 @@
596598
var keyCode = evt.data.keyCode, isHandled;
597599

598600
// Backspace OR Delete.
599-
if ( keyCode in { 8:1,46:1 } ) {
601+
if ( keyCode in backspaceOrDelete ) {
600602
var sel = editor.getSelection(),
601603
selected,
602604
range = sel.getRanges()[ 0 ],
@@ -678,6 +680,20 @@
678680
return !isHandled;
679681
} );
680682

683+
// On IE>=11 we need to fill blockless editable with <br> if it was deleted.
684+
if ( editor.blockless && CKEDITOR.env.ie && CKEDITOR.env.needsBrFiller ) {
685+
this.attachListener( this, 'keyup', function( evt ) {
686+
if ( evt.data.getKeystroke() in backspaceOrDelete && !this.getFirst( isNotEmpty ) ) {
687+
this.appendBogus();
688+
689+
// Set the selection before bogus, because IE tends to put it after.
690+
var range = editor.createRange();
691+
range.moveToPosition( this, CKEDITOR.POSITION_AFTER_START );
692+
range.select();
693+
}
694+
} );
695+
}
696+
681697
this.attachListener( this, 'dblclick', function( evt ) {
682698
if ( editor.readOnly )
683699
return false;
@@ -807,25 +823,16 @@
807823
blockLimit = path.blockLimit,
808824
selection = evt.data.selection,
809825
range = selection.getRanges()[ 0 ],
810-
enterMode = editor.activeEnterMode;
811-
812-
if ( CKEDITOR.env.gecko ) {
813-
// v3: check if this is needed.
814-
// activateEditing( editor );
815-
816-
// Ensure bogus br could help to move cursor (out of styles) to the end of block. (#7041)
817-
var pathBlock = path.block || path.blockLimit || path.root,
818-
lastNode = pathBlock && pathBlock.getLast( isNotEmpty );
819-
820-
// Check some specialities of the current path block:
821-
// 1. It is really displayed as block; (#7221)
822-
// 2. It doesn't end with one inner block; (#7467)
823-
// 3. It doesn't have bogus br yet.
824-
if ( pathBlock && pathBlock.isBlockBoundary() &&
825-
!( lastNode && lastNode.type == CKEDITOR.NODE_ELEMENT && lastNode.isBlockBoundary() ) &&
826-
!pathBlock.is( 'pre' ) && !pathBlock.getBogus() ) {
827-
828-
pathBlock.appendBogus();
826+
enterMode = editor.activeEnterMode,
827+
selectionUpdateNeeded;
828+
829+
if ( CKEDITOR.env.gecko || ( CKEDITOR.env.ie && CKEDITOR.env.needsBrFiller ) ) {
830+
var blockNeedsFiller = needsBrFiller( selection, path );
831+
if ( blockNeedsFiller ) {
832+
blockNeedsFiller.appendBogus();
833+
// IE tends to place selection after appended bogus, so we need to
834+
// select the original range (placed before bogus).
835+
selectionUpdateNeeded = CKEDITOR.env.ie;
829836
}
830837
}
831838

@@ -848,19 +855,45 @@
848855

849856
var fixedBlock = range.fixBlock( true, editor.activeEnterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
850857

851-
// For IE, we should remove any filler node which was introduced before.
852-
if ( CKEDITOR.env.ie ) {
858+
// For IE<11, we should remove any filler node which was introduced before.
859+
if ( !CKEDITOR.env.needsBrFiller ) {
853860
var first = fixedBlock.getFirst( isNotEmpty );
854-
if ( first && isNbsp( first ) ) {
861+
if ( first && isNbsp( first ) )
855862
first.remove();
856-
}
857863
}
858864

859-
range.select();
860-
// Cancel this selection change in favor of the next (correct). (#6811)
865+
selectionUpdateNeeded = 1;
866+
867+
// Cancel this selection change in favor of the next (correct). (#6811)
861868
evt.cancel();
862869
}
863870
}
871+
872+
if ( selectionUpdateNeeded )
873+
range.select();
874+
}
875+
876+
// Checks whether current selection requires br filler to be appended.
877+
// @returns Block which needs filler or falsy value.
878+
function needsBrFiller( selection, path ) {
879+
// Fake selection does not need filler, because it is fake.
880+
if ( selection.isFake )
881+
return 0;
882+
883+
// Ensure bogus br could help to move cursor (out of styles) to the end of block. (#7041)
884+
var pathBlock = path.block || path.blockLimit,
885+
lastNode = pathBlock && pathBlock.getLast( isNotEmpty );
886+
887+
// Check some specialities of the current path block:
888+
// 1. It is really displayed as block; (#7221)
889+
// 2. It doesn't end with one inner block; (#7467)
890+
// 3. It doesn't have bogus br yet.
891+
if (
892+
pathBlock && pathBlock.isBlockBoundary() &&
893+
!( lastNode && lastNode.type == CKEDITOR.NODE_ELEMENT && lastNode.isBlockBoundary() ) &&
894+
!pathBlock.is( 'pre' ) && !pathBlock.getBogus()
895+
)
896+
return pathBlock;
864897
}
865898

866899
function blockInputClick( evt ) {
@@ -1321,9 +1354,9 @@
13211354
// Auto paragraphing.
13221355
if ( !nodeData.isBlock && shouldAutoParagraph( that.editor, path.block, path.blockLimit ) && ( fixBlock = autoParagraphTag( that.editor ) ) ) {
13231356
fixBlock = doc.createElement( fixBlock );
1324-
!CKEDITOR.env.ie && fixBlock.appendBogus();
1357+
fixBlock.appendBogus();
13251358
range.insertNode( fixBlock );
1326-
if ( !CKEDITOR.env.ie && ( bogus = fixBlock.getBogus() ) )
1359+
if ( CKEDITOR.env.needsBrFiller && ( bogus = fixBlock.getBogus() ) )
13271360
bogus.remove();
13281361
range.moveToPosition( fixBlock, CKEDITOR.POSITION_BEFORE_END );
13291362
}
@@ -1427,8 +1460,12 @@
14271460

14281461
if ( bogusNeededBlocks ) {
14291462
// Bring back all block bogus nodes.
1430-
while ( ( node = bogusNeededBlocks.pop() ) )
1431-
node.append( CKEDITOR.env.ie ? range.document.createText( '\u00a0' ) : range.document.createElement( 'br' ) );
1463+
while ( ( node = bogusNeededBlocks.pop() ) ) {
1464+
if ( CKEDITOR.env.needsBrFiller )
1465+
node.appendBogus();
1466+
else
1467+
node.append( range.document.createText( '\u00a0' ) );
1468+
}
14321469
}
14331470

14341471
// Eventually merge identical inline elements.

core/env.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,24 @@ if ( !CKEDITOR.env ) {
288288
*/
289289
env.hidpi = window.devicePixelRatio >= 2;
290290

291+
/**
292+
* Indicates that CKEditor is running in a browser which uses a bogus
293+
* `<br>` filler in order to correctly display caret in empty blocks.
294+
*
295+
* @since 4.3
296+
* @property {Boolean}
297+
*/
298+
env.needsBrFiller = env.gecko || env.webkit || ( env.ie && version > 10 );
299+
300+
/**
301+
* Indicates that CKEditor is running in a browser which needs a
302+
* non-breaking space filler in order to correctly display caret in empty blocks.
303+
*
304+
* @since 4.3
305+
* @property {Boolean}
306+
*/
307+
env.needsNbspFiller = env.ie && version < 11;
308+
291309
/**
292310
* A CSS class that denotes the browser where CKEditor runs and is appended
293311
* to the HTML element that contains the editor. It makes it easier to apply

core/htmldataprocessor.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,13 @@
290290
//
291291
// Various forms of the filler:
292292
// In output HTML: Filler should be consistently &NBSP; <BR> at the end of block is always considered as bogus.
293-
// In Wysiwyg HTML: Browser dependent - Filler is either BR for non-IE, or &NBSP; for IE, <BR> is NEVER considered as bogus for IE.
293+
// In Wysiwyg HTML: Browser dependent - see env.needsBrFiller. Either BR for when needsBrFiller is true, or &NBSP; otherwise.
294+
// <BR> is NEVER considered as bogus when needsBrFiller is true.
294295
function createBogusAndFillerRules( editor, type ) {
295296
function createFiller( isOutput ) {
296-
return isOutput || CKEDITOR.env.ie ?
297-
new CKEDITOR.htmlParser.text( '\xa0' ) :
298-
new CKEDITOR.htmlParser.element( 'br', { 'data-cke-bogus': 1 } );
297+
return isOutput || CKEDITOR.env.needsNbspFiller ?
298+
new CKEDITOR.htmlParser.text( '\xa0' ) :
299+
new CKEDITOR.htmlParser.element( 'br', { 'data-cke-bogus': 1 } );
299300
}
300301

301302
// This text block filter, remove any bogus and create the filler on demand.
@@ -348,8 +349,8 @@
348349
// Determinate whether this node is potentially a bogus node.
349350
function maybeBogus( node, atBlockEnd ) {
350351

351-
// BR that's not from IE DOM, except for a EOL marker.
352-
if ( !( isOutput && CKEDITOR.env.ie ) &&
352+
// BR that's not from IE<11 DOM, except for a EOL marker.
353+
if ( !( isOutput && !CKEDITOR.env.needsBrFiller ) &&
353354
node.type == CKEDITOR.NODE_ELEMENT && node.name == 'br' &&
354355
!node.attributes[ 'data-cke-eol' ] )
355356
return true;
@@ -365,8 +366,8 @@
365366
node.value = match[ 0 ];
366367
}
367368

368-
// From IE DOM, at the end of a text block, or before block boundary.
369-
if ( CKEDITOR.env.ie && isOutput && ( !atBlockEnd || node.parent.name in textBlockTags ) )
369+
// From IE<11 DOM, at the end of a text block, or before block boundary.
370+
if ( !CKEDITOR.env.needsBrFiller && isOutput && ( !atBlockEnd || node.parent.name in textBlockTags ) )
370371
return true;
371372

372373
// From the output.
@@ -426,13 +427,13 @@
426427
// Judge whether it's an empty block that requires a filler node.
427428
function isEmptyBlockNeedFiller( block ) {
428429

429-
// DO NOT fill empty editable in IE.
430-
if ( !isOutput && CKEDITOR.env.ie && block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT )
430+
// DO NOT fill empty editable in IE<11.
431+
if ( !isOutput && !CKEDITOR.env.needsBrFiller && block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT )
431432
return false;
432433

433434
// 1. For IE version >=8, empty blocks are displayed correctly themself in wysiwiyg;
434435
// 2. For the rest, at least table cell and list item need no filler space. (#6248)
435-
if ( !isOutput && CKEDITOR.env.ie &&
436+
if ( !isOutput && !CKEDITOR.env.needsBrFiller &&
436437
( document.documentMode > 7 ||
437438
block.name in CKEDITOR.dtd.tr ||
438439
block.name in CKEDITOR.dtd.$listItem ) ) {

core/htmlparser/fragment.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,7 @@ CKEDITOR.htmlParser.fragment = function() {
431431
// Parse it.
432432
parser.parse( fragmentHtml );
433433

434-
// Send all pending BRs except one, which we consider a unwanted bogus. (#5293)
435-
sendPendingBRs( !CKEDITOR.env.ie && 1 );
434+
sendPendingBRs();
436435

437436
// Close all pending nodes, make sure return point is properly restored.
438437
while ( currentNode != root )

plugins/enterkey/plugin.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
}
131131

132132
else if ( !needsBlock ) {
133-
block.appendBogus();
133+
block.appendBogus( true );
134134

135135
// If block is the first or last child of the parent
136136
// list, move all block's children out of the list:
@@ -301,9 +301,8 @@
301301
};
302302

303303
node = walker.next();
304-
if ( node && node.type == CKEDITOR.NODE_ELEMENT && node.is( 'ul', 'ol' ) ) {
305-
( CKEDITOR.env.ie ? doc.createText( '\xa0' ) : doc.createElement( 'br' ) ).insertBefore( node );
306-
}
304+
if ( node && node.type == CKEDITOR.NODE_ELEMENT && node.is( 'ul', 'ol' ) )
305+
( CKEDITOR.env.needsBrFiller ? doc.createElement( 'br' ) : doc.createText( '\xa0' ) ).insertBefore( node );
307306
}
308307

309308
// Move the selection to the end block.
@@ -356,8 +355,7 @@
356355
}
357356
}
358357

359-
if ( !CKEDITOR.env.ie )
360-
newBlock.appendBogus();
358+
newBlock.appendBogus();
361359

362360
if ( !newBlock.getParent() )
363361
range.insertNode( newBlock );
@@ -445,8 +443,8 @@
445443
range.deleteContents();
446444
range.insertNode( lineBreak );
447445

448-
// IE has different behavior regarding position.
449-
if ( CKEDITOR.env.ie )
446+
// Old IEs have different behavior regarding position.
447+
if ( !CKEDITOR.env.needsBrFiller )
450448
range.setStartAt( lineBreak, CKEDITOR.POSITION_AFTER_END );
451449
else {
452450
// A text node is required by Gecko only to make the cursor blink.

0 commit comments

Comments
 (0)