Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 't/11980'
  • Loading branch information
oleq committed May 28, 2014
2 parents edb73ce + 84270a9 commit a0510b6
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 72 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -11,6 +11,7 @@ Fixed Issues:
* [#11960](http://dev.ckeditor.com/ticket/11960): [Blink/Webkit] Fixed: The caret should be scrolled into view on *Backspace* and *Delete* (covers only the merging blocks case).
* [#11306](http://dev.ckeditor.com/ticket/11306): [OSX][Blink/Webkit] Fixed: No widget's entries in context menu on widget right-click.
* [#11957](http://dev.ckeditor.com/ticket/11957): Fixed: Alignment labels in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog are not translated.
* [#11980](http://dev.ckeditor.com/ticket/11980): [Blink/Webkit] Fixed: Span elements created while joining adjacent elements (non-collapsed selection).

## CKEditor 4.4.1

Expand Down
212 changes: 140 additions & 72 deletions core/editable.js
Expand Up @@ -812,81 +812,19 @@
return;

var backspace = key == 8,
selection = editor.getSelection(),
range = selection.getRanges()[ 0 ],
startPath = range.startPath(),
startBlock = startPath.block;

// Selection must be collapsed and to be anchored in a block.
if ( !range.collapsed || !startBlock )
return;

// Exclude cases where, i.e. if pressed arrow key, selection
// would move within the same block (merge inside a block).
if ( !range[ backspace ? 'checkStartOfBlock' : 'checkEndOfBlock' ]() )
return;

// Make sure, there's an editable position to put selection,
// which i.e. would be used if pressed arrow key, but abort
// if such position exists but means a selected non-editable element.
if ( !range.moveToClosestEditablePosition( startBlock, !backspace ) || !range.collapsed )
return;

// Handle special case, when block's sibling is a <hr>. Delete it and keep selection
// in the same place (http://dev.ckeditor.com/ticket/11861#comment:9).
if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT ) {
var touched = range.startContainer.getChild( range.startOffset - ( backspace ? 1 : 0 ) );
if ( touched && touched.type == CKEDITOR.NODE_ELEMENT && touched.is( 'hr' ) ) {
editor.fire( 'saveSnapshot' );
touched.remove();
editor.fire( 'saveSnapshot' );
return false;
}
range = editor.getSelection().getRanges()[ 0 ],
startPath = range.startPath();

if ( range.collapsed ) {
if ( !mergeBlocksCollapsedSelection( editor, range, backspace, startPath ) )
return;
} else {
if ( !mergeBlocksNonCollapsedSelection( editor, range, startPath ) )
return;
}

var siblingBlock = range.startPath().block;

// Abort if an editable position exists, but either it's not
// in a block or that block is the parent of the start block
// (merging child into parent).
if ( !siblingBlock || ( siblingBlock && siblingBlock.contains( startBlock ) ) )
return;

editor.fire( 'saveSnapshot' );

var commonParent = startBlock.getCommonAncestor( siblingBlock ),
node = backspace ? startBlock : siblingBlock,
removableParent = node;

// Find an element (DOM branch), which contains the block
// to be merged but nothing else, so if removed, it will
// leave no empty parents.
while ( ( node = node.getParent() ) && !commonParent.equals( node ) && node.getChildCount() == 1 )
removableParent = node;

// Remove bogus to avoid duplicated boguses.
var bogus;
if ( ( bogus = ( backspace ? siblingBlock : startBlock ).getBogus() ) )
bogus.remove();

// Save selection. It will be restored.
var bookmarks = selection.createBookmarks();

// Merge blocks.
( backspace ? startBlock : siblingBlock ).moveChildren( backspace ? siblingBlock : startBlock, false );

// Also merge children along with parents.
startPath.lastElement.mergeSiblings();

// Cut off removable branch of the DOM tree.
removableParent.remove();

// Restore selection.
selection.selectBookmarks( bookmarks );

// Scroll to the new position of the caret (#11960).
selection.scrollIntoView();

editor.getSelection().scrollIntoView();
editor.fire( 'saveSnapshot' );

return false;
Expand Down Expand Up @@ -2057,6 +1995,136 @@
};
} )();

function mergeBlocksCollapsedSelection( editor, range, backspace, startPath ) {
var startBlock = startPath.block;

// Selection must be collapsed and to be anchored in a block.
if ( !startBlock )
return false;

// Exclude cases where, i.e. if pressed arrow key, selection
// would move within the same block (merge inside a block).
if ( !range[ backspace ? 'checkStartOfBlock' : 'checkEndOfBlock' ]() )
return false;

// Make sure, there's an editable position to put selection,
// which i.e. would be used if pressed arrow key, but abort
// if such position exists but means a selected non-editable element.
if ( !range.moveToClosestEditablePosition( startBlock, !backspace ) || !range.collapsed )
return false;

// Handle special case, when block's sibling is a <hr>. Delete it and keep selection
// in the same place (http://dev.ckeditor.com/ticket/11861#comment:9).
if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT ) {
var touched = range.startContainer.getChild( range.startOffset - ( backspace ? 1 : 0 ) );
if ( touched && touched.type == CKEDITOR.NODE_ELEMENT && touched.is( 'hr' ) ) {
editor.fire( 'saveSnapshot' );
touched.remove();
return true;
}
}

var siblingBlock = range.startPath().block;

// Abort if an editable position exists, but either it's not
// in a block or that block is the parent of the start block
// (merging child into parent).
if ( !siblingBlock || ( siblingBlock && siblingBlock.contains( startBlock ) ) )
return;

editor.fire( 'saveSnapshot' );

// Remove bogus to avoid duplicated boguses.
var bogus;
if ( ( bogus = ( backspace ? siblingBlock : startBlock ).getBogus() ) )
bogus.remove();

// Save selection. It will be restored.
var selection = editor.getSelection(),
bookmarks = selection.createBookmarks();

// Merge blocks.
( backspace ? startBlock : siblingBlock ).moveChildren( backspace ? siblingBlock : startBlock, false );

// Also merge children along with parents.
startPath.lastElement.mergeSiblings();

// Cut off removable branch of the DOM tree.
pruneEmptyDisjointAncestors( startBlock, siblingBlock, !backspace );

// Restore selection.
selection.selectBookmarks( bookmarks );

return true;
}

function mergeBlocksNonCollapsedSelection( editor, range, startPath ) {
var startBlock = startPath.block,
endPath = range.endPath(),
endBlock = endPath.block;

// Selection must be anchored in two different blocks.
if ( !startBlock || !endBlock || startBlock.equals( endBlock ) )
return false;

editor.fire( 'saveSnapshot' );

// Remove bogus to avoid duplicated boguses.
var bogus;
if ( ( bogus = startBlock.getBogus() ) )
bogus.remove();

// Delete range contents. Do NOT merge. Merging is weird.
range.deleteContents();

// If something has left of the block to be merged, clean it up.
// It may happen when merging with list items.
if ( endBlock.getParent() ) {
// Move children to the first block.
endBlock.moveChildren( startBlock, false );

// ...and merge them if that's possible.
startPath.lastElement.mergeSiblings();

// If expanded selection, things are always merged like with BACKSPACE.
pruneEmptyDisjointAncestors( startBlock, endBlock, true );
}

// Make sure the result selection is collapsed.
range = editor.getSelection().getRanges()[ 0 ];
range.collapse( 1 );
range.select();

return true;
}

// Finds the innermost child of common parent, which,
// if removed, removes nothing but the contents of the element.
//
// before: <div><p><strong>first</strong></p><p>second</p></div>
// after: <div><p>second</p></div>
//
// before: <div><p>x<strong>first</strong></p><p>second</p></div>
// after: <div><p>x</p><p>second</p></div>
//
// isPruneToEnd=true
// before: <div><p><strong>first</strong></p><p>second</p></div>
// after: <div><p><strong>first</strong></p></div>
//
// @param {CKEDITOR.dom.element} first
// @param {CKEDITOR.dom.element} second
// @param {Boolean} isPruneToEnd
function pruneEmptyDisjointAncestors( first, second, isPruneToEnd ) {
var commonParent = first.getCommonAncestor( second ),
node = isPruneToEnd ? second : first,
removableParent = node;

while ( ( node = node.getParent() ) && !commonParent.equals( node ) && node.getChildCount() == 1 )
removableParent = node;

removableParent.remove();
}

} )();

/**
Expand Down

0 comments on commit a0510b6

Please sign in to comment.