From 82032abdf5639892bfac21c34883177d4c64a326 Mon Sep 17 00:00:00 2001 From: Piotr Jasiun Date: Mon, 20 Mar 2017 11:06:47 +0100 Subject: [PATCH 01/16] Added getFiles and getTypes to the data transfer object. --- src/datatransfer.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/datatransfer.js b/src/datatransfer.js index f556a48..019796a 100644 --- a/src/datatransfer.js +++ b/src/datatransfer.js @@ -42,4 +42,26 @@ export default class DataTransfer { setData( type, data ) { this._native.setData( type, data ); } + + *getFiles() { + // DataTransfer.files and items are Array-like and might not have an iterable interface. + const files = this._native.files ? Array.from( this._native.files ) : []; + const items = this._native.items ? Array.from( this._native.items ) : []; + + if ( files.length ) { + yield* files; + } + // // Chrome have empty DataTransfer.files, but let get files through the items interface. + else { + for ( const item of items ) { + if ( item.kind == 'file' ) { + yield item.getAsFile(); + } + } + } + } + + getTypes() { + return this._native.types; + } } From 658c7a75de246b57a98915e025adcec40c968cfe Mon Sep 17 00:00:00 2001 From: Piotr Jasiun Date: Mon, 20 Mar 2017 11:07:13 +0100 Subject: [PATCH 02/16] Added drop basic handling. --- src/clipboardobserver.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clipboardobserver.js b/src/clipboardobserver.js index cf9d63d..91b312c 100644 --- a/src/clipboardobserver.js +++ b/src/clipboardobserver.js @@ -22,12 +22,12 @@ export default class ClipboardObserver extends DomEventObserver { constructor( doc ) { super( doc ); - this.domEventType = [ 'paste', 'copy', 'cut' ]; + this.domEventType = [ 'paste', 'copy', 'cut', 'drop' ]; } onDomEvent( domEvent ) { this.fire( domEvent.type, domEvent, { - dataTransfer: new DataTransfer( domEvent.clipboardData ) + dataTransfer: new DataTransfer( domEvent.clipboardData ? domEvent.clipboardData : domEvent.dataTransfer ) } ); } } From 8d848cc043cd874933c9b853f26734b7e4070192 Mon Sep 17 00:00:00 2001 From: Piotr Jasiun Date: Tue, 4 Apr 2017 12:27:44 +0200 Subject: [PATCH 03/16] Split `clipboardInput` event into `input` and `clipboardInput`. --- src/clipboard.js | 8 +++----- src/clipboardobserver.js | 10 ++++++++++ src/datatransfer.js | 6 ++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index d8d2241..7389ec5 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -111,7 +111,7 @@ export default class Clipboard extends Plugin { // The clipboard paste pipeline. - this.listenTo( editingView, 'paste', ( evt, data ) => { + this.listenTo( editingView, 'input', ( evt, data ) => { const dataTransfer = data.dataTransfer; let content = ''; @@ -123,12 +123,10 @@ export default class Clipboard extends Plugin { content = this._htmlDataProcessor.toView( content ); - data.preventDefault(); - - editingView.fire( 'clipboardInput', { dataTransfer, content } ); + editingView.fire( 'clipboardInputTransformation', { content } ); }, { priority: 'low' } ); - this.listenTo( editingView, 'clipboardInput', ( evt, data ) => { + this.listenTo( editingView, 'clipboardInputTransformation', ( evt, data ) => { if ( !data.content.isEmpty ) { const dataController = this.editor.data; diff --git a/src/clipboardobserver.js b/src/clipboardobserver.js index 91b312c..2d97662 100644 --- a/src/clipboardobserver.js +++ b/src/clipboardobserver.js @@ -23,6 +23,16 @@ export default class ClipboardObserver extends DomEventObserver { super( doc ); this.domEventType = [ 'paste', 'copy', 'cut', 'drop' ]; + + this.document.on( 'paste', _handleInput, { priority: 'low' } ); + this.document.on( 'drop', _handleInput, { priority: 'low' } ); + + function _handleInput( evt, data ) { + // Prevent page refreshing. + data.preventDefault(); + + this.document.fire( 'input', { data: data.dataTransfer } ); + } } onDomEvent( domEvent ) { diff --git a/src/datatransfer.js b/src/datatransfer.js index 019796a..0d724f1 100644 --- a/src/datatransfer.js +++ b/src/datatransfer.js @@ -19,6 +19,8 @@ export default class DataTransfer { * @member {DataTransfer} #_native */ this._native = nativeDataTransfer; + + this.files = Array.from( this.getFiles() ); } /** @@ -43,7 +45,7 @@ export default class DataTransfer { this._native.setData( type, data ); } - *getFiles() { + *_getFiles() { // DataTransfer.files and items are Array-like and might not have an iterable interface. const files = this._native.files ? Array.from( this._native.files ) : []; const items = this._native.items ? Array.from( this._native.items ) : []; @@ -61,7 +63,7 @@ export default class DataTransfer { } } - getTypes() { + get types() { return this._native.types; } } From f583d0ce17311fb8c408dfa17caa9e2bc57e297c Mon Sep 17 00:00:00 2001 From: Piotr Jasiun Date: Tue, 4 Apr 2017 15:51:02 +0200 Subject: [PATCH 04/16] Fire inputTransformation on clipboard plugin. Fix bugs. --- src/clipboard.js | 10 ++++++++-- src/clipboardobserver.js | 6 +++--- src/datatransfer.js | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index 7389ec5..73e8395 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -91,6 +91,10 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * @extends module:core.plugin~Plugin */ export default class Clipboard extends Plugin { + static get pluginName() { + return 'clipboard'; + } + /** * @inheritDoc */ @@ -123,10 +127,10 @@ export default class Clipboard extends Plugin { content = this._htmlDataProcessor.toView( content ); - editingView.fire( 'clipboardInputTransformation', { content } ); + this.fire( 'inputTransformation', { content } ); }, { priority: 'low' } ); - this.listenTo( editingView, 'clipboardInputTransformation', ( evt, data ) => { + this.listenTo( this, 'inputTransformation', ( evt, data ) => { if ( !data.content.isEmpty ) { const dataController = this.editor.data; @@ -167,6 +171,8 @@ export default class Clipboard extends Plugin { } }, { priority: 'low' } ); } + + } /** diff --git a/src/clipboardobserver.js b/src/clipboardobserver.js index 2d97662..877dab9 100644 --- a/src/clipboardobserver.js +++ b/src/clipboardobserver.js @@ -24,14 +24,14 @@ export default class ClipboardObserver extends DomEventObserver { this.domEventType = [ 'paste', 'copy', 'cut', 'drop' ]; - this.document.on( 'paste', _handleInput, { priority: 'low' } ); - this.document.on( 'drop', _handleInput, { priority: 'low' } ); + doc.on( 'paste', _handleInput, { priority: 'low' } ); + doc.on( 'drop', _handleInput, { priority: 'low' } ); function _handleInput( evt, data ) { // Prevent page refreshing. data.preventDefault(); - this.document.fire( 'input', { data: data.dataTransfer } ); + doc.fire( 'input', { dataTransfer: data.dataTransfer } ); } } diff --git a/src/datatransfer.js b/src/datatransfer.js index 0d724f1..e32c4ba 100644 --- a/src/datatransfer.js +++ b/src/datatransfer.js @@ -20,7 +20,7 @@ export default class DataTransfer { */ this._native = nativeDataTransfer; - this.files = Array.from( this.getFiles() ); + this.files = Array.from( this._getFiles() ); } /** From 960b8e96060a681511a08081716dfa157db9b2f9 Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Thu, 6 Apr 2017 16:25:28 +0200 Subject: [PATCH 05/16] Aligned tests to the new API. --- src/clipboard.js | 4 +- tests/clipboard.js | 106 ++++++++++++++++++++++++++++++--------------- 2 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index 73e8395..7825924 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -135,7 +135,7 @@ export default class Clipboard extends Plugin { const dataController = this.editor.data; // Convert the pasted content to a model document fragment. - // Convertion is contextual, but in this case we need an "all allowed" context and for that + // Conversion is contextual, but in this case we need an "all allowed" context and for that // we use the $clipboardHolder item. const modelFragment = dataController.toModel( data.content, '$clipboardHolder' ); @@ -171,8 +171,6 @@ export default class Clipboard extends Plugin { } }, { priority: 'low' } ); } - - } /** diff --git a/tests/clipboard.js b/tests/clipboard.js index 4280cb7..ceecd1b 100644 --- a/tests/clipboard.js +++ b/tests/clipboard.js @@ -20,7 +20,7 @@ import ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfr import ViewText from '@ckeditor/ckeditor5-engine/src/view/text'; describe( 'Clipboard feature', () => { - let editor, editingView; + let editor, editingView, clipboardPlugin; beforeEach( () => { return VirtualTestEditor.create( { @@ -29,6 +29,7 @@ describe( 'Clipboard feature', () => { .then( ( newEditor ) => { editor = newEditor; editingView = editor.editing.view; + clipboardPlugin = editor.plugins.get( 'clipboard' ); } ); } ); @@ -39,60 +40,97 @@ describe( 'Clipboard feature', () => { } ); describe( 'clipboard paste pipeline', () => { - it( 'takes HTML data from the dataTransfer', ( done ) => { - const dataTransferMock = createDataTransfer( { 'text/html': '

x

', 'text/plain': 'y' } ); - const preventDefaultSpy = sinon.spy(); - - editingView.on( 'clipboardInput', ( evt, data ) => { - expect( preventDefaultSpy.calledOnce ).to.be.true; + describe( 'takes HTML data from the dataTransfer', () => { + it( 'and fires the input event on the editingView', ( done ) => { + const dataTransferMock = createDataTransfer( { 'text/html': '

x

', 'text/plain': 'y' } ); + const preventDefaultSpy = sinon.spy(); + + editingView.on( 'input', ( evt, data ) => { + expect( preventDefaultSpy.calledOnce ).to.be.true; + expect( data.dataTransfer ).to.equal( dataTransferMock ); + + done(); + } ); + + editingView.fire( 'paste', { + dataTransfer: dataTransferMock, + preventDefault: preventDefaultSpy + } ); + } ); - expect( data.dataTransfer ).to.equal( dataTransferMock ); + it( 'and fires the inputTransformation event on the clipboardPlugin', ( done ) => { + const dataTransferMock = createDataTransfer( { 'text/html': '

x

', 'text/plain': 'y' } ); + const preventDefaultSpy = sinon.spy(); - expect( data.content ).is.instanceOf( ViewDocumentFragment ); - expect( stringifyView( data.content ) ).to.equal( '

x

' ); + clipboardPlugin.on( 'inputTransformation', ( evt, data ) => { + expect( data.content ).is.instanceOf( ViewDocumentFragment ); + expect( stringifyView( data.content ) ).to.equal( '

x

' ); - done(); - } ); + done(); + } ); - editingView.fire( 'paste', { - dataTransfer: dataTransferMock, - preventDefault: preventDefaultSpy + 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(); + describe( 'takes plain text data from the dataTransfer if there is no HTML', () => { + it( 'and fires the input event on the editingView', ( 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; + editingView.on( 'input', ( evt, data ) => { + expect( preventDefaultSpy.calledOnce ).to.be.true; + expect( data.dataTransfer ).to.equal( dataTransferMock ); - expect( data.dataTransfer ).to.equal( dataTransferMock ); - - expect( data.content ).is.instanceOf( ViewDocumentFragment ); - expect( stringifyView( data.content ) ).to.equal( '

x

y z

' ); + done(); + } ); - done(); + editingView.fire( 'paste', { + dataTransfer: dataTransferMock, + preventDefault: preventDefaultSpy + } ); } ); - editingView.fire( 'paste', { - dataTransfer: dataTransferMock, - preventDefault: preventDefaultSpy + it( 'and fires the inputTransformation event on the clipboardPlugin', ( done ) => { + const dataTransferMock = createDataTransfer( { 'text/plain': 'x\n\ny z' } ); + const preventDefaultSpy = sinon.spy(); + + clipboardPlugin.on( 'inputTransformation', ( evt, data ) => { + expect( data.content ).is.instanceOf( ViewDocumentFragment ); + expect( stringifyView( data.content ) ).to.equal( '

x

y z

' ); + + done(); + } ); + + editingView.fire( 'paste', { + dataTransfer: dataTransferMock, + preventDefault: preventDefaultSpy + } ); } ); } ); - it( 'fires clipboardInput event with empty data if there is no HTML nor plain text', ( done ) => { + it( 'fires events with empty data if there is no HTML nor plain text', ( done ) => { const dataTransferMock = createDataTransfer( {} ); const preventDefaultSpy = sinon.spy(); + const editorViewCalled = sinon.spy(); - editingView.on( 'clipboardInput', ( evt, data ) => { + editingView.on( 'input', ( evt, data ) => { expect( preventDefaultSpy.calledOnce ).to.be.true; expect( data.dataTransfer ).to.equal( dataTransferMock ); + editorViewCalled(); + } ); + + clipboardPlugin.on( 'inputTransformation', ( evt, data ) => { expect( data.content ).is.instanceOf( ViewDocumentFragment ); expect( stringifyView( data.content ) ).to.equal( '' ); + expect( editorViewCalled.calledOnce ).to.be.true; + done(); } ); @@ -110,7 +148,7 @@ describe( 'Clipboard feature', () => { evt.stop(); } ); - editingView.on( 'clipboardInput', spy ); + editingView.on( 'input', spy ); editingView.fire( 'paste', { dataTransfer: dataTransferMock, @@ -152,7 +190,7 @@ describe( 'Clipboard feature', () => { const dataTransferMock = createDataTransfer( { 'text/plain': '' } ); const spy = sinon.stub( editor.data, 'insertContent' ); - editingView.fire( 'clipboardInput', { + editingView.fire( 'input', { dataTransfer: dataTransferMock, content: new ViewDocumentFragment() } ); @@ -160,11 +198,11 @@ describe( 'Clipboard feature', () => { expect( spy.callCount ).to.equal( 0 ); } ); - it( 'uses low priority observer for the clipboardInput event', () => { + it( 'uses low priority observer for the input event', () => { const dataTransferMock = createDataTransfer( { 'text/html': 'x' } ); const spy = sinon.stub( editor.data, 'insertContent' ); - editingView.on( 'clipboardInput', ( evt ) => { + editingView.on( 'input', ( evt ) => { evt.stop(); } ); From 403179214b2a91dfc74309a3c7eb5b0c12010a4e Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Thu, 6 Apr 2017 17:18:23 +0200 Subject: [PATCH 06/16] Fixed coverage in datatransfer. --- tests/clipboardobserver.js | 4 ++-- tests/datatransfer.js | 41 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/tests/clipboardobserver.js b/tests/clipboardobserver.js index b1f2151..1d8ac3c 100644 --- a/tests/clipboardobserver.js +++ b/tests/clipboardobserver.js @@ -18,11 +18,11 @@ describe( 'ClipboardObserver', () => { } ); it( 'should define domEventType', () => { - expect( observer.domEventType ).to.deep.equal( [ 'paste', 'copy', 'cut' ] ); + expect( observer.domEventType ).to.deep.equal( [ 'paste', 'copy', 'cut', 'drop' ] ); } ); describe( 'onDomEvent', () => { - it( 'should fire paste with the right event data', () => { + it.skip( 'should fire paste with the right event data', () => { const spy = sinon.spy(); const dataTransfer = { getData( type ) { diff --git a/tests/datatransfer.js b/tests/datatransfer.js index aa58b1a..5301ddc 100644 --- a/tests/datatransfer.js +++ b/tests/datatransfer.js @@ -6,7 +6,34 @@ import DataTransfer from '../src/datatransfer'; describe( 'DataTransfer', () => { - describe( 'getData', () => { + describe( 'constructor', () => { + it( 'should create files from the native files', () => { + const dt = new DataTransfer( { + files: { + 0: 'file1', + 1: 'file2', + length: 2 + } + } ); + + expect( dt.files ).to.deep.equal( [ 'file1', 'file2' ] ); + } ); + + it( 'should create files from the native items', () => { + const dt = new DataTransfer( { + items: { + 0: { kind: 'file', getAsFile: () => 'file1' }, + 1: { kind: 'file', getAsFile: () => 'file2' }, + 2: { kind: 'someOtherKind' }, + length: 3 + }, + files: [] + } ); + + expect( dt.files ).to.deep.equal( [ 'file1', 'file2' ] ); + } ); + } ); + describe( 'getData()', () => { it( 'should return data from the native data transfer', () => { const dt = new DataTransfer( { getData( type ) { @@ -18,7 +45,7 @@ describe( 'DataTransfer', () => { } ); } ); - describe( 'setData', () => { + describe( 'setData()', () => { it( 'should return set data in the native data transfer', () => { const spy = sinon.spy(); const dt = new DataTransfer( { @@ -30,4 +57,14 @@ describe( 'DataTransfer', () => { expect( spy.calledWithExactly( 'text/html', 'bar' ) ).to.be.true; } ); } ); + + describe( 'types', () => { + it( 'should return available types', () => { + const dt = new DataTransfer( { + types: [ 'text/html', 'text/plain' ] + } ); + + expect( dt.types ).to.deep.equal( [ 'text/html', 'text/plain' ] ); + } ); + } ); } ); From 0a96e79f2f749891fd808f5dd9a88af9048c62cd Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Thu, 6 Apr 2017 17:45:06 +0200 Subject: [PATCH 07/16] Fixed code coverage in clipboardobserver. --- tests/clipboardobserver.js | 51 ++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/tests/clipboardobserver.js b/tests/clipboardobserver.js index 1d8ac3c..dcb041a 100644 --- a/tests/clipboardobserver.js +++ b/tests/clipboardobserver.js @@ -22,26 +22,61 @@ describe( 'ClipboardObserver', () => { } ); describe( 'onDomEvent', () => { - it.skip( 'should fire paste with the right event data', () => { - const spy = sinon.spy(); - const dataTransfer = { + let pasteSpy, preventDefaultSpy; + + function getDataTransfer() { + return { getData( type ) { return 'foo:' + type; } }; + } + + beforeEach( () => { + pasteSpy = sinon.spy(); + preventDefaultSpy = sinon.spy(); + } ); - viewDocument.on( 'paste', spy ); + it( 'should fire paste with the right event data - clipboardData', () => { + const dataTransfer = getDataTransfer(); - observer.onDomEvent( { type: 'paste', target: document.body, clipboardData: dataTransfer } ); + viewDocument.on( 'paste', pasteSpy ); - expect( spy.calledOnce ).to.be.true; + observer.onDomEvent( { + type: 'paste', + target: document.body, + clipboardData: dataTransfer, + preventDefault: preventDefaultSpy + } ); - const data = spy.args[ 0 ][ 1 ]; + expect( pasteSpy.calledOnce ).to.be.true; + + const data = pasteSpy.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' ); + expect( preventDefaultSpy.calledOnce ).to.be.true; } ); - // If it fires paste it fires all the other events too. + it( 'should fire paste with the right event data - dataTransfer', () => { + const dataTransfer = getDataTransfer(); + + viewDocument.on( 'drop', pasteSpy ); + + observer.onDomEvent( { + type: 'drop', + target: document.body, + dataTransfer, + preventDefault: preventDefaultSpy + } ); + + expect( pasteSpy.calledOnce ).to.be.true; + + const data = pasteSpy.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' ); + expect( preventDefaultSpy.calledOnce ).to.be.true; + } ); } ); } ); From 2ddf971e4cb14f26608c7566217f58c232b77bbc Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Fri, 7 Apr 2017 11:25:07 +0200 Subject: [PATCH 08/16] Fixed pluginName method. --- src/clipboard.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index b6f1ea2..15385bb 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -91,15 +91,11 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * @extends module:core.plugin~Plugin */ export default class Clipboard extends Plugin { - static get pluginName() { - return 'clipboard'; - } - /** * @inheritDoc */ static get pluginName() { - return 'clipboard/clipboard'; + return 'clipboard'; } /** From 4080c9e782cd0b4a86835f8e0dc01ae6f079eb71 Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Fri, 7 Apr 2017 13:43:25 +0200 Subject: [PATCH 09/16] Fixed clipboard plugin name. --- src/clipboard.js | 2 +- tests/clipboard.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index 15385bb..b2fa4da 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -95,7 +95,7 @@ export default class Clipboard extends Plugin { * @inheritDoc */ static get pluginName() { - return 'clipboard'; + return 'clipboard/clipboard'; } /** diff --git a/tests/clipboard.js b/tests/clipboard.js index ceecd1b..9a5305f 100644 --- a/tests/clipboard.js +++ b/tests/clipboard.js @@ -29,7 +29,7 @@ describe( 'Clipboard feature', () => { .then( ( newEditor ) => { editor = newEditor; editingView = editor.editing.view; - clipboardPlugin = editor.plugins.get( 'clipboard' ); + clipboardPlugin = editor.plugins.get( 'clipboard/clipboard' ); } ); } ); From bf6b5fe66d18fca0d69420249ef0318eb41a597f Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Fri, 7 Apr 2017 15:47:56 +0200 Subject: [PATCH 10/16] Added first batch of docs changes. --- src/clipboard.js | 51 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index b2fa4da..8fe5ebb 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -17,7 +17,7 @@ import normalizeClipboardHtml from './utils/normalizeclipboarddata'; import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor'; /** - * The clipboard feature. Currently, it's only responsible for intercepting the `paste` event and + * The clipboard feature. Currently, it's responsible for intercepting the `paste` and `drop` events and * passing the pasted content through the clipboard pipeline. * * ## Clipboard input pipeline @@ -26,21 +26,28 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * before it gets inserted into the editor. The pipeline consists of two events on which * the features can listen in order to modify or totally override the default behavior. * - * ### On {@link module:engine/view/document~Document#event:paste} + * ### On {@link module:engine/view/document~Document#event:paste} and {@link module:engine/view/document~Document#event:drop} * * The default action is to: * * 1. get HTML or plain text from the clipboard, - * 2. prevent the default action of the native `paste` event, - * 3. fire {@link module:engine/view/document~Document#event:clipboardInput} with the clipboard data parsed to + * 2. prevent the default action of the native `paste` or `drop` event, + * 3. fire {@link module:engine/view/document~Document#event:input} with a + * {@link module:clipboard/datatransfer~DataTransfer `dataTransfer`} property. + * 4. fire {@link module:clipboard/clipboard~Clipboard#event:inputTransformation} with a + * {@link module:clipboard/clipboard~ClipboardInputEventData `data`} containing the clipboard data parsed to * a {@link module:engine/view/documentfragment~DocumentFragment view document fragment}. * - * This action is performed by a low priority listener, so it can be overridden by a normal one + * These action are performed by a low priority listeners, so they can be overridden by a normal ones * when a deeper change in pasting behavior is needed. For example, a feature which wants to differently read * data from the clipboard (the {@link module:clipboard/datatransfer~DataTransfer `DataTransfer`}). * should plug a listener at this stage. * - * ### On {@link module:engine/view/document~Document#event:clipboardInput} + * ### On {@link module:engine/view/document~Document#event:input} + * + * TODO + * + * ### On {@link module:clipboard/clipboard~Clipboard#event:inputTransformation} * * The default action is to insert the content (`data.content`, represented by a * {@link module:engine/view/documentfragment~DocumentFragment}) to an editor if the data is not empty. @@ -177,34 +184,28 @@ export default class Clipboard extends Plugin { } /** - * Fired with a content which comes from the clipboard (was pasted or dropped) and + * Fired with a `dataTransfer`, which comes from the clipboard (was pasted or dropped) and * should be processed in order to be inserted into the editor. * It's part of the {@link module:clipboard/clipboard~Clipboard "clipboard pipeline"}. * * @see module:clipboard/clipboardobserver~ClipboardObserver * @see module:clipboard/clipboard~Clipboard - * @event module:engine/view/document~Document#event:clipboardInput - * @param {module:clipboard/clipboard~ClipboardInputEventData} data Event data. + * @event module:engine/view/document~Document#event:input + * @param {Object} data Event data. + * @param {module:clipboard/datatransfer~DataTransfer} data.dataTransfer Data transfer instance. */ /** - * The value of the {@link module:engine/view/document~Document#event:clipboardInput} event. - * - * @class module:clipboard/clipboard~ClipboardInputEventData - */ - -/** - * Data transfer instance. - * - * @readonly - * @member {module:clipboard/datatransfer~DataTransfer} module:clipboard/clipboard~ClipboardInputEventData#dataTransfer - */ - -/** - * Content to be inserted into the editor. It can be modified by the event listeners. - * Read more about the clipboard pipelines in {@link module:clipboard/clipboard~Clipboard}. + * Fired with a `content`, which comes from the clipboard (was pasted or dropped) and + * should be processed in order to be inserted into the editor. + * It's part of the {@link module:clipboard/clipboard~Clipboard "clipboard pipeline"}. * - * @member {module:engine/view/documentfragment~DocumentFragment} module:clipboard/clipboard~ClipboardInputEventData#content + * @see module:clipboard/clipboardobserver~ClipboardObserver + * @see module:clipboard/clipboard~Clipboard + * @event module:clipboard/clipboard~Clipboard#event:inputTransformation + * @param {Object} data Event data. + * @param {module:engine/view/documentfragment~DocumentFragment} data.content Event data. Content to be inserted into the editor. + * It can be modified by the event listeners. Read more about the clipboard pipelines in {@link module:clipboard/clipboard~Clipboard} */ /** From c980d48374c4f8968c3009e4df0e36514ee1c639 Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Fri, 7 Apr 2017 15:54:59 +0200 Subject: [PATCH 11/16] Added missing event docs. --- src/clipboardobserver.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/clipboardobserver.js b/src/clipboardobserver.js index 877dab9..89ba776 100644 --- a/src/clipboardobserver.js +++ b/src/clipboardobserver.js @@ -42,6 +42,20 @@ export default class ClipboardObserver extends DomEventObserver { } } +/** + * Fired when user dropped content into one of the editables. + * + * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}. + * + * Note that this event is not available by default. To make it available {@link module:clipboard/clipboardobserver~ClipboardObserver} + * needs to be added to {@link module:engine/view/document~Document} by the {@link module:engine/view/document~Document#addObserver} method. + * It's done by the {@link module:clipboard/clipboard~Clipboard} feature. If it's not loaded, it must be done manually. + * + * @see module:clipboard/clipboardobserver~ClipboardObserver + * @event module:engine/view/document~Document#event:drop + * @param {module:clipboard/clipboardobserver~ClipboardEventData} data Event data. + */ + /** * Fired when user pasted content into one of the editables. * From 81e9376cf50167538056b27944721d796c1c9332 Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Fri, 7 Apr 2017 16:31:27 +0200 Subject: [PATCH 12/16] Added more docs. --- src/clipboard.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index 8fe5ebb..65a898b 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -45,7 +45,15 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * * ### On {@link module:engine/view/document~Document#event:input} * - * TODO + * This action is performed by a low priority listener, so it can be overridden by a normal one. + * + * At this stage the dataTransfer object can be processed by the features, which want to transform the original dataTransform. + * + * this.listenTo( editor.editing.view, 'input', ( evt, data ) => { + * const content = customTransform( data.dataTransfer.get( 'text/html' ) ); + * const transformedContent = transform( content ); + * data.dataTransfer.set( 'text/html', transformedContent ); + * } ); * * ### On {@link module:clipboard/clipboard~Clipboard#event:inputTransformation} * @@ -57,7 +65,7 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * At this stage the pasted content can be processed by the features. E.g. a feature which wants to transform * a pasted text into a link can be implemented in this way: * - * this.listenTo( editor.editing.view, 'clipboardInput', ( evt, data ) => { + * this.listenTo( editor.plugins.get( 'clipboard/clipboard' ), 'inputTransformation', ( evt, data ) => { * if ( data.content.childCount == 1 && isUrlText( data.content.getChild( 0 ) ) ) { * const linkUrl = data.content.getChild( 0 ).data; * From 845c2ba73de0a09fa2dfad50cd27095edd82643d Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Fri, 7 Apr 2017 16:51:05 +0200 Subject: [PATCH 13/16] Fixed invalid event names. --- src/clipboard.js | 3 +-- tests/manual/copycut.js | 2 +- tests/manual/pasting.js | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index 65a898b..cced2f1 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -34,8 +34,7 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * 2. prevent the default action of the native `paste` or `drop` event, * 3. fire {@link module:engine/view/document~Document#event:input} with a * {@link module:clipboard/datatransfer~DataTransfer `dataTransfer`} property. - * 4. fire {@link module:clipboard/clipboard~Clipboard#event:inputTransformation} with a - * {@link module:clipboard/clipboard~ClipboardInputEventData `data`} containing the clipboard data parsed to + * 4. fire {@link module:clipboard/clipboard~Clipboard#event:inputTransformation} with a `data` containing the clipboard data parsed to * a {@link module:engine/view/documentfragment~DocumentFragment view document fragment}. * * These action are performed by a low priority listeners, so they can be overridden by a normal ones diff --git a/tests/manual/copycut.js b/tests/manual/copycut.js index bbb0a5d..a192725 100644 --- a/tests/manual/copycut.js +++ b/tests/manual/copycut.js @@ -45,7 +45,7 @@ ClassicEditor.create( document.querySelector( '#editor' ), { editor.editing.view.on( 'copy', onViewEvent, { priority: 'lowest' } ); editor.editing.view.on( 'cut', onViewEvent, { priority: 'lowest' } ); - editor.editing.view.on( 'clipboardInput', onPipelineEvent ); + editor.editing.view.on( 'input', onPipelineEvent ); editor.editing.view.on( 'clipboardOutput', ( evt, data ) => { console.clear(); onPipelineEvent( evt, data ); diff --git a/tests/manual/pasting.js b/tests/manual/pasting.js index 8921670..5415656 100644 --- a/tests/manual/pasting.js +++ b/tests/manual/pasting.js @@ -46,7 +46,7 @@ ClassicEditor.create( document.querySelector( '#editor' ), { console.log( 'text/plain\n', data.dataTransfer.getData( 'text/plain' ) ); } ); - editor.editing.view.on( 'clipboardInput', ( evt, data ) => { + editor.editing.view.on( 'input', ( evt, data ) => { console.log( '----- clipboardInput -----' ); console.log( 'stringify( data.content )\n', stringifyView( data.content ) ); } ); From fdc1d7789b835729d07854e2083e9a7e4443d5b9 Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Tue, 11 Apr 2017 13:38:58 +0200 Subject: [PATCH 14/16] Fixed docs and manual tests. --- src/clipboard.js | 12 ------------ src/clipboardobserver.js | 12 ++++++++++++ src/datatransfer.js | 6 ++++++ tests/manual/copycut.js | 3 ++- tests/manual/pasting.js | 5 +++-- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index cced2f1..fca64f4 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -190,18 +190,6 @@ export default class Clipboard extends Plugin { } } -/** - * Fired with a `dataTransfer`, which comes from the clipboard (was pasted or dropped) and - * should be processed in order to be inserted into the editor. - * It's part of the {@link module:clipboard/clipboard~Clipboard "clipboard pipeline"}. - * - * @see module:clipboard/clipboardobserver~ClipboardObserver - * @see module:clipboard/clipboard~Clipboard - * @event module:engine/view/document~Document#event:input - * @param {Object} data Event data. - * @param {module:clipboard/datatransfer~DataTransfer} data.dataTransfer Data transfer instance. - */ - /** * Fired with a `content`, which comes from the clipboard (was pasted or dropped) and * should be processed in order to be inserted into the editor. diff --git a/src/clipboardobserver.js b/src/clipboardobserver.js index 89ba776..fcea05c 100644 --- a/src/clipboardobserver.js +++ b/src/clipboardobserver.js @@ -42,6 +42,18 @@ export default class ClipboardObserver extends DomEventObserver { } } +/** + * Fired with a `dataTransfer`, which comes from the clipboard (was pasted or dropped) and + * should be processed in order to be inserted into the editor. + * It's part of the {@link module:clipboard/clipboard~Clipboard "clipboard pipeline"}. + * + * @see module:clipboard/clipboardobserver~ClipboardObserver + * @see module:clipboard/clipboard~Clipboard + * @event module:engine/view/document~Document#event:input + * @param {Object} data Event data. + * @param {module:clipboard/datatransfer~DataTransfer} data.dataTransfer Data transfer instance. + */ + /** * Fired when user dropped content into one of the editables. * diff --git a/src/datatransfer.js b/src/datatransfer.js index e32c4ba..f9aed58 100644 --- a/src/datatransfer.js +++ b/src/datatransfer.js @@ -20,6 +20,12 @@ export default class DataTransfer { */ this._native = nativeDataTransfer; + /** + * The array of files created from the native `DataTransfer#files` and `DataTransfer#items` objects. + * + * @public + * @member {Array.} #files + */ this.files = Array.from( this._getFiles() ); } diff --git a/tests/manual/copycut.js b/tests/manual/copycut.js index a192725..bca8218 100644 --- a/tests/manual/copycut.js +++ b/tests/manual/copycut.js @@ -36,6 +36,7 @@ ClassicEditor.create( document.querySelector( '#editor' ), { } ) .then( editor => { window.editor = editor; + const clipboard = editor.plugins.get( 'clipboard/clipboard' ); editor.editing.view.on( 'paste', ( evt, data ) => { console.clear(); @@ -45,7 +46,7 @@ ClassicEditor.create( document.querySelector( '#editor' ), { editor.editing.view.on( 'copy', onViewEvent, { priority: 'lowest' } ); editor.editing.view.on( 'cut', onViewEvent, { priority: 'lowest' } ); - editor.editing.view.on( 'input', onPipelineEvent ); + clipboard.on( 'inputTransformation', onPipelineEvent ); editor.editing.view.on( 'clipboardOutput', ( evt, data ) => { console.clear(); onPipelineEvent( evt, data ); diff --git a/tests/manual/pasting.js b/tests/manual/pasting.js index 5415656..bc77c54 100644 --- a/tests/manual/pasting.js +++ b/tests/manual/pasting.js @@ -36,6 +36,7 @@ ClassicEditor.create( document.querySelector( '#editor' ), { } ) .then( editor => { window.editor = editor; + const clipboard = editor.plugins.get( 'clipboard/clipboard' ); editor.editing.view.on( 'paste', ( evt, data ) => { console.clear(); @@ -46,9 +47,9 @@ ClassicEditor.create( document.querySelector( '#editor' ), { console.log( 'text/plain\n', data.dataTransfer.getData( 'text/plain' ) ); } ); - editor.editing.view.on( 'input', ( evt, data ) => { + clipboard.on( 'inputTransformation', ( evt, data ) => { console.log( '----- clipboardInput -----' ); - console.log( 'stringify( data.content )\n', stringifyView( data.content ) ); + console.log( 'stringify( data.dataTransfer )\n', stringifyView( data.content ) ); } ); } ) .catch( err => { From 3c160f8676cac837bd2d374c17d1766ba7a51e0c Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Tue, 11 Apr 2017 16:58:00 +0200 Subject: [PATCH 15/16] Renamed event. Switched listening method to listenTo. --- src/clipboard.js | 8 ++++---- src/clipboardobserver.js | 9 ++++----- tests/clipboard.js | 18 +++++++++--------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index fca64f4..f097e19 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -32,7 +32,7 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * * 1. get HTML or plain text from the clipboard, * 2. prevent the default action of the native `paste` or `drop` event, - * 3. fire {@link module:engine/view/document~Document#event:input} with a + * 3. fire {@link module:engine/view/document~Document#event:clipboardInput} with a * {@link module:clipboard/datatransfer~DataTransfer `dataTransfer`} property. * 4. fire {@link module:clipboard/clipboard~Clipboard#event:inputTransformation} with a `data` containing the clipboard data parsed to * a {@link module:engine/view/documentfragment~DocumentFragment view document fragment}. @@ -42,13 +42,13 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * data from the clipboard (the {@link module:clipboard/datatransfer~DataTransfer `DataTransfer`}). * should plug a listener at this stage. * - * ### On {@link module:engine/view/document~Document#event:input} + * ### On {@link module:engine/view/document~Document#event:clipboardInput} * * This action is performed by a low priority listener, so it can be overridden by a normal one. * * At this stage the dataTransfer object can be processed by the features, which want to transform the original dataTransform. * - * this.listenTo( editor.editing.view, 'input', ( evt, data ) => { + * this.listenTo( editor.editing.view, 'clipboardInput', ( evt, data ) => { * const content = customTransform( data.dataTransfer.get( 'text/html' ) ); * const transformedContent = transform( content ); * data.dataTransfer.set( 'text/html', transformedContent ); @@ -132,7 +132,7 @@ export default class Clipboard extends Plugin { // The clipboard paste pipeline. - this.listenTo( editingView, 'input', ( evt, data ) => { + this.listenTo( editingView, 'clipboardInput', ( evt, data ) => { const dataTransfer = data.dataTransfer; let content = ''; diff --git a/src/clipboardobserver.js b/src/clipboardobserver.js index fcea05c..d8df380 100644 --- a/src/clipboardobserver.js +++ b/src/clipboardobserver.js @@ -24,14 +24,13 @@ export default class ClipboardObserver extends DomEventObserver { this.domEventType = [ 'paste', 'copy', 'cut', 'drop' ]; - doc.on( 'paste', _handleInput, { priority: 'low' } ); - doc.on( 'drop', _handleInput, { priority: 'low' } ); + this.listenTo( doc, 'paste', handleInput, { priority: 'low' } ); + this.listenTo( doc, 'drop', handleInput, { priority: 'low' } ); - function _handleInput( evt, data ) { - // Prevent page refreshing. + function handleInput( evt, data ) { data.preventDefault(); - doc.fire( 'input', { dataTransfer: data.dataTransfer } ); + doc.fire( 'clipboardInput', { dataTransfer: data.dataTransfer } ); } } diff --git a/tests/clipboard.js b/tests/clipboard.js index 9a5305f..be41355 100644 --- a/tests/clipboard.js +++ b/tests/clipboard.js @@ -41,11 +41,11 @@ describe( 'Clipboard feature', () => { describe( 'clipboard paste pipeline', () => { describe( 'takes HTML data from the dataTransfer', () => { - it( 'and fires the input event on the editingView', ( done ) => { + it( 'and fires the clipboardInput event on the editingView', ( done ) => { const dataTransferMock = createDataTransfer( { 'text/html': '

x

', 'text/plain': 'y' } ); const preventDefaultSpy = sinon.spy(); - editingView.on( 'input', ( evt, data ) => { + editingView.on( 'clipboardInput', ( evt, data ) => { expect( preventDefaultSpy.calledOnce ).to.be.true; expect( data.dataTransfer ).to.equal( dataTransferMock ); @@ -77,11 +77,11 @@ describe( 'Clipboard feature', () => { } ); describe( 'takes plain text data from the dataTransfer if there is no HTML', () => { - it( 'and fires the input event on the editingView', ( done ) => { + it( 'and fires the clipboardInput event on the editingView', ( done ) => { const dataTransferMock = createDataTransfer( { 'text/plain': 'x\n\ny z' } ); const preventDefaultSpy = sinon.spy(); - editingView.on( 'input', ( evt, data ) => { + editingView.on( 'clipboardInput', ( evt, data ) => { expect( preventDefaultSpy.calledOnce ).to.be.true; expect( data.dataTransfer ).to.equal( dataTransferMock ); @@ -117,7 +117,7 @@ describe( 'Clipboard feature', () => { const preventDefaultSpy = sinon.spy(); const editorViewCalled = sinon.spy(); - editingView.on( 'input', ( evt, data ) => { + editingView.on( 'clipboardInput', ( evt, data ) => { expect( preventDefaultSpy.calledOnce ).to.be.true; expect( data.dataTransfer ).to.equal( dataTransferMock ); @@ -148,7 +148,7 @@ describe( 'Clipboard feature', () => { evt.stop(); } ); - editingView.on( 'input', spy ); + editingView.on( 'clipboardInput', spy ); editingView.fire( 'paste', { dataTransfer: dataTransferMock, @@ -190,7 +190,7 @@ describe( 'Clipboard feature', () => { const dataTransferMock = createDataTransfer( { 'text/plain': '' } ); const spy = sinon.stub( editor.data, 'insertContent' ); - editingView.fire( 'input', { + editingView.fire( 'clipboardInput', { dataTransfer: dataTransferMock, content: new ViewDocumentFragment() } ); @@ -198,11 +198,11 @@ describe( 'Clipboard feature', () => { expect( spy.callCount ).to.equal( 0 ); } ); - it( 'uses low priority observer for the input event', () => { + it( 'uses low priority observer for the clipboardInput event', () => { const dataTransferMock = createDataTransfer( { 'text/html': 'x' } ); const spy = sinon.stub( editor.data, 'insertContent' ); - editingView.on( 'input', ( evt ) => { + editingView.on( 'clipboardInput', ( evt ) => { evt.stop(); } ); From 8d38cfce998a49f361f9a144db163cdfa9d490b5 Mon Sep 17 00:00:00 2001 From: Maciej Bukowski Date: Tue, 11 Apr 2017 17:13:44 +0200 Subject: [PATCH 16/16] Fixed DataTransfer class. --- src/clipboard.js | 2 +- src/datatransfer.js | 53 +++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/clipboard.js b/src/clipboard.js index f097e19..8ee98c6 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -51,7 +51,7 @@ import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/html * this.listenTo( editor.editing.view, 'clipboardInput', ( evt, data ) => { * const content = customTransform( data.dataTransfer.get( 'text/html' ) ); * const transformedContent = transform( content ); - * data.dataTransfer.set( 'text/html', transformedContent ); + * data.dataTransfer.set( 'text/html', transformedContent ); * } ); * * ### On {@link module:clipboard/clipboard~Clipboard#event:inputTransformation} diff --git a/src/datatransfer.js b/src/datatransfer.js index f9aed58..dcdcd44 100644 --- a/src/datatransfer.js +++ b/src/datatransfer.js @@ -12,6 +12,14 @@ */ export default class DataTransfer { constructor( nativeDataTransfer ) { + /** + * The array of files created from the native `DataTransfer#files` or `DataTransfer#items`. + * + * @readonly + * @member {Array.} #files + */ + this.files = getFiles( nativeDataTransfer ); + /** * The native DataTransfer object. * @@ -19,14 +27,15 @@ export default class DataTransfer { * @member {DataTransfer} #_native */ this._native = nativeDataTransfer; + } - /** - * The array of files created from the native `DataTransfer#files` and `DataTransfer#items` objects. - * - * @public - * @member {Array.} #files - */ - this.files = Array.from( this._getFiles() ); + /** + * Returns an array of available native content types. + * + * @returns {Array.} + */ + get types() { + return this._native.types; } /** @@ -50,26 +59,18 @@ export default class DataTransfer { setData( type, data ) { this._native.setData( type, data ); } +} - *_getFiles() { - // DataTransfer.files and items are Array-like and might not have an iterable interface. - const files = this._native.files ? Array.from( this._native.files ) : []; - const items = this._native.items ? Array.from( this._native.items ) : []; - - if ( files.length ) { - yield* files; - } - // // Chrome have empty DataTransfer.files, but let get files through the items interface. - else { - for ( const item of items ) { - if ( item.kind == 'file' ) { - yield item.getAsFile(); - } - } - } - } +function getFiles( nativeDataTransfer ) { + // DataTransfer.files and items are Array-like and might not have an iterable interface. + const files = nativeDataTransfer.files ? Array.from( nativeDataTransfer.files ) : []; + const items = nativeDataTransfer.items ? Array.from( nativeDataTransfer.items ) : []; - get types() { - return this._native.types; + if ( files.length ) { + return files; } + // Chrome have empty DataTransfer.files, but let get files through the items interface. + return items + .filter( item => item.kind === 'file' ) + .map( item => item.getAsFile() ); }