diff --git a/core/editor.js b/core/editor.js index 1bf5fdf39ca..4ffc0f9a0b9 100644 --- a/core/editor.js +++ b/core/editor.js @@ -1109,13 +1109,14 @@ * @returns {CKEDITOR.dom.documentFragment/String} */ getSelectedHtml: function( toString ) { - var editable = this.editable(); + var editable = this.editable(), + ranges = this.getSelection().getRanges(); - if ( !editable ) { + if ( !editable || ranges.length === 0 ) { return null; } - var docFragment = editable.getHtmlFromRange( this.getSelection().getRanges()[ 0 ] ); + var docFragment = editable.getHtmlFromRange( ranges[ 0 ] ); return toString ? docFragment.getHtml() : docFragment; }, @@ -1135,13 +1136,14 @@ * @returns {CKEDITOR.dom.documentFragment/String} */ extractSelectedHtml: function( toString ) { - var editable = this.editable(); + var editable = this.editable(), + ranges = this.getSelection().getRanges(); - if ( !editable ) { + if ( !editable || ranges.length === 0 ) { return null; } - var range = this.getSelection().getRanges()[ 0 ], + var range = ranges[ 0 ], docFragment = editable.extractHtmlFromRange( range ); this.getSelection().selectRanges( [ range ] ); diff --git a/core/selection.js b/core/selection.js index f5975b1f60a..ca25310338a 100644 --- a/core/selection.js +++ b/core/selection.js @@ -1597,38 +1597,6 @@ return ( cache.selectedText = text ); }, - /** - * Retrieves the HTML contained within the range. If selection - * contains multiple ranges method will return concatenation of HTMLs from ranges. - * - * var text = editor.getSelection().getSelectedText(); - * alert( text ); - * - * @since 4.4 - * @returns {String} A string of HTML within the current selection. - */ - getSelectedHtml: function() { - var nativeSel = this.getNative(); - - if ( this.isFake ) { - return this.getSelectedElement().getHtml(); - } - - if ( nativeSel && nativeSel.createRange ) - return nativeSel.createRange().htmlText; - - if ( nativeSel.rangeCount > 0 ) { - var div = document.createElement( 'div' ); - - for ( var i = 0; i < nativeSel.rangeCount; i++ ) { - div.appendChild( nativeSel.getRangeAt( i ).cloneContents() ); - } - return div.innerHTML; - } - - return ''; - }, - /** * Locks the selection made in the editor in order to make it possible to * manipulate it without browser interference. A locked selection is diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index ad67b07f13b..d830cb641d0 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -499,7 +499,7 @@ // Delete content with the low priority so one can overwrite cut data. editable.on( 'cut', function() { - editor.getSelection().getRanges()[ 0 ].deleteContents(); // @todo replace with the new delete content function + editor.extractSelectedHtml(); }, null, null, 999 ); } @@ -1421,7 +1421,7 @@ // No we can safely delete content for the drag range... dragRange = editor.createRange(); dragRange.moveToBookmark( dragBookmark ); - dragRange.deleteContents(); // @todo replace with the new delete content function + editable.extractHtmlFromRange( dragRange ); // ...and paste content into the drop position. dropRange = editor.createRange(); @@ -1445,7 +1445,7 @@ // Remove dragged content and make a snapshot. dataTransfer.sourceEditor.fire( 'saveSnapshot' ); - dragRange.deleteContents(); // @todo replace with the new delete content function + editable.extractHtmlFromRange( dragRange ); dataTransfer.sourceEditor.getSelection().reset(); dataTransfer.sourceEditor.fire( 'saveSnapshot' ); @@ -1944,8 +1944,7 @@ if ( editor ) { this.sourceEditor = editor; - // @todo replace with the new function - this.setData( 'text/html', editor.getSelection().getSelectedHtml() ); + this.setData( 'text/html', editor.getSelectedHtml( 1 ) ); // Without setData( 'text', ... ) on dragstart there is no drop event in Safari. // Also 'text' data is empty as drop to the textarea does not work if we do not put there text. diff --git a/tests/core/editor/getextractselectedhtml.js b/tests/core/editor/getextractselectedhtml.js index 58b0ca95851..d6fe8c07646 100644 --- a/tests/core/editor/getextractselectedhtml.js +++ b/tests/core/editor/getextractselectedhtml.js @@ -9,7 +9,17 @@ bender.editor = { } }; +var stubs = []; + bender.test( { + 'tearDown': function() { + var stub; + + while ( stub = stubs.pop() ) { + stub.restore(); + } + }, + 'test getSelectedHtml': function() { var editor = this.editor; bender.tools.selection.setWithHtml( editor, '

fo{ob}ar

' ); @@ -27,6 +37,21 @@ bender.test( { assert.areSame( 'ob', editor.getSelectedHtml( true ) ); }, + 'test getSelectedHtml with no ranges': function() { + // Only on Firefox it may happens that selection has no ranges. + if ( !CKEDITOR.env.gecko ) { + assert.ignore(); + } + + sinon.stub( CKEDITOR.dom.selection.prototype, 'getRanges' ).returns( [] ); + stubs.push( CKEDITOR.dom.selection.prototype.getRanges ); + + var editor = this.editor, + selectedHtml = editor.getSelectedHtml(); + + assert.isNull( selectedHtml, 'There should be no error but null should be returns if selection contains no ranges' ); + }, + 'test extractSelectedHtml': function() { var editor = this.editor; bender.tools.selection.setWithHtml( editor, '

fo{ob}ar

' ); @@ -54,5 +79,15 @@ bender.test( { assert.areSame( 'ob', editor.extractSelectedHtml( true ) ); assert.isInnerHtmlMatching( '

fo^ar@

', bender.tools.selection.getWithHtml( editor ), { compareSelection: true, normalizeSelection: true }, 'contents of the editor' ); + }, + + 'test extractSelectedHtml with no ranges': function() { + sinon.stub( CKEDITOR.dom.selection.prototype, 'getRanges' ).returns( [] ); + stubs.push( CKEDITOR.dom.selection.prototype.getRanges ); + + var editor = this.editor, + selectedHtml = editor.getSelectedHtml(); + + assert.isNull( selectedHtml, 'There should be no error but null should be returns if selection contains no ranges' ); } } ); \ No newline at end of file diff --git a/tests/plugins/clipboard/drop.js b/tests/plugins/clipboard/drop.js index b5f68cedb38..83252eb8142 100644 --- a/tests/plugins/clipboard/drop.js +++ b/tests/plugins/clipboard/drop.js @@ -344,6 +344,32 @@ var testsForMultipleEditor = { } ); }, + // Integration test (#12806). + 'test drop part of the link': function( editor ) { + var bot = bender.editorBots[ editor.name ], + evt = bender.tools.mockDropEvent(); + + bot.setHtmlWithSelection( '

Lorem [ipsum] dolor sit amet.

' ); + editor.resetUndo(); + + drag( editor, evt ); + + drop( editor, evt, { + element: editor.document.getById( 'p' ).getChild( 1 ), + offset: 4, + expectedTransferType: CKEDITOR.DATA_TRANSFER_INTERNAL, + expectedText: 'ipsum', + expectedHtml: 'ipsum', + expectedDataType: 'html', + expectedDataValue: 'ipsum' + }, null, function() { + assert.isInnerHtmlMatching( + '

Lorem dolor sit' + + 'ipsum' + ( ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) ? '^' : '^' ) + ' amet.@

', + getWithHtml( editor ), htmlMatchOpts, 'after drop' ); + } ); + }, + 'test drop text from external source': function( editor ) { var bot = bender.editorBots[ editor.name ], evt = bender.tools.mockDropEvent(); @@ -479,9 +505,9 @@ var testsForMultipleEditor = { offset: 0, expectedTransferType: CKEDITOR.DATA_TRANSFER_INTERNAL, expectedText: 'drag1', - expectedHtml: 'drag1', + expectedHtml: 'drag1', expectedDataType: 'html', - expectedDataValue: 'drag1' + expectedDataValue: 'drag1' }, function( evt ) { if ( !( CKEDITOR.env.ie && CKEDITOR.env.version == 8 ) && !CKEDITOR.env.safari ) { assert.areSame( editor.document.getById( 'drag1' ), evt.data.dragRange.startContainer, 'dropRange.startContainer' ); @@ -492,7 +518,7 @@ var testsForMultipleEditor = { evt.data.dropRange.setStart( editor.document.getById( 'drop2' ), 4 ); evt.data.dropRange.collapse( true ); }, function() { - assert.areSame( '

xxdrag1xxxdrop1xdrop2drag1^x

', bender.tools.getHtmlWithSelection( editor ), 'after drop' ); + assert.areSame( '

xxdrag1xxxdrop1xdrop2drag1^x

', bender.tools.getHtmlWithSelection( editor ), 'after drop' ); editor.execCommand( 'undo' );