Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 't/11636' into major
  • Loading branch information
Piotr Jasiun committed Jan 29, 2015
2 parents 2de5349 + 76980e0 commit 8470d94
Show file tree
Hide file tree
Showing 15 changed files with 1,574 additions and 206 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -22,6 +22,7 @@ New Features:
* [#11580](http://dev.ckeditor.com/ticket/11580): Introduced the [notifications system](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.notification).
* [#11461](http://dev.ckeditor.com/ticket/11461): Introduced support for uploading pasted and dropped images with architecture for handling other types.
* [#12810](http://dev.ckeditor.com/ticket/12810): Introduced [notifications aggregator](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.notificationAggregator) for the [notifications system](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.notification).
* [#11636](http://dev.ckeditor.com/ticket/11636): Introduce new, focused on UX, methods for getting selected HTML and deleting it.

Fixed Issues:

Expand Down
17 changes: 16 additions & 1 deletion core/dom/documentfragment.js
Expand Up @@ -41,9 +41,24 @@ CKEDITOR.tools.extend( CKEDITOR.dom.documentFragment.prototype, CKEDITOR.dom.ele
insertAfterNode: function( node ) {
node = node.$;
node.parentNode.insertBefore( this.$, node.nextSibling );
},

/**
* Gets HTML of this DocumentFragment's children.
*
* @since 4.5
* @returns {String} The HTML of this DocumentFragment's children.
*/
getHtml: function() {
var container = new CKEDITOR.dom.element( 'div' );

// Note node.clone( 1 ) would purge ids.
new CKEDITOR.dom.documentFragment( this.$.cloneNode( 1 ) ).appendTo( container );

return container.getHtml();
}
}, true, {
'append': 1, 'appendBogus': 1, 'getFirst': 1, 'getLast': 1, 'getParent': 1, 'getNext': 1, 'getPrevious': 1,
'append': 1, 'appendBogus': 1, 'getFirst': 1, 'getHtml': 1, 'getLast': 1, 'getParent': 1, 'getNext': 1, 'getPrevious': 1,
'appendTo': 1, 'moveChildren': 1, 'insertBefore': 1, 'insertAfterNode': 1, 'replace': 1, 'trim': 1, 'type': 1,
'ltrim': 1, 'rtrim': 1, 'getDocument': 1, 'getChildCount': 1, 'getChild': 1, 'getChildren': 1
} );
36 changes: 25 additions & 11 deletions core/dom/range.js
Expand Up @@ -2329,7 +2329,7 @@ CKEDITOR.dom.range = function( root ) {

/**
* Moves the range boundaries to the closest editing point after/before an
* element.
* element or the current range position (depends on whether the element was specified).
*
* For example, if the start element has `id="start"`,
* `<p><b>foo</b><span id="start">start</start></p>`, the closest previous editing point is
Expand All @@ -2338,38 +2338,52 @@ CKEDITOR.dom.range = function( root ) {
* See also: {@link #moveToElementEditablePosition}.
*
* @since 4.3
* @param {CKEDITOR.dom.element} element The starting element.
* @param {Boolean} isMoveToEnd Whether move to the end of editable. Otherwise, look back.
* @param {CKEDITOR.dom.element} [element] The starting element. If not specified the current range
* position will be used.
* @param {Boolean} [isMoveForward] Whether move to the end of editable. Otherwise, look back.
* @returns {Boolean} Whether the range was moved.
*/
moveToClosestEditablePosition: function( element, isMoveToEnd ) {
moveToClosestEditablePosition: function( element, isMoveForward ) {
// We don't want to modify original range if there's no editable position.
var range = new CKEDITOR.dom.range( this.root ),
var range,
found = 0,
sibling,
isElement,
positions = [ CKEDITOR.POSITION_AFTER_END, CKEDITOR.POSITION_BEFORE_START ];

// Set collapsed range at one of ends of element.
range.moveToPosition( element, positions[ isMoveToEnd ? 0 : 1 ] );
if ( element ) {
// Set collapsed range at one of ends of element.
// Can't clone this range, because this range might not be yet positioned (no containers => errors).
range = new CKEDITOR.dom.range( this.root );
range.moveToPosition( element, positions[ isMoveForward ? 0 : 1 ] );
} else {
range = this.clone();
}

// Start element isn't a block, so we can automatically place range
// next to it.
if ( !element.is( CKEDITOR.dtd.$block ) )
if ( element && !element.is( CKEDITOR.dtd.$block ) )
found = 1;
else {
// Look for first node that fulfills eval function and place range next to it.
sibling = range[ isMoveToEnd ? 'getNextEditableNode' : 'getPreviousEditableNode' ]();
sibling = range[ isMoveForward ? 'getNextEditableNode' : 'getPreviousEditableNode' ]();
if ( sibling ) {
found = 1;
isElement = sibling.type == CKEDITOR.NODE_ELEMENT;

// Special case - eval accepts block element only if it's a non-editable block,
// which we want to select, not place collapsed selection next to it (which browsers
// can't handle).
if ( sibling.type == CKEDITOR.NODE_ELEMENT && sibling.is( CKEDITOR.dtd.$block ) && sibling.getAttribute( 'contenteditable' ) == 'false' ) {
if ( isElement && sibling.is( CKEDITOR.dtd.$block ) && sibling.getAttribute( 'contenteditable' ) == 'false' ) {
range.setStartAt( sibling, CKEDITOR.POSITION_BEFORE_START );
range.setEndAt( sibling, CKEDITOR.POSITION_AFTER_END );
}
// Handle empty blocks which can be selection containers on old IEs.
else if ( !CKEDITOR.env.needsBrFiller && isElement && sibling.is( CKEDITOR.dom.walker.validEmptyBlockContainers ) ) {
range.setEnd( sibling, 0 );
range.collapse();
} else {
range.moveToPosition( sibling, positions[ isMoveToEnd ? 1 : 0 ] );
range.moveToPosition( sibling, positions[ isMoveForward ? 1 : 0 ] );
}
}
}
Expand Down
52 changes: 39 additions & 13 deletions core/dom/walker.js
Expand Up @@ -527,16 +527,31 @@

var isIgnored = CKEDITOR.dom.walker.ignored();

function isEmpty( node ) {
var i = 0,
l = node.getChildCount();
/**
* Returns a function which checks whether node is empty.
*
* @since 4.5
* @static
* @param {Boolean} [isReject=false] Whether should return `false` for the
* ignored element instead of `true` (default).
* @returns {Function}
*/
CKEDITOR.dom.walker.empty = function( isReject ) {
return function( node ) {
var i = 0,
l = node.getChildCount();

for ( ; i < l; ++i ) {
if ( !isIgnored( node.getChild( i ) ) )
return false;
}
return true;
}
for ( ; i < l; ++i ) {
if ( !isIgnored( node.getChild( i ) ) ) {
return !!isReject;
}
}

return !isReject;
};
};

var isEmpty = CKEDITOR.dom.walker.empty();

function filterTextContainers( dtd ) {
var hash = {},
Expand All @@ -549,8 +564,18 @@
return hash;
}

// Block elements which can contain text nodes (without ul, ol, dl, etc.).
var dtdTextBlock = filterTextContainers( CKEDITOR.dtd.$block );
/**
* A hash of element names which on browsers which {@link CKEDITOR.env#needsBrFiller do not need `<br>` fillers}
* can be selection containers despite being empty.
*
* @since 4.5
* @static
* @property {Object} validEmptyBlockContainers
*/
var validEmptyBlocks = CKEDITOR.dom.walker.validEmptyBlockContainers = CKEDITOR.tools.extend(
filterTextContainers( CKEDITOR.dtd.$block ),
{ caption: 1, td: 1, th: 1 }
);

function isEditable( node ) {
// Skip temporary elements, bookmarks and whitespaces.
Expand All @@ -569,7 +594,7 @@
return true;

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

Expand All @@ -589,7 +614,8 @@
* it's only `<hr>`),
* * non-editable blocks (special case - such blocks cannot be containers nor
* siblings, they need to be selected entirely),
* * empty blocks which can contain text (IE only).
* * empty {@link #validEmptyBlockContainers blocks} which can contain text
* ({@link CKEDITOR.env#needsBrFiller old IEs only}).
*
* @since 4.3
* @static
Expand Down

0 comments on commit 8470d94

Please sign in to comment.