diff --git a/src/clipboard.js b/src/clipboard.js
index 8ee98c6..8b4c887 100644
--- a/src/clipboard.js
+++ b/src/clipboard.js
@@ -13,6 +13,7 @@ import ClipboardObserver from './clipboardobserver';
import plainTextToHtml from './utils/plaintexttohtml';
import normalizeClipboardHtml from './utils/normalizeclipboarddata';
+import viewToPlainText from './utils/viewtoplaintext.js';
import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
@@ -179,6 +180,7 @@ export default class Clipboard extends Plugin {
this.listenTo( editingView, 'clipboardOutput', ( evt, data ) => {
if ( !data.content.isEmpty ) {
data.dataTransfer.setData( 'text/html', this._htmlDataProcessor.toData( data.content ) );
+ data.dataTransfer.setData( 'text/plain', viewToPlainText( data.content ) );
}
if ( data.method == 'cut' ) {
diff --git a/src/utils/viewtoplaintext.js b/src/utils/viewtoplaintext.js
new file mode 100644
index 0000000..c162968
--- /dev/null
+++ b/src/utils/viewtoplaintext.js
@@ -0,0 +1,53 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module clipboard/utils/viewtoplaintext
+ */
+
+/**
+ * Deeply converts {@link module:engine/model/view/item view item} to plain text.
+ *
+ * @param {module:engine/model/view/item} viewItem View item to convert.
+ * @returns {String} Plain text representation of `viewItem`.
+ */
+export default function viewToPlainText( viewItem ) {
+ let text = '';
+
+ if ( viewItem.is( 'text' ) || viewItem.is( 'textProxy' ) ) {
+ // If item is `Text` or `TextProxy` simple take its text data.
+ text = viewItem.data;
+ } else if ( viewItem.is( 'img' ) && viewItem.hasAttribute( 'alt' ) ) {
+ // Special case for images - use alt attribute if it is provided.
+ text = viewItem.getAttribute( 'alt' );
+ } else {
+ // Other elements are document fragments, attribute elements or container elements.
+ // They don't have their own text value, so convert their children.
+ let prev = null;
+
+ for ( let child of viewItem.getChildren() ) {
+ const childText = viewToPlainText( child );
+
+ // Separate container element children with one or more new-line characters.
+ if ( prev && ( prev.is( 'containerElement' ) || child.is( 'containerElement' ) ) ) {
+ if ( smallPaddingElements.includes( prev.name ) || smallPaddingElements.includes( child.name ) ) {
+ text += '\n';
+ } else {
+ text += '\n\n';
+ }
+ }
+
+ text += childText;
+ prev = child;
+ }
+ }
+
+ return text;
+}
+
+// Elements which should not have empty-line padding.
+// Most `view.ContainerElement` want to be separate by new-line, but some are creating one structure
+// together (like `
`) so it is better to separate them by only one "\n".
+const smallPaddingElements = [ 'figcaption', 'li' ];
diff --git a/tests/clipboard.js b/tests/clipboard.js
index be41355..53006ff 100644
--- a/tests/clipboard.js
+++ b/tests/clipboard.js
@@ -9,7 +9,10 @@ import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import ClipboardObserver from '../src/clipboardobserver';
-import { stringify as stringifyView } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
+import {
+ stringify as stringifyView,
+ parse as parseView
+} from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
import {
stringify as stringifyModel,
setData as setModelData,
@@ -287,16 +290,98 @@ describe( 'Clipboard feature', () => {
it( 'sets clipboard HTML data', () => {
const dataTransferMock = createDataTransfer();
- setModelData( editor.document, 'f[o]o' );
+ const input =
+ '