Skip to content

Commit

Permalink
Merge branch 't/10907' into major
Browse files Browse the repository at this point in the history
  • Loading branch information
Reinmar committed Oct 30, 2013
2 parents a2d9130 + 947c510 commit ac1285f
Show file tree
Hide file tree
Showing 17 changed files with 148 additions and 87 deletions.
11 changes: 9 additions & 2 deletions core/dom/element.js
Expand Up @@ -250,9 +250,16 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype, {
},

/**
* @todo
* Appends a `<br>` filler element to this element if the filler is not present already.
* By default filler is appended only if {@link CKEDITOR.env#needsBrFiller} is `true`,
* however when `force` is set to `true` filler will be appended regardless of the environment.
*
* @param {Boolean} [force] Append filler regardless of the environment.
*/
appendBogus: function() {
appendBogus: function( force ) {
if ( !force && !( CKEDITOR.env.needsBrFiller || CKEDITOR.env.opera ) )
return;

var lastChild = this.getLast();

// Ignore empty/spaces text.
Expand Down
4 changes: 2 additions & 2 deletions core/dom/iterator.js
Expand Up @@ -339,8 +339,8 @@
if ( removeLastBr ) {
var lastChild = block.getLast();
if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.getName() == 'br' ) {
// Take care not to remove the block expanding <br> in non-IE browsers.
if ( CKEDITOR.env.ie || lastChild.getPrevious( bookmarkGuard ) || lastChild.getNext( bookmarkGuard ) )
// Remove br filler on browser which do not need it.
if ( !CKEDITOR.env.needsBrFiller || lastChild.getPrevious( bookmarkGuard ) || lastChild.getNext( bookmarkGuard ) )
lastChild.remove();
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/dom/node.js
Expand Up @@ -686,7 +686,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, {
break;
}

if ( !CKEDITOR.env.ie && !CKEDITOR.env.opera ) {
if ( CKEDITOR.env.needsBrFiller ) {
child = this.$.lastChild;

if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' ) {
Expand Down
5 changes: 2 additions & 3 deletions core/dom/range.js
Expand Up @@ -1660,8 +1660,7 @@ CKEDITOR.dom.range = function( root ) {
this.extractContents().appendTo( fixedBlock );
fixedBlock.trim();

if ( !CKEDITOR.env.ie )
fixedBlock.appendBogus();
fixedBlock.appendBogus();

this.insertNode( fixedBlock );

Expand Down Expand Up @@ -1722,7 +1721,7 @@ CKEDITOR.dom.range = function( root ) {
// In Gecko, the last child node must be a bogus <br>.
// Note: bogus <br> added under <ul> or <ol> would cause
// lists to be incorrectly rendered.
if ( !CKEDITOR.env.ie && !startBlock.is( 'ul', 'ol' ) )
if ( !startBlock.is( 'ul', 'ol' ) )
startBlock.appendBogus();
}
}
Expand Down
6 changes: 3 additions & 3 deletions core/dom/walker.js
Expand Up @@ -451,7 +451,7 @@
}

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

if ( isBogus ) {
var parent = node.getParent(),
Expand Down Expand Up @@ -560,7 +560,7 @@
return true;

// Empty blocks are editable on IE.
if ( CKEDITOR.env.ie && node.is( dtdTextBlock ) && isEmpty( node ) )
if ( !CKEDITOR.env.needsBrFiller && node.is( dtdTextBlock ) && isEmpty( node ) )
return true;
}

Expand Down Expand Up @@ -606,7 +606,7 @@
}
while ( toSkip( tail ) )

if ( tail && ( !CKEDITOR.env.ie ? tail.is && tail.is( 'br' ) : tail.getText && tailNbspRegex.test( tail.getText() ) ) ) {
if ( tail && ( CKEDITOR.env.needsBrFiller ? tail.is && tail.is( 'br' ) : tail.getText && tailNbspRegex.test( tail.getText() ) ) ) {
return tail;
}
return false;
Expand Down
97 changes: 67 additions & 30 deletions core/editable.js
Expand Up @@ -587,6 +587,8 @@
evt.preventDefault();
} );

var backspaceOrDelete = { 8:1,46:1 };

// Override keystrokes which should have deletion behavior
// on fully selected element . (#4047) (#7645)
this.attachListener( editor, 'key', function( evt ) {
Expand All @@ -596,7 +598,7 @@
var keyCode = evt.data.keyCode, isHandled;

// Backspace OR Delete.
if ( keyCode in { 8:1,46:1 } ) {
if ( keyCode in backspaceOrDelete ) {
var sel = editor.getSelection(),
selected,
range = sel.getRanges()[ 0 ],
Expand Down Expand Up @@ -678,6 +680,20 @@
return !isHandled;
} );

// On IE>=11 we need to fill blockless editable with <br> if it was deleted.
if ( editor.blockless && CKEDITOR.env.ie && CKEDITOR.env.needsBrFiller ) {
this.attachListener( this, 'keyup', function( evt ) {
if ( evt.data.getKeystroke() in backspaceOrDelete && !this.getFirst( isNotEmpty ) ) {
this.appendBogus();

// Set the selection before bogus, because IE tends to put it after.
var range = editor.createRange();
range.moveToPosition( this, CKEDITOR.POSITION_AFTER_START );
range.select();
}
} );
}

this.attachListener( this, 'dblclick', function( evt ) {
if ( editor.readOnly )
return false;
Expand Down Expand Up @@ -807,25 +823,16 @@
blockLimit = path.blockLimit,
selection = evt.data.selection,
range = selection.getRanges()[ 0 ],
enterMode = editor.activeEnterMode;

if ( CKEDITOR.env.gecko ) {
// v3: check if this is needed.
// activateEditing( editor );

// Ensure bogus br could help to move cursor (out of styles) to the end of block. (#7041)
var pathBlock = path.block || path.blockLimit || path.root,
lastNode = pathBlock && pathBlock.getLast( isNotEmpty );

// Check some specialities of the current path block:
// 1. It is really displayed as block; (#7221)
// 2. It doesn't end with one inner block; (#7467)
// 3. It doesn't have bogus br yet.
if ( pathBlock && pathBlock.isBlockBoundary() &&
!( lastNode && lastNode.type == CKEDITOR.NODE_ELEMENT && lastNode.isBlockBoundary() ) &&
!pathBlock.is( 'pre' ) && !pathBlock.getBogus() ) {

pathBlock.appendBogus();
enterMode = editor.activeEnterMode,
selectionUpdateNeeded;

if ( CKEDITOR.env.gecko || ( CKEDITOR.env.ie && CKEDITOR.env.needsBrFiller ) ) {
var blockNeedsFiller = needsBrFiller( selection, path );
if ( blockNeedsFiller ) {
blockNeedsFiller.appendBogus();
// IE tends to place selection after appended bogus, so we need to
// select the original range (placed before bogus).
selectionUpdateNeeded = CKEDITOR.env.ie;
}
}

Expand All @@ -848,19 +855,45 @@

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

// For IE, we should remove any filler node which was introduced before.
if ( CKEDITOR.env.ie ) {
// For IE<11, we should remove any filler node which was introduced before.
if ( !CKEDITOR.env.needsBrFiller ) {
var first = fixedBlock.getFirst( isNotEmpty );
if ( first && isNbsp( first ) ) {
if ( first && isNbsp( first ) )
first.remove();
}
}

range.select();
// Cancel this selection change in favor of the next (correct). (#6811)
selectionUpdateNeeded = 1;

// Cancel this selection change in favor of the next (correct). (#6811)
evt.cancel();
}
}

if ( selectionUpdateNeeded )
range.select();
}

// Checks whether current selection requires br filler to be appended.
// @returns Block which needs filler or falsy value.
function needsBrFiller( selection, path ) {
// Fake selection does not need filler, because it is fake.
if ( selection.isFake )
return 0;

// Ensure bogus br could help to move cursor (out of styles) to the end of block. (#7041)
var pathBlock = path.block || path.blockLimit,
lastNode = pathBlock && pathBlock.getLast( isNotEmpty );

// Check some specialities of the current path block:
// 1. It is really displayed as block; (#7221)
// 2. It doesn't end with one inner block; (#7467)
// 3. It doesn't have bogus br yet.
if (
pathBlock && pathBlock.isBlockBoundary() &&
!( lastNode && lastNode.type == CKEDITOR.NODE_ELEMENT && lastNode.isBlockBoundary() ) &&
!pathBlock.is( 'pre' ) && !pathBlock.getBogus()
)
return pathBlock;
}

function blockInputClick( evt ) {
Expand Down Expand Up @@ -1321,9 +1354,9 @@
// Auto paragraphing.
if ( !nodeData.isBlock && shouldAutoParagraph( that.editor, path.block, path.blockLimit ) && ( fixBlock = autoParagraphTag( that.editor ) ) ) {
fixBlock = doc.createElement( fixBlock );
!CKEDITOR.env.ie && fixBlock.appendBogus();
fixBlock.appendBogus();
range.insertNode( fixBlock );
if ( !CKEDITOR.env.ie && ( bogus = fixBlock.getBogus() ) )
if ( CKEDITOR.env.needsBrFiller && ( bogus = fixBlock.getBogus() ) )
bogus.remove();
range.moveToPosition( fixBlock, CKEDITOR.POSITION_BEFORE_END );
}
Expand Down Expand Up @@ -1427,8 +1460,12 @@

if ( bogusNeededBlocks ) {
// Bring back all block bogus nodes.
while ( ( node = bogusNeededBlocks.pop() ) )
node.append( CKEDITOR.env.ie ? range.document.createText( '\u00a0' ) : range.document.createElement( 'br' ) );
while ( ( node = bogusNeededBlocks.pop() ) ) {
if ( CKEDITOR.env.needsBrFiller )
node.appendBogus();
else
node.append( range.document.createText( '\u00a0' ) );
}
}

// Eventually merge identical inline elements.
Expand Down
18 changes: 18 additions & 0 deletions core/env.js
Expand Up @@ -288,6 +288,24 @@ if ( !CKEDITOR.env ) {
*/
env.hidpi = window.devicePixelRatio >= 2;

/**
* Indicates that CKEditor is running in a browser which uses a bogus
* `<br>` filler in order to correctly display caret in empty blocks.
*
* @since 4.3
* @property {Boolean}
*/
env.needsBrFiller = env.gecko || env.webkit || ( env.ie && version > 10 );

/**
* Indicates that CKEditor is running in a browser which needs a
* non-breaking space filler in order to correctly display caret in empty blocks.
*
* @since 4.3
* @property {Boolean}
*/
env.needsNbspFiller = env.ie && version < 11;

/**
* A CSS class that denotes the browser where CKEditor runs and is appended
* to the HTML element that contains the editor. It makes it easier to apply
Expand Down
23 changes: 12 additions & 11 deletions core/htmldataprocessor.js
Expand Up @@ -290,12 +290,13 @@
//
// Various forms of the filler:
// In output HTML: Filler should be consistently &NBSP; <BR> at the end of block is always considered as bogus.
// In Wysiwyg HTML: Browser dependent - Filler is either BR for non-IE, or &NBSP; for IE, <BR> is NEVER considered as bogus for IE.
// In Wysiwyg HTML: Browser dependent - see env.needsBrFiller. Either BR for when needsBrFiller is true, or &NBSP; otherwise.
// <BR> is NEVER considered as bogus when needsBrFiller is true.
function createBogusAndFillerRules( editor, type ) {
function createFiller( isOutput ) {
return isOutput || CKEDITOR.env.ie ?
new CKEDITOR.htmlParser.text( '\xa0' ) :
new CKEDITOR.htmlParser.element( 'br', { 'data-cke-bogus': 1 } );
return isOutput || CKEDITOR.env.needsNbspFiller ?
new CKEDITOR.htmlParser.text( '\xa0' ) :
new CKEDITOR.htmlParser.element( 'br', { 'data-cke-bogus': 1 } );
}

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

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

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

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

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

// 1. For IE version >=8, empty blocks are displayed correctly themself in wysiwiyg;
// 2. For the rest, at least table cell and list item need no filler space. (#6248)
if ( !isOutput && CKEDITOR.env.ie &&
if ( !isOutput && !CKEDITOR.env.needsBrFiller &&
( document.documentMode > 7 ||
block.name in CKEDITOR.dtd.tr ||
block.name in CKEDITOR.dtd.$listItem ) ) {
Expand Down
3 changes: 1 addition & 2 deletions core/htmlparser/fragment.js
Expand Up @@ -431,8 +431,7 @@ CKEDITOR.htmlParser.fragment = function() {
// Parse it.
parser.parse( fragmentHtml );

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

// Close all pending nodes, make sure return point is properly restored.
while ( currentNode != root )
Expand Down
14 changes: 6 additions & 8 deletions plugins/enterkey/plugin.js
Expand Up @@ -130,7 +130,7 @@
}

else if ( !needsBlock ) {
block.appendBogus();
block.appendBogus( true );

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

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

// Move the selection to the end block.
Expand Down Expand Up @@ -356,8 +355,7 @@
}
}

if ( !CKEDITOR.env.ie )
newBlock.appendBogus();
newBlock.appendBogus();

if ( !newBlock.getParent() )
range.insertNode( newBlock );
Expand Down Expand Up @@ -445,8 +443,8 @@
range.deleteContents();
range.insertNode( lineBreak );

// IE has different behavior regarding position.
if ( CKEDITOR.env.ie )
// Old IEs have different behavior regarding position.
if ( !CKEDITOR.env.needsBrFiller )
range.setStartAt( lineBreak, CKEDITOR.POSITION_AFTER_END );
else {
// A text node is required by Gecko only to make the cursor blink.
Expand Down
2 changes: 1 addition & 1 deletion plugins/forms/dialogs/form.js
Expand Up @@ -28,7 +28,7 @@ CKEDITOR.dialog.add( 'form', function( editor ) {
if ( isInsertMode ) {
editor = this.getParentEditor();
element = editor.document.createElement( 'form' );
!CKEDITOR.env.ie && element.append( editor.document.createElement( 'br' ) );
element.appendBogus();
}

if ( isInsertMode )
Expand Down

0 comments on commit ac1285f

Please sign in to comment.