Piotrek Koszuliński il y a 9 ans
Parent
commit
a2dff35c85

+ 2 - 3
packages/ckeditor5-clipboard/src/clipboard.js

@@ -95,9 +95,9 @@ export default class Clipboard extends Feature {
 
 			content = this._htmlDataProcessor.toView( content );
 
-			editingView.fire( 'clipboardInput', { dataTransfer, content } );
-
 			data.preventDefault();
+
+			editingView.fire( 'clipboardInput', { dataTransfer, content } );
 		}, { priority: 'low' } );
 
 		this.listenTo( editingView, 'clipboardInput', ( evt, data ) => {
@@ -127,7 +127,6 @@ export default class Clipboard extends Feature {
  * The value of the {@link engine.view.Document#clipboardInput} event.
  *
  * @class engine.view.observer.ClipboardInputEventData
- * @extends engine.view.observer.DomEventData
  */
 
 /**

+ 1 - 1
packages/ckeditor5-clipboard/src/clipboardobserver.js

@@ -19,7 +19,7 @@ export default class ClipboardObserver extends DomEventObserver {
 	constructor( doc ) {
 		super( doc );
 
-		this.domEventType = [ 'paste' ];
+		this.domEventType = 'paste';
 	}
 
 	onDomEvent( domEvent ) {

+ 1 - 0
packages/ckeditor5-clipboard/src/datatransfer.js

@@ -25,6 +25,7 @@ export default class DataTransfer {
 	 *		dataTransfer.getData( 'text/plain' );
 	 *
 	 * @param {String} type The mime type. E.g. `text/html` or `text/plain`.
+	 * @returns {String}
 	 */
 	getData( type ) {
 		return this._native.getData( type );

+ 3 - 3
packages/ckeditor5-clipboard/src/utils/plaintexttohtml.js

@@ -17,12 +17,12 @@ export default function plainTextToHtml( text ) {
 		.replace( />/g, '>' )
 		// Creates paragraphs for double line breaks and change single line breaks to spaces.
 		// In the future single line breaks may be converted into <br>s.
-		.replace( /\n\n/, '</p><p>' )
-		.replace( /\n/, ' ' )
+		.replace( /\n\n/g, '</p><p>' )
+		.replace( /\n/g, ' ' )
 		// Preserve trailing spaces (only the first and last one – the rest is handled below).
 		.replace( /^\s/, '&nbsp;' )
 		.replace( /\s$/, '&nbsp;' )
-		// Presever other subsequent spaces now.
+		// Preserve other subsequent spaces now.
 		.replace( /\s\s/g, ' &nbsp;' );
 
 	if ( text.indexOf( '</p><p>' ) > -1 ) {

+ 162 - 0
packages/ckeditor5-clipboard/tests/clipboard.js

@@ -0,0 +1,162 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import VirtualTestEditor from '/tests/core/_utils/virtualtesteditor.js';
+import Clipboard from '/ckeditor5/clipboard/clipboard.js';
+import ClipboardObserver from '/ckeditor5/clipboard/clipboardobserver.js';
+import { stringify as stringifyView } from '/ckeditor5/engine/dev-utils/view.js';
+import ViewDocumentFragment from '/ckeditor5/engine/view/documentfragment.js';
+
+describe( 'Clipboard feature', () => {
+	let editor, editingView;
+
+	beforeEach( () => {
+		return VirtualTestEditor.create( {
+				features: [ Clipboard ]
+			} )
+			.then( ( newEditor ) => {
+				editor = newEditor;
+				editingView = editor.editing.view;
+			} );
+	} );
+
+	describe( 'constructor', () => {
+		it( 'registers ClipboardObserver', () => {
+			expect( editingView.getObserver( ClipboardObserver ) ).to.be.instanceOf( ClipboardObserver );
+		} );
+	} );
+
+	describe( 'clipboard pipeline', () => {
+		it( 'takes HTML data from the dataTransfer', ( done ) => {
+			const dataTransferMock = createDataTransfer( { 'text/html': '<p>x</p>', 'text/plain': 'y' } );
+			const preventDefaultSpy = sinon.spy();
+
+			editingView.on( 'clipboardInput', ( evt, data ) => {
+				expect( preventDefaultSpy.calledOnce ).to.be.true;
+
+				expect( data.dataTransfer ).to.equal( dataTransferMock );
+
+				expect( data.content ).is.instanceOf( ViewDocumentFragment );
+				expect( stringifyView( data.content ) ).to.equal( '<p>x</p>' );
+
+				done();
+			} );
+
+			editingView.fire( 'paste', {
+				dataTransfer: dataTransferMock,
+				preventDefault: preventDefaultSpy
+			} );
+		} );
+
+		it( 'takes plain text data from the dataTransfer if there is no HTML', ( done ) => {
+			const dataTransferMock = createDataTransfer( { 'text/plain': 'x\n\ny  z' } );
+			const preventDefaultSpy = sinon.spy();
+
+			editingView.on( 'clipboardInput', ( evt, data ) => {
+				expect( preventDefaultSpy.calledOnce ).to.be.true;
+
+				expect( data.dataTransfer ).to.equal( dataTransferMock );
+
+				expect( data.content ).is.instanceOf( ViewDocumentFragment );
+				expect( stringifyView( data.content ) ).to.equal( '<p>x</p><p>y  z</p>' );
+
+				done();
+			} );
+
+			editingView.fire( 'paste', {
+				dataTransfer: dataTransferMock,
+				preventDefault: preventDefaultSpy
+			} );
+		} );
+
+		it( 'fires clipboardInput event with empty data if there is no HTML nor plain text', ( done ) => {
+			const dataTransferMock = createDataTransfer( {} );
+			const preventDefaultSpy = sinon.spy();
+
+			editingView.on( 'clipboardInput', ( evt, data ) => {
+				expect( preventDefaultSpy.calledOnce ).to.be.true;
+
+				expect( data.dataTransfer ).to.equal( dataTransferMock );
+
+				expect( data.content ).is.instanceOf( ViewDocumentFragment );
+				expect( stringifyView( data.content ) ).to.equal( '' );
+
+				done();
+			} );
+
+			editingView.fire( 'paste', {
+				dataTransfer: dataTransferMock,
+				preventDefault: preventDefaultSpy
+			} );
+		} );
+
+		it( 'uses low priority observer for the paste event', () => {
+			const dataTransferMock = createDataTransfer( { 'text/html': 'x' } );
+			const spy = sinon.spy();
+
+			editingView.on( 'paste', ( evt ) => {
+				evt.stop();
+			} );
+
+			editingView.on( 'clipboardInput', spy );
+
+			editingView.fire( 'paste', {
+				dataTransfer: dataTransferMock,
+				preventDefault() {}
+			} );
+
+			expect( spy.callCount ).to.equal( 0 );
+		} );
+
+		it( 'inserts content to the editor', () => {
+			const dataTransferMock = createDataTransfer( { 'text/html': '<p>x</p>', 'text/plain': 'y' } );
+			const spy = sinon.stub( editor.data, 'insert' );
+
+			editingView.fire( 'paste', {
+				dataTransfer: dataTransferMock,
+				preventDefault() {}
+			} );
+
+			expect( spy.calledOnce ).to.be.true;
+			expect( stringifyView( spy.args[ 0 ][ 0 ] ) ).to.equal( '<p>x</p>' );
+		} );
+
+		it( 'does nothing when pasted content is empty', () => {
+			const dataTransferMock = createDataTransfer( { 'text/plain': '' } );
+			const spy = sinon.stub( editor.data, 'insert' );
+
+			editingView.fire( 'clipboardInput', {
+				dataTransfer: dataTransferMock,
+				content: new ViewDocumentFragment()
+			} );
+
+			expect( spy.callCount ).to.equal( 0 );
+		} );
+
+		it( 'uses low priority observer for the clipboardInput event', () => {
+			const dataTransferMock = createDataTransfer( { 'text/html': 'x' } );
+			const spy = sinon.stub( editor.data, 'insert' );
+
+			editingView.on( 'clipboardInput', ( evt ) => {
+				evt.stop();
+			} );
+
+			editingView.fire( 'paste', {
+				dataTransfer: dataTransferMock,
+				preventDefault() {}
+			} );
+
+			expect( spy.callCount ).to.equal( 0 );
+		} );
+	} );
+} );
+
+function createDataTransfer( data ) {
+	return {
+		getData( type ) {
+			return data[ type ];
+		}
+	};
+}

+ 45 - 0
packages/ckeditor5-clipboard/tests/clipboardobserver.js

@@ -0,0 +1,45 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals document */
+
+import ClipboardObserver from '/ckeditor5/clipboard/clipboardobserver.js';
+import ViewDocument from '/ckeditor5/engine/view/document.js';
+import DataTransfer from '/ckeditor5/clipboard/datatransfer.js';
+
+describe( 'ClipboardObserver', () => {
+	let viewDocument, observer;
+
+	beforeEach( () => {
+		viewDocument = new ViewDocument();
+		observer = viewDocument.addObserver( ClipboardObserver );
+	} );
+
+	it( 'should define domEventType', () => {
+		expect( observer.domEventType ).to.equal( 'paste' );
+	} );
+
+	describe( 'onDomEvent', () => {
+		it( 'should fire paste with the right event data', () => {
+			const spy = sinon.spy();
+			const dataTransfer = {
+				getData( type ) {
+					return 'foo:' + type;
+				}
+			};
+
+			viewDocument.on( 'paste', spy );
+
+			observer.onDomEvent( { type: 'paste', target: document.body, clipboardData: dataTransfer } );
+
+			expect( spy.calledOnce ).to.be.true;
+
+			const data = spy.args[ 0 ][ 1 ];
+			expect( data.domTarget ).to.equal( document.body );
+			expect( data.dataTransfer ).to.be.instanceOf( DataTransfer );
+			expect( data.dataTransfer.getData( 'x/y' ) ).to.equal( 'foo:x/y' );
+		} );
+	} );
+} );

+ 18 - 0
packages/ckeditor5-clipboard/tests/datatransfer.js

@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import DataTransfer from '/ckeditor5/clipboard/datatransfer.js';
+
+describe( 'DataTransfer', () => {
+	it( 'should return data from the native data transfer', () => {
+		const dt = new DataTransfer( {
+			getData( type ) {
+				return 'foo:' + type;
+			}
+		} );
+
+		expect( dt.getData( 'x/y' ) ).to.equal( 'foo:x/y' );
+	} );
+} );

+ 1 - 1
packages/ckeditor5-clipboard/tests/manual/pasting.md

@@ -7,4 +7,4 @@ Simply test pasting various content to the editor.
 Check:
 
 1. Copy & paste within the editor (nothing should be filtered out, nothing should be lost).
-2. Pasting from some websites (editor should not blow up).
+2. ~~Pasting from some websites (editor should not blow up).~~ (not yet stable)

+ 20 - 0
packages/ckeditor5-clipboard/tests/utils/normalizeclipboarddata.js

@@ -0,0 +1,20 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import normalizeClipboardData from '/ckeditor5/clipboard/utils/normalizeclipboarddata.js';
+
+describe( 'normalizeClipboardData', () => {
+	it( 'should strip all span.Apple-converted-space', () => {
+		expect(
+			normalizeClipboardData( '<span class="Apple-converted-space"> \t\n</span>x<span class="Apple-converted-space">\u00a0\u00a0</span>' )
+		).to.equal( ' \t\nx\u00a0\u00a0' );
+	} );
+
+	it( 'should replace span.Apple-converted-space of length one with a normal space', () => {
+		expect(
+			normalizeClipboardData( '<span class="Apple-converted-space"> </span>x<span class="Apple-converted-space">\u00a0</span>' )
+		).to.equal( ' x ' );
+	} );
+} );

+ 28 - 0
packages/ckeditor5-clipboard/tests/utils/plaintexttohtml.js

@@ -0,0 +1,28 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import plainTextToHtml from '/ckeditor5/clipboard/utils/plaintexttohtml.js';
+
+describe( 'plainTextToHtml', () => {
+	it( 'encodes < and >', () => {
+		expect( plainTextToHtml( 'x y <z>' ) ).to.equal( 'x y &lt;z&gt;' );
+	} );
+
+	it( 'turns double line breaks into paragraphs', () => {
+		expect( plainTextToHtml( 'x\n\ny\n\nz' ) ).to.equal( '<p>x</p><p>y</p><p>z</p>' );
+	} );
+
+	it( 'preserves trailing spaces', () => {
+		expect( plainTextToHtml( ' x ' ) ).to.equal( '&nbsp;x&nbsp;' );
+	} );
+
+	it( 'preserve subsequent spaces', () => {
+		expect( plainTextToHtml( 'x  y  ' ) ).to.equal( 'x &nbsp;y &nbsp;' );
+	} );
+
+	it( 'turns single line breaks to spaces', () => {
+		expect( plainTextToHtml( 'x\ny\nz' ) ).to.equal( 'x y z' );
+	} );
+} );