@ckeditor/ckeditor5-clipboard 35.2.1 → 35.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -2,11 +2,9 @@
2
2
  * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module clipboard
8
7
  */
9
-
10
8
  export { default as Clipboard } from './clipboard';
11
9
  export { default as ClipboardPipeline } from './clipboardpipeline';
12
10
  export { default as DragDrop } from './dragdrop';
@@ -2,16 +2,12 @@
2
2
  * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module clipboard/pasteplaintext
8
7
  */
9
-
10
8
  import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
11
-
12
9
  import ClipboardObserver from './clipboardobserver';
13
10
  import ClipboardPipeline from './clipboardpipeline';
14
-
15
11
  /**
16
12
  * The plugin detects the user's intention to paste plain text.
17
13
  *
@@ -20,86 +16,71 @@ import ClipboardPipeline from './clipboardpipeline';
20
16
  * @extends module:core/plugin~Plugin
21
17
  */
22
18
  export default class PastePlainText extends Plugin {
23
- /**
24
- * @inheritDoc
25
- */
26
- static get pluginName() {
27
- return 'PastePlainText';
28
- }
29
-
30
- /**
31
- * @inheritDoc
32
- */
33
- static get requires() {
34
- return [ ClipboardPipeline ];
35
- }
36
-
37
- /**
38
- * @inheritDoc
39
- */
40
- init() {
41
- const editor = this.editor;
42
- const model = editor.model;
43
- const view = editor.editing.view;
44
- const viewDocument = view.document;
45
- const selection = model.document.selection;
46
-
47
- let shiftPressed = false;
48
-
49
- view.addObserver( ClipboardObserver );
50
-
51
- this.listenTo( viewDocument, 'keydown', ( evt, data ) => {
52
- shiftPressed = data.shiftKey;
53
- } );
54
-
55
- editor.plugins.get( ClipboardPipeline ).on( 'contentInsertion', ( evt, data ) => {
56
- // Plain text can be determined based on the event flag (#7799) or auto-detection (#1006). If detected,
57
- // preserve selection attributes on pasted items.
58
- if ( !shiftPressed && !isPlainTextFragment( data.content, model.schema ) ) {
59
- return;
60
- }
61
-
62
- model.change( writer => {
63
- // Formatting attributes should be preserved.
64
- const textAttributes = Array.from( selection.getAttributes() )
65
- .filter( ( [ key ] ) => model.schema.getAttributeProperties( key ).isFormatting );
66
-
67
- if ( !selection.isCollapsed ) {
68
- model.deleteContent( selection, { doNotAutoparagraph: true } );
69
- }
70
-
71
- // Also preserve other attributes if they survived the content deletion (because they were not fully selected).
72
- // For example linkHref is not a formatting attribute but it should be preserved if pasted text was in the middle
73
- // of a link.
74
- textAttributes.push( ...selection.getAttributes() );
75
-
76
- const range = writer.createRangeIn( data.content );
77
-
78
- for ( const item of range.getItems() ) {
79
- if ( item.is( '$textProxy' ) ) {
80
- writer.setAttributes( textAttributes, item );
81
- }
82
- }
83
- } );
84
- } );
85
- }
19
+ /**
20
+ * @inheritDoc
21
+ */
22
+ static get pluginName() {
23
+ return 'PastePlainText';
24
+ }
25
+ /**
26
+ * @inheritDoc
27
+ */
28
+ static get requires() {
29
+ return [ClipboardPipeline];
30
+ }
31
+ /**
32
+ * @inheritDoc
33
+ */
34
+ init() {
35
+ const editor = this.editor;
36
+ const model = editor.model;
37
+ const view = editor.editing.view;
38
+ const viewDocument = view.document;
39
+ const selection = model.document.selection;
40
+ let shiftPressed = false;
41
+ view.addObserver(ClipboardObserver);
42
+ this.listenTo(viewDocument, 'keydown', (evt, data) => {
43
+ shiftPressed = data.shiftKey;
44
+ });
45
+ editor.plugins.get(ClipboardPipeline).on('contentInsertion', (evt, data) => {
46
+ // Plain text can be determined based on the event flag (#7799) or auto-detection (#1006). If detected,
47
+ // preserve selection attributes on pasted items.
48
+ if (!shiftPressed && !isPlainTextFragment(data.content, model.schema)) {
49
+ return;
50
+ }
51
+ model.change(writer => {
52
+ // Formatting attributes should be preserved.
53
+ const textAttributes = Array.from(selection.getAttributes())
54
+ .filter(([key]) => model.schema.getAttributeProperties(key).isFormatting);
55
+ if (!selection.isCollapsed) {
56
+ model.deleteContent(selection, { doNotAutoparagraph: true });
57
+ }
58
+ // Also preserve other attributes if they survived the content deletion (because they were not fully selected).
59
+ // For example linkHref is not a formatting attribute but it should be preserved if pasted text was in the middle
60
+ // of a link.
61
+ textAttributes.push(...selection.getAttributes());
62
+ const range = writer.createRangeIn(data.content);
63
+ for (const item of range.getItems()) {
64
+ if (item.is('$textProxy')) {
65
+ writer.setAttributes(textAttributes, item);
66
+ }
67
+ }
68
+ });
69
+ });
70
+ }
86
71
  }
87
-
88
72
  // Returns true if specified `documentFragment` represents a plain text.
89
73
  //
90
74
  // @param {module:engine/view/documentfragment~DocumentFragment} documentFragment
91
75
  // @param {module:engine/model/schema~Schema} schema
92
76
  // @returns {Boolean}
93
- function isPlainTextFragment( documentFragment, schema ) {
94
- if ( documentFragment.childCount > 1 ) {
95
- return false;
96
- }
97
-
98
- const child = documentFragment.getChild( 0 );
99
-
100
- if ( schema.isObject( child ) ) {
101
- return false;
102
- }
103
-
104
- return [ ...child.getAttributeKeys() ].length == 0;
77
+ function isPlainTextFragment(documentFragment, schema) {
78
+ if (documentFragment.childCount > 1) {
79
+ return false;
80
+ }
81
+ const child = documentFragment.getChild(0);
82
+ if (schema.isObject(child)) {
83
+ return false;
84
+ }
85
+ return Array.from(child.getAttributeKeys()).length == 0;
105
86
  }
@@ -2,11 +2,9 @@
2
2
  * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module clipboard/utils/normalizeclipboarddata
8
7
  */
9
-
10
8
  /**
11
9
  * Removes some popular browser quirks out of the clipboard data (HTML).
12
10
  * Removes all HTML comments. These are considered an internal thing and it makes little sense if they leak into the editor data.
@@ -14,17 +12,16 @@
14
12
  * @param {String} data The HTML data to normalize.
15
13
  * @returns {String} Normalized HTML.
16
14
  */
17
- export default function normalizeClipboardData( data ) {
18
- return data
19
- .replace( /<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g, ( fullMatch, spaces ) => {
20
- // Handle the most popular and problematic case when even a single space becomes an nbsp;.
21
- // Decode those to normal spaces. Read more in https://github.com/ckeditor/ckeditor5-clipboard/issues/2.
22
- if ( spaces.length == 1 ) {
23
- return ' ';
24
- }
25
-
26
- return spaces;
27
- } )
28
- // Remove all HTML comments.
29
- .replace( /<!--[\s\S]*?-->/g, '' );
15
+ export default function normalizeClipboardData(data) {
16
+ return data
17
+ .replace(/<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g, (fullMatch, spaces) => {
18
+ // Handle the most popular and problematic case when even a single space becomes an nbsp;.
19
+ // Decode those to normal spaces. Read more in https://github.com/ckeditor/ckeditor5-clipboard/issues/2.
20
+ if (spaces.length == 1) {
21
+ return ' ';
22
+ }
23
+ return spaces;
24
+ })
25
+ // Remove all HTML comments.
26
+ .replace(/<!--[\s\S]*?-->/g, '');
30
27
  }
@@ -2,39 +2,34 @@
2
2
  * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module clipboard/utils/plaintexttohtml
8
7
  */
9
-
10
8
  /**
11
9
  * Converts plain text to its HTML-ized version.
12
10
  *
13
11
  * @param {String} text The plain text to convert.
14
12
  * @returns {String} HTML generated from the plain text.
15
13
  */
16
- export default function plainTextToHtml( text ) {
17
- text = text
18
- // Encode <>.
19
- .replace( /</g, '&lt;' )
20
- .replace( />/g, '&gt;' )
21
- // Creates a paragraph for each double line break.
22
- .replace( /\r?\n\r?\n/g, '</p><p>' )
23
- // Creates a line break for each single line break.
24
- .replace( /\r?\n/g, '<br>' )
25
- // Preserve trailing spaces (only the first and last one – the rest is handled below).
26
- .replace( /^\s/, '&nbsp;' )
27
- .replace( /\s$/, '&nbsp;' )
28
- // Preserve other subsequent spaces now.
29
- .replace( /\s\s/g, ' &nbsp;' );
30
-
31
- if ( text.includes( '</p><p>' ) || text.includes( '<br>' ) ) {
32
- // If we created paragraphs above, add the trailing ones.
33
- text = `<p>${ text }</p>`;
34
- }
35
-
36
- // TODO:
37
- // * What about '\nfoo' vs ' foo'?
38
-
39
- return text;
14
+ export default function plainTextToHtml(text) {
15
+ text = text
16
+ // Encode <>.
17
+ .replace(/</g, '&lt;')
18
+ .replace(/>/g, '&gt;')
19
+ // Creates a paragraph for each double line break.
20
+ .replace(/\r?\n\r?\n/g, '</p><p>')
21
+ // Creates a line break for each single line break.
22
+ .replace(/\r?\n/g, '<br>')
23
+ // Preserve trailing spaces (only the first and last one – the rest is handled below).
24
+ .replace(/^\s/, '&nbsp;')
25
+ .replace(/\s$/, '&nbsp;')
26
+ // Preserve other subsequent spaces now.
27
+ .replace(/\s\s/g, ' &nbsp;');
28
+ if (text.includes('</p><p>') || text.includes('<br>')) {
29
+ // If we created paragraphs above, add the trailing ones.
30
+ text = `<p>${text}</p>`;
31
+ }
32
+ // TODO:
33
+ // * What about '\nfoo' vs ' foo'?
34
+ return text;
40
35
  }
@@ -2,55 +2,49 @@
2
2
  * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
- /**
7
- * @module clipboard/utils/viewtoplaintext
8
- */
9
-
10
5
  // Elements which should not have empty-line padding.
11
6
  // Most `view.ContainerElement` want to be separate by new-line, but some are creating one structure
12
7
  // together (like `<li>`) so it is better to separate them by only one "\n".
13
- const smallPaddingElements = [ 'figcaption', 'li' ];
14
-
8
+ const smallPaddingElements = ['figcaption', 'li'];
15
9
  /**
16
10
  * Converts {@link module:engine/view/item~Item view item} and all of its children to plain text.
17
11
  *
18
12
  * @param {module:engine/view/item~Item} viewItem View item to convert.
19
13
  * @returns {String} Plain text representation of `viewItem`.
20
14
  */
21
- export default function viewToPlainText( viewItem ) {
22
- let text = '';
23
-
24
- if ( viewItem.is( '$text' ) || viewItem.is( '$textProxy' ) ) {
25
- // If item is `Text` or `TextProxy` simple take its text data.
26
- text = viewItem.data;
27
- } else if ( viewItem.is( 'element', 'img' ) && viewItem.hasAttribute( 'alt' ) ) {
28
- // Special case for images - use alt attribute if it is provided.
29
- text = viewItem.getAttribute( 'alt' );
30
- } else if ( viewItem.is( 'element', 'br' ) ) {
31
- // A soft break should be converted into a single line break (#8045).
32
- text = '\n';
33
- } else {
34
- // Other elements are document fragments, attribute elements or container elements.
35
- // They don't have their own text value, so convert their children.
36
- let prev = null;
37
-
38
- for ( const child of viewItem.getChildren() ) {
39
- const childText = viewToPlainText( child );
40
-
41
- // Separate container element children with one or more new-line characters.
42
- if ( prev && ( prev.is( 'containerElement' ) || child.is( 'containerElement' ) ) ) {
43
- if ( smallPaddingElements.includes( prev.name ) || smallPaddingElements.includes( child.name ) ) {
44
- text += '\n';
45
- } else {
46
- text += '\n\n';
47
- }
48
- }
49
-
50
- text += childText;
51
- prev = child;
52
- }
53
- }
54
-
55
- return text;
15
+ export default function viewToPlainText(viewItem) {
16
+ let text = '';
17
+ if (viewItem.is('$text') || viewItem.is('$textProxy')) {
18
+ // If item is `Text` or `TextProxy` simple take its text data.
19
+ text = viewItem.data;
20
+ }
21
+ else if (viewItem.is('element', 'img') && viewItem.hasAttribute('alt')) {
22
+ // Special case for images - use alt attribute if it is provided.
23
+ text = viewItem.getAttribute('alt');
24
+ }
25
+ else if (viewItem.is('element', 'br')) {
26
+ // A soft break should be converted into a single line break (#8045).
27
+ text = '\n';
28
+ }
29
+ else {
30
+ // Other elements are document fragments, attribute elements or container elements.
31
+ // They don't have their own text value, so convert their children.
32
+ let prev = null;
33
+ for (const child of viewItem.getChildren()) {
34
+ const childText = viewToPlainText(child);
35
+ // Separate container element children with one or more new-line characters.
36
+ if (prev && (prev.is('containerElement') || child.is('containerElement'))) {
37
+ if (smallPaddingElements.includes(prev.name) ||
38
+ smallPaddingElements.includes(child.name)) {
39
+ text += '\n';
40
+ }
41
+ else {
42
+ text += '\n\n';
43
+ }
44
+ }
45
+ text += childText;
46
+ prev = child;
47
+ }
48
+ }
49
+ return text;
56
50
  }
@@ -1,112 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
-
6
- /**
7
- * @module clipboard/datatransfer
8
- */
9
-
10
- /**
11
- * A facade over the native [`DataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) object.
12
- */
13
- export default class DataTransfer {
14
- constructor( nativeDataTransfer ) {
15
- /**
16
- * The array of files created from the native `DataTransfer#files` or `DataTransfer#items`.
17
- *
18
- * @readonly
19
- * @member {Array.<File>} #files
20
- */
21
- this.files = getFiles( nativeDataTransfer );
22
-
23
- /**
24
- * The native DataTransfer object.
25
- *
26
- * @private
27
- * @member {DataTransfer} #_native
28
- */
29
- this._native = nativeDataTransfer;
30
- }
31
-
32
- /**
33
- * Returns an array of available native content types.
34
- *
35
- * @returns {Array.<String>}
36
- */
37
- get types() {
38
- return this._native.types;
39
- }
40
-
41
- /**
42
- * Gets the data from the data transfer by its MIME type.
43
- *
44
- * dataTransfer.getData( 'text/plain' );
45
- *
46
- * @param {String} type The MIME type. E.g. `text/html` or `text/plain`.
47
- * @returns {String}
48
- */
49
- getData( type ) {
50
- return this._native.getData( type );
51
- }
52
-
53
- /**
54
- * Sets the data in the data transfer.
55
- *
56
- * @param {String} type The MIME type. E.g. `text/html` or `text/plain`.
57
- * @param {String} data
58
- */
59
- setData( type, data ) {
60
- this._native.setData( type, data );
61
- }
62
-
63
- /**
64
- * The effect that is allowed for a drag operation.
65
- *
66
- * @param {String} value
67
- */
68
- set effectAllowed( value ) {
69
- this._native.effectAllowed = value;
70
- }
71
-
72
- get effectAllowed() {
73
- return this._native.effectAllowed;
74
- }
75
-
76
- /**
77
- * The actual drop effect.
78
- *
79
- * @param {String} value
80
- */
81
- set dropEffect( value ) {
82
- this._native.dropEffect = value;
83
- }
84
-
85
- get dropEffect() {
86
- return this._native.dropEffect;
87
- }
88
-
89
- /**
90
- * Whether the dragging operation was canceled.
91
- *
92
- * @returns {Boolean}
93
- */
94
- get isCanceled() {
95
- return this._native.dropEffect == 'none' || !!this._native.mozUserCancelled;
96
- }
97
- }
98
-
99
- function getFiles( nativeDataTransfer ) {
100
- // DataTransfer.files and items are array-like and might not have an iterable interface.
101
- const files = Array.from( nativeDataTransfer.files || [] );
102
- const items = Array.from( nativeDataTransfer.items || [] );
103
-
104
- if ( files.length ) {
105
- return files;
106
- }
107
-
108
- // Chrome has empty DataTransfer.files, but allows getting files through the items interface.
109
- return items
110
- .filter( item => item.kind === 'file' )
111
- .map( item => item.getAsFile() );
112
- }