Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 't/12178'
  • Loading branch information
Reinmar committed Aug 12, 2014
2 parents 18c6781 + e02e793 commit 5ea2a38
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -14,6 +14,7 @@ Fixed Issues:
* [#12243](http://dev.ckeditor.com/ticket/12243): Fixed: Text formatting lost while pasting from Word. Thanks to [Alin Purcaru](https://github.com/mesmerizero)!
* [#12273](http://dev.ckeditor.com/ticket/12273): Fixed: Applying block style in description list breaks it.
* [#12281](http://dev.ckeditor.com/ticket/12281): Fixed: Minor syntax issue in CSS files.
* [#12178](http://dev.ckeditor.com/ticket/12178): [Blink/Webkit] Fixed: Iterator does not return block if selection is located at the end of it.

## CKEditor 4.4.3

Expand Down
30 changes: 29 additions & 1 deletion core/dom/iterator.js
Expand Up @@ -361,11 +361,28 @@
function startIterator() {
var range = this.range.clone(),
// Indicate at least one of the range boundaries is inside a preformat block.
touchPre;
touchPre,

// (#12178)
// Remember if following situation takes place:
// * startAtInnerBoundary: <p>foo[</p>...
// * endAtInnerBoundary: ...<p>]bar</p>
// Because information about line break will be lost when shrinking range.
// Note that we test only if path block exist, because we must properly shrink
// range containing table and/or table cells.
startPath = range.startPath(),
endPath = range.endPath(),
startAtInnerBoundary = rangeAtInnerBlockBoundary( range, startPath.block ),
endAtInnerBoundary = rangeAtInnerBlockBoundary( range, endPath.block, 1 );

// Shrink the range to exclude harmful "noises" (#4087, #4450, #5435).
range.shrink( CKEDITOR.SHRINK_ELEMENT, true );

if ( startAtInnerBoundary )
range.setStartAt( startPath.block, CKEDITOR.POSITION_BEFORE_END );
if ( endAtInnerBoundary )
range.setEndAt( endPath.block, CKEDITOR.POSITION_AFTER_START );

touchPre = range.endContainer.hasAscendant( 'pre', true ) || range.startContainer.hasAscendant( 'pre', true );

range.enlarge( this.forceBrBreak && !touchPre || !this.enlargeBr ? CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS : CKEDITOR.ENLARGE_BLOCK_CONTENTS );
Expand Down Expand Up @@ -489,6 +506,17 @@
return 1;
}

// Checks whether range starts or ends at inner block boundary.
// See usage comments to learn more.
function rangeAtInnerBlockBoundary( range, block, checkEnd ) {
if ( !block )
return false;

var testRange = range.clone();
testRange.collapse( !checkEnd );
return testRange.checkBoundaryOfElement( block, checkEnd ? CKEDITOR.START : CKEDITOR.END );
}

/**
* Creates {CKEDITOR.dom.iterator} instance for this range.
*
Expand Down
135 changes: 122 additions & 13 deletions tests/core/dom/iterator.js
Expand Up @@ -46,6 +46,21 @@ function iterateScopedRange( html, opt ) {
return { html: tools.compatHtml( sandbox.getOuterHtml() ), list: results };
}

function iterateWithRangeIterator( ranges, mergeConsequent, rangeIteratorOpts ) {
var rangesIterator = ranges.createIterator(),
range,
blockList = [];

while ( range = rangesIterator.getNextRange( mergeConsequent ) ) {
var iter = range.createIterator(), block;
CKEDITOR.tools.extend( iter, rangeIteratorOpts, true );
while ( ( block = iter.getNextParagraph() ) )
blockList.push( block.getName() );
}

return blockList;
}

function checkActiveFilter( source, opt, results, msg ) {
var sandbox = doc.getById( 'sandbox' );
var range = tools.setHtmlWithRange( sandbox, source )[ 0 ];
Expand Down Expand Up @@ -91,6 +106,52 @@ bender.test( {
checkRangeIteration( source, null, [ 'p' ], output, 'Iteration will yield one single paragraph' );
},

// #12178
'test iterating over end of line': function() {
if ( !CKEDITOR.env.needsBrFiller )
assert.ignore();

var source = '<h1>para1[<br /></h1><p>par]a2</p>';
checkRangeIteration( source, null, [ 'h1', 'p' ], null, 'Iteration will yield heading and paragraph.' );
},

// #12178
'test iterating over end of line - no bogus br': function() {
var source = '<h1>para1[</h1><p>par]a2</p>';
checkRangeIteration( source, null, [ 'h1', 'p' ], null, 'Iteration will yield heading and paragraph.' );
},

// #12178
'test iterating over start of line': function() {
if ( !CKEDITOR.env.needsBrFiller )
assert.ignore();

var source = '<h1>pa[ra1<br /></h1><p>]para2</p>';
checkRangeIteration( source, null, [ 'h1', 'p' ], null, 'Iteration will yield heading and paragraph.' );
},

// #12178
'test iterating over start of line - no bogus br': function() {
var source = '<h1>pa[ra1</h1><p>]para2</p>';
checkRangeIteration( source, null, [ 'h1', 'p' ], null, 'Iteration will yield heading and paragraph.' );
},

// #12178
'test iterating over start of line 2 - no bogus br': function() {
var source = '<h1>pa[ra1</h1><p><b>]para2</b></p>';
checkRangeIteration( source, null, [ 'h1', 'p' ], null, 'Iteration will yield heading and paragraph.' );
},

// #12178
'test iterating over start of line 2 - no bogus br - rangeIterator': function() {
var source = '<h1>pa[ra1</h1><p><b>]para2</b></p>';

var sandbox = doc.getById( 'sandbox' ),
ranges = tools.setHtmlWithRange( sandbox, source );

assert.areSame( 'h1,p', iterateWithRangeIterator( ranges ).join( ',' ) );
},

'test iterating over pseudo block': function() {
var source = '<div><p>[paragraph</p>text]</div>';
var output = '<div><p>paragraph</p><p>text</p></div>';
Expand Down Expand Up @@ -139,31 +200,79 @@ bender.test( {
},

// #6728, #4450
// While this test may seem to be totally broken (why would someone create bookmakrs between <tr> and <td>?)
// it has a deeper sense. It tests what rangeIterator#getNextRange does.
'test iterating over table cells (with bookmarks among cells)': function() {
var source = '<table><tbody><tr>[<td id="cell1">cell1</td>][<td id="cell2">cell2</td>]</tr></tbody></table>',
output = source.replace( /\[|\]|\^/g, '' );

var sandbox = doc.getById( 'sandbox' );
var ranges = tools.setHtmlWithRange( sandbox, source );
var sandbox = doc.getById( 'sandbox' ),
ranges = tools.setHtmlWithRange( sandbox, source );

// Create bookmarks by intention.
// Create bookmarks intentionally.
var bms = ranges.createBookmarks();

// Check iteration sequence.
var rangeIterator = ranges.createIterator(), range, blockList = [];
// Merge multiple ranges.
while ( range = rangeIterator.getNextRange( true ) ) {
var iter = range.createIterator(), block;
while ( ( block = iter.getNextParagraph() ) )
blockList.push( block.getName() );
}
assert.areSame( 'td,td', iterateWithRangeIterator( ranges ).join( ',' ), true );

// Just to remove bookmarks.
ranges.moveToBookmarks( bms );

arrayAssert.itemsAreEqual( [ 'td', 'td' ], blockList );
assert.areSame( tools.compatHtml( output ), tools.compatHtml( sandbox.getHtml() ) );
},

// See above an explanation of this test.
'test iterating over table cells (with bookmarks among cells) - do not merge subsequent ranges': function() {
var source = '<table><tbody><tr>[<td id="cell1">cell1</td>][<td id="cell2">cell2</td>]</tr></tbody></table>',
output = source.replace( /\[|\]|\^/g, '' );

var sandbox = doc.getById( 'sandbox' ),
ranges = tools.setHtmlWithRange( sandbox, source );

// Create bookmarks intentionally.
var bms = ranges.createBookmarks();

assert.areSame( 'td,td', iterateWithRangeIterator( ranges ).join( ',' ) );

// Just to remove bookmarks.
ranges.moveToBookmarks( bms );

assert.areSame( tools.compatHtml( output ) , tools.compatHtml( sandbox.getHtml() ) );
assert.areSame( output, tools.compatHtml( sandbox.getHtml() ) );
},

// #6728, #4450
'test iterating over entire table': function() {
var source = '[<table><tbody><tr><th>cell1</th><td>cell2</td></tr></tbody></table>]',
output1 = source.replace( /\[|\]|\^/g, '' ),
output2 = '<table><tbody><tr><th><p>cell1</p></th><td><p>cell2</p></td></tr></tbody></table>';

checkRangeIteration( source, null, [ 'th', 'td' ], output1, 'Iteration should report table cells' );
checkRangeIteration( source, { enforceRealBlocks: 1 }, [ 'p', 'p' ], output2, 'Iteration should establish paragraphs inside table cells' );
},

// #6728, #4450
'test iterating over entire table - use rangeIterator': function() {
var source = '[<table><tbody><tr><th>cell1</th><td>cell2</td></tr></tbody></table>]',
output = source.replace( /\[|\]|\^/g, '' );

var sandbox = doc.getById( 'sandbox' ),
ranges = tools.setHtmlWithRange( sandbox, source );

assert.areSame( 'th,td', iterateWithRangeIterator( ranges, true ).join( ',' ) );

assert.areSame( output, tools.compatHtml( sandbox.getHtml() ) );
},

// #6728, #4450
'test iterating over entire table - use rangeIterator and enforceRealBlocks': function() {
var source = '[<table><tbody><tr><th>cell1</th><td>cell2</td></tr></tbody></table>]',
output = '<table><tbody><tr><th><p>cell1</p></th><td><p>cell2</p></td></tr></tbody></table>';

var sandbox = doc.getById( 'sandbox' ),
ranges = tools.setHtmlWithRange( sandbox, source );

assert.areSame( 'p,p', iterateWithRangeIterator( ranges, true, { enforceRealBlocks: true } ).join( ',' ) );

assert.areSame( output, tools.compatHtml( sandbox.getHtml() ) );
},

'test iterating over list items': function() {
Expand Down
67 changes: 67 additions & 0 deletions tests/core/dom/range/shrink.js
Expand Up @@ -179,6 +179,73 @@
assert.isFalse( range.collapsed, 'range.collapsed' );
},

'test shrink does stop on block boundary end - SHRINK_ELEMENT': function() {
var ct = doc.getById( 'editable_playground' ),
source = '<p>para1[</p><p>para2]</p>',
result = '<p>para1</p><p>[para2]</p>';

var range = bender.tools.setHtmlWithRange( ct, source )[ 0 ];
range.shrink( CKEDITOR.SHRINK_ELEMENT, true );
assert.areSame( result, bender.tools.getHtmlWithRanges( ct, new CKEDITOR.dom.rangeList( [ range ] ) ) );
},

'test shrink does stop on block boundary start - SHRINK_ELEMENT': function() {
var ct = doc.getById( 'editable_playground' ),
source = '<p>[para1</p><p>]para2</p>',
result = '<p>[para1]</p><p>para2</p>';

var range = bender.tools.setHtmlWithRange( ct, source )[ 0 ];
range.shrink( CKEDITOR.SHRINK_ELEMENT, true );
assert.areSame( result, bender.tools.getHtmlWithRanges( ct, new CKEDITOR.dom.rangeList( [ range ] ) ) );
},

'test shrink does stop on block boundary end - SHRINK_ELEMENT, no shrink on block boundary': function() {
var ct = doc.getById( 'editable_playground' ),
source = '<p>para1[</p><p>para2]</p>';

var range = bender.tools.setHtmlWithRange( ct, source )[ 0 ];
range.shrink( CKEDITOR.SHRINK_ELEMENT, true, false );
assert.areSame( source, bender.tools.getHtmlWithRanges( ct, new CKEDITOR.dom.rangeList( [ range ] ) ) );
},

'test shrink does stop on block boundary start - SHRINK_ELEMENT, no shrink on block boundary': function() {
var ct = doc.getById( 'editable_playground' ),
source = '<p>[para1</p><p>]para2</p>';

var range = bender.tools.setHtmlWithRange( ct, source )[ 0 ];
range.shrink( CKEDITOR.SHRINK_ELEMENT, true, false );
assert.areSame( source, bender.tools.getHtmlWithRanges( ct, new CKEDITOR.dom.rangeList( [ range ] ) ) );
},

'test shrink does not stop on inline boundary end - SHRINK_ELEMENT': function() {
var ct = doc.getById( 'editable_playground' ),
source = '<p><b>text1[</b><i>te]xt2</i></p>',
result = '<p><b>text1</b><i>[te]xt2</i></p>';

var range = bender.tools.setHtmlWithRange( ct, source )[ 0 ];
range.shrink( CKEDITOR.SHRINK_ELEMENT, true );
assert.areSame( result, bender.tools.getHtmlWithRanges( ct, new CKEDITOR.dom.rangeList( [ range ] ) ) );
},

'test shrink does not stop on inline boundary start - SHRINK_ELEMENT': function() {
var ct = doc.getById( 'editable_playground' ),
source = '<p><b>te[xt1</b><i>]text2</i></p>',
result = '<p><b>te[xt1]</b><i>text2</i></p>';

var range = bender.tools.setHtmlWithRange( ct, source )[ 0 ];
range.shrink( CKEDITOR.SHRINK_ELEMENT, true );
assert.areSame( result, bender.tools.getHtmlWithRanges( ct, new CKEDITOR.dom.rangeList( [ range ] ) ) );
},

'test shrink does stop on text - SHRINK_ELEMENT': function() {
var ct = doc.getById( 'editable_playground' ),
source = '<p><b>text1[</b>x<i>]text2</i></p>';

var range = bender.tools.setHtmlWithRange( ct, source )[ 0 ];
range.shrink( CKEDITOR.SHRINK_ELEMENT, true );
assert.areSame( source, bender.tools.getHtmlWithRanges( ct, new CKEDITOR.dom.rangeList( [ range ] ) ) );
},

'test shrink should stop on a non-editable block - SHRINK_ELEMENT': function() {
var ct = doc.getById( 'editable_playground' ),
source = '[<p contenteditable="false">x</p>]';
Expand Down

0 comments on commit 5ea2a38

Please sign in to comment.