8
0
Piotrek Koszuliński 9 лет назад
Родитель
Сommit
580386125b

+ 84 - 0
packages/ckeditor5-clipboard/src/clipboard.js

@@ -5,10 +5,40 @@
 
 
 import Feature from '../core/feature.js';
 import Feature from '../core/feature.js';
 
 
+import ClipboardObserver from './clipboardobserver.js';
+import ClipboardInputCommand from './clipboardinputcommand.js';
+
+import plainTextToHtml from './utils/plaintexttohtml.js';
+import normalizeClipboardHtml from './utils/normalizeclipboarddata.js';
+
+import HtmlDataProcessor from '../engine/dataprocessor/htmldataprocessor.js';
+
+import { stringify as stringifyView } from '../engine/dev-utils/view.js';
+
 /**
 /**
  * The clipboard feature. Currently, it's only responsible for intercepting the paste event and
  * The clipboard feature. Currently, it's only responsible for intercepting the paste event and
  * passing the pasted content through a paste pipeline.
  * passing the pasted content through a paste pipeline.
  *
  *
+ * ## Clipboard Pipeline
+ *
+ * The feature creates the clipboard pipeline which allows processing clipboard contents
+ * and finally inserts the data to the editor/
+ *
+ * ### On {@link engine.view.Document#paste}
+ *
+ * 1. Get HTML or plain text from the clipboard,
+ * 2. Fire {@link engine.view.Document#clipboardInput} with the clipboard data parsed to
+ * a {@link engine.view.DocumentFragment view document fragment}.
+ * 3. Prevent default action of the native `paste` event.
+ *
+ * This action is performed by a low priority listener, so it can be overriden by a normal one.
+ *
+ * ### On {@link engine.view.Document#clipboardInput}
+ *
+ * If the data is not empty insert it to the editor using the `clipboardInput` command.
+ *
+ * This action is performed by a low priority listener, so it can be overriden by a normal one.
+ *
  * @memberOf clipboard
  * @memberOf clipboard
  * @extends core.Feature
  * @extends core.Feature
  */
  */
@@ -17,5 +47,59 @@ export default class Clipboard extends Feature {
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */
 	init() {
 	init() {
+		const editor = this.editor;
+		const editingView = editor.editing.view;
+
+		this._htmlDataProcessor = new HtmlDataProcessor();
+
+		editor.commands.set( 'clipboardInput', new ClipboardInputCommand( editor ) );
+
+		editingView.addObserver( ClipboardObserver );
+
+		// The clipboard pipeline.
+
+		this.listenTo( editingView, 'paste', ( evt, data ) => {
+			const dataTransfer = data.dataTransfer;
+			let content = '';
+
+			if ( dataTransfer.getData( 'text/html' ) ) {
+				content = normalizeClipboardHtml( dataTransfer.getData( 'text/html' ) );
+			} else if ( dataTransfer.getData( 'text/plain' ) ) {
+				content = plainTextToHtml( dataTransfer.getData( 'text/plain' ) );
+			}
+
+			content = this._htmlDataProcessor.toView( content );
+
+			editingView.fire( 'clipboardInput', { dataTransfer, content } );
+
+			data.preventDefault();
+		}, { priority: 'low' } );
+
+		this.listenTo( editingView, 'clipboardInput', ( evt, data ) => {
+			if ( data.content.childCount ) {
+				console.log( 'pasted (view):' ); // jshint ignore:line
+				console.log( stringifyView( data.content ) ); // jshint ignore:line
+
+				editor.execute( 'clipboardInput', { content: data.content } );
+			}
+		}, { priority: 'low' } );
+
+		// TMP!
+		// Create a context in the schema for processing the pasted content.
+		// Read: https://github.com/ckeditor/ckeditor5-engine/issues/638#issuecomment-255086588
+
+		const schema = editor.document.schema;
+
+		schema.registerItem( '$clipboardHolder', '$root' );
+		schema.allow( { name: '$text', inside: '$clipboardHolder' } );
 	}
 	}
 }
 }
+
+/**
+ * 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 "clipboard pipeline".
+ *
+ * @see clipboard.ClipboardObserver
+ * @event engine.view.Document#clipboardInput
+ * @param {engine.view.observer.ClipboardInputEventData} data Event data.
+ */

+ 32 - 0
packages/ckeditor5-clipboard/src/clipboardinputcommand.js

@@ -0,0 +1,32 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Command from '../core/command/command.js';
+
+/**
+ * TODO
+ *
+ * @memberOf clipboard
+ * @extends core.command.Command
+ */
+export default class ClipboardInputCommand extends Command {
+	constructor( editor ) {
+		super( editor );
+	}
+
+	/**
+	 * @protected
+	 * @param {Object} options
+	 * @param {engine.view.DocumentFragment} options.content
+	 */
+	_doExecute( options = {} ) {
+		const doc = this.editor.document;
+		const batch = doc.batch();
+
+		doc.enqueueChanges( () => {
+			this.editor.data.insertContent( batch, doc.selection, options.content );
+		} );
+	}
+}

+ 59 - 0
packages/ckeditor5-clipboard/src/clipboardobserver.js

@@ -0,0 +1,59 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import DomEventObserver from '../engine/view/observer/domeventobserver.js';
+import DataTransfer from './datatransfer.js';
+
+/**
+ * {@link engine.view.Document#paste Paste} event observer.
+ *
+ * Note that this observer is not available by default. To make it available it needs to be added to {@link engine.view.Document}
+ * by the {@link engine.view.Document#addObserver} method.
+ *
+ * @memberOf engine.view.observer
+ * @extends engine.view.observer.DomEventObserver
+ */
+export default class ClipboardObserver extends DomEventObserver {
+	constructor( doc ) {
+		super( doc );
+
+		this.domEventType = [ 'paste' ];
+	}
+
+	onDomEvent( domEvent ) {
+		this.fire( domEvent.type, domEvent, {
+			dataTransfer: new DataTransfer( domEvent.clipboardData )
+		} );
+	}
+}
+
+/**
+ * Fired when user pasted content into one of the editables.
+ *
+ * Introduced by {@link clipboard.ClipboardObserver}.
+ *
+ * Note that this event is not available by default. To make it available {@link clipboard.ClipboardObserver} needs to be added
+ * to {@link engine.view.Document} by the {@link engine.view.Document#addObserver} method.
+ *
+ * @see clipboard.ClipboardObserver
+ * @event engine.view.Document#paste
+ * @param {engine.view.observer.ClipboardEventData} data Event data.
+ */
+
+/**
+ * The value of the {@link engine.view.Document#paste} event.
+ *
+ * In order to access clipboard data use {@link #dataTransfer}.
+ *
+ * @class engine.view.observer.ClipboardEventData
+ * @extends engine.view.observer.DomEventData
+ */
+
+/**
+ * Data transfer instance.
+ *
+ * @readonly
+ * @member {clipboard.DataTransfer} engine.view.observer.ClipboardEventData#dataTransfer
+ */

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

@@ -0,0 +1,29 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * Facade over the native [`DataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer).
+ *
+ * @memberOf clipboard
+ */
+export default class DataTransfer {
+	constructor( nativeDataTransfer ) {
+		/**
+		 * @private {DataTransfer}
+		 */
+		this._native = nativeDataTransfer;
+	}
+
+	/**
+	 * Gets data from the data transfer by its mime type.
+	 *
+	 *		dataTransfer.getData( 'text/plain' );
+	 *
+	 * @param {String} type The mime type. E.g. `text/html` or `text/plain`.
+	 */
+	getData( type ) {
+		return this._native.getData( type );
+	}
+}

+ 15 - 0
packages/ckeditor5-clipboard/src/utils/normalizeclipboarddata.js

@@ -0,0 +1,15 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * Removes some popular browser quirks out of the clipboard data (HTML).
+ *
+ * @param {String} data The HTML data to normalize.
+ * @returns {String} Normalized HTML.
+ */
+export default function normalizeClipboardData( data ) {
+	return data
+		.replace( /<span class="Apple-converted-space">(\s+)<\/span>/, '$1' );
+}

+ 36 - 0
packages/ckeditor5-clipboard/src/utils/plaintexttohtml.js

@@ -0,0 +1,36 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * Converts plain text to its HTML-ized version.
+ *
+ * @param {String} text The plain text to convert.
+ * @returns {String} HTML generated from the plain text.
+ */
+export default function plainTextToHtml( text ) {
+	text = text
+		// Encode <>.
+		.replace( /</g, '&lt;' )
+		.replace( />/g, '&gt;' )
+		// 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/, ' ' )
+		// 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.
+		.replace( /\s\s/g, ' &nbsp;' );
+
+	if ( text.indexOf( '</p><p>' ) > -1 ) {
+		// If we created paragraphs above, add the trailing ones.
+		text = `<p>${ text }</p>`;
+	}
+
+	// TODO:
+	// * What about '\nfoo' vs ' foo'?
+
+	return text;
+}

+ 12 - 0
packages/ckeditor5-clipboard/tests/manual/pasting.js

@@ -24,6 +24,18 @@ ClassicEditor.create( document.querySelector( '#editor' ), {
 } )
 } )
 .then( editor => {
 .then( editor => {
 	window.editor = editor;
 	window.editor = editor;
+
+	editor.editing.view.on( 'paste', ( evt, data ) => {
+		console.log( '----- paste -----' );
+		console.log( data );
+		console.log( 'text/html', data.dataTransfer.getData( 'text/html' ) );
+		console.log( 'text/plain', data.dataTransfer.getData( 'text/plain' ) );
+	} );
+
+	editor.editing.view.on( 'clipboardInput', ( evt, data ) => {
+		console.log( '----- clipboardInput -----' );
+		console.log( data.dataValue );
+	} );
 } )
 } )
 .catch( err => {
 .catch( err => {
 	console.error( err.stack );
 	console.error( err.stack );