UNPKG

7.08 kBJavaScriptView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @format
8 *
9 * @emails oncall+draft_js
10 */
11'use strict';
12
13var BlockMapBuilder = require("./BlockMapBuilder");
14
15var CharacterMetadata = require("./CharacterMetadata");
16
17var DataTransfer = require("fbjs/lib/DataTransfer");
18
19var DraftModifier = require("./DraftModifier");
20
21var DraftPasteProcessor = require("./DraftPasteProcessor");
22
23var EditorState = require("./EditorState");
24
25var RichTextEditorUtil = require("./RichTextEditorUtil");
26
27var getEntityKeyForSelection = require("./getEntityKeyForSelection");
28
29var getTextContentFromFiles = require("./getTextContentFromFiles");
30
31var isEventHandled = require("./isEventHandled");
32
33var splitTextIntoTextBlocks = require("./splitTextIntoTextBlocks");
34/**
35 * Paste content.
36 */
37
38
39function editOnPaste(editor, e) {
40 e.preventDefault();
41 var data = new DataTransfer(e.clipboardData); // Get files, unless this is likely to be a string the user wants inline.
42
43 if (!data.isRichText()) {
44 var files = data.getFiles();
45 var defaultFileText = data.getText();
46
47 if (files.length > 0) {
48 // Allow customized paste handling for images, etc. Otherwise, fall
49 // through to insert text contents into the editor.
50 if (editor.props.handlePastedFiles && isEventHandled(editor.props.handlePastedFiles(files))) {
51 return;
52 }
53 /* $FlowFixMe[incompatible-call] This comment suppresses an error found
54 * DataTransfer was typed. getFiles() returns an array of <Files extends
55 * Blob>, not Blob */
56
57
58 getTextContentFromFiles(files, function (
59 /*string*/
60 fileText) {
61 fileText = fileText || defaultFileText;
62
63 if (!fileText) {
64 return;
65 }
66
67 var editorState = editor._latestEditorState;
68 var blocks = splitTextIntoTextBlocks(fileText);
69 var character = CharacterMetadata.create({
70 style: editorState.getCurrentInlineStyle(),
71 entity: getEntityKeyForSelection(editorState.getCurrentContent(), editorState.getSelection())
72 });
73 var currentBlockType = RichTextEditorUtil.getCurrentBlockType(editorState);
74 var text = DraftPasteProcessor.processText(blocks, character, currentBlockType);
75 var fragment = BlockMapBuilder.createFromArray(text);
76 var withInsertedText = DraftModifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), fragment);
77 editor.update(EditorState.push(editorState, withInsertedText, 'insert-fragment'));
78 });
79 return;
80 }
81 }
82
83 var textBlocks = [];
84 var text = data.getText();
85 var html = data.getHTML();
86 var editorState = editor._latestEditorState;
87
88 if (editor.props.formatPastedText) {
89 var _editor$props$formatP = editor.props.formatPastedText(text, html),
90 formattedText = _editor$props$formatP.text,
91 formattedHtml = _editor$props$formatP.html;
92
93 text = formattedText;
94 html = formattedHtml;
95 }
96
97 if (editor.props.handlePastedText && isEventHandled(editor.props.handlePastedText(text, html, editorState))) {
98 return;
99 }
100
101 if (text) {
102 textBlocks = splitTextIntoTextBlocks(text);
103 }
104
105 if (!editor.props.stripPastedStyles) {
106 // If the text from the paste event is rich content that matches what we
107 // already have on the internal clipboard, assume that we should just use
108 // the clipboard fragment for the paste. This will allow us to preserve
109 // styling and entities, if any are present. Note that newlines are
110 // stripped during comparison -- this is because copy/paste within the
111 // editor in Firefox and IE will not include empty lines. The resulting
112 // paste will preserve the newlines correctly.
113 var internalClipboard = editor.getClipboard();
114
115 if (!editor.props.formatPastedText && data.isRichText() && internalClipboard) {
116 var _html;
117
118 if ( // If the editorKey is present in the pasted HTML, it should be safe to
119 // assume this is an internal paste.
120 ((_html = html) === null || _html === void 0 ? void 0 : _html.indexOf(editor.getEditorKey())) !== -1 || // The copy may have been made within a single block, in which case the
121 // editor key won't be part of the paste. In this case, just check
122 // whether the pasted text matches the internal clipboard.
123 textBlocks.length === 1 && internalClipboard.size === 1 && internalClipboard.first().getText() === text) {
124 editor.update(insertFragment(editor._latestEditorState, internalClipboard));
125 return;
126 }
127 } else if (internalClipboard && data.types.includes('com.apple.webarchive') && !data.types.includes('text/html') && areTextBlocksAndClipboardEqual(textBlocks, internalClipboard)) {
128 // Safari does not properly store text/html in some cases.
129 // Use the internalClipboard if present and equal to what is on
130 // the clipboard. See https://bugs.webkit.org/show_bug.cgi?id=19893.
131 editor.update(insertFragment(editor._latestEditorState, internalClipboard));
132 return;
133 } // If there is html paste data, try to parse that.
134
135
136 if (html) {
137 var htmlFragment = DraftPasteProcessor.processHTML(html, editor.props.blockRenderMap);
138
139 if (htmlFragment) {
140 var contentBlocks = htmlFragment.contentBlocks,
141 entityMap = htmlFragment.entityMap;
142
143 if (contentBlocks) {
144 var htmlMap = BlockMapBuilder.createFromArray(contentBlocks);
145 editor.update(insertFragment(editor._latestEditorState, htmlMap, entityMap));
146 return;
147 }
148 }
149 } // Otherwise, create a new fragment from our pasted text. Also
150 // empty the internal clipboard, since it's no longer valid.
151
152
153 editor.setClipboard(null);
154 }
155
156 if (textBlocks.length) {
157 var character = CharacterMetadata.create({
158 style: editorState.getCurrentInlineStyle(),
159 entity: getEntityKeyForSelection(editorState.getCurrentContent(), editorState.getSelection())
160 });
161 var currentBlockType = RichTextEditorUtil.getCurrentBlockType(editorState);
162 var textFragment = DraftPasteProcessor.processText(textBlocks, character, currentBlockType);
163 var textMap = BlockMapBuilder.createFromArray(textFragment);
164 editor.update(insertFragment(editor._latestEditorState, textMap));
165 }
166}
167
168function insertFragment(editorState, fragment, entityMap) {
169 var newContent = DraftModifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), fragment); // TODO: merge the entity map once we stop using DraftEntity
170 // like this:
171 // const mergedEntityMap = newContent.getEntityMap().merge(entityMap);
172
173 return EditorState.push(editorState, newContent.set('entityMap', entityMap), 'insert-fragment');
174}
175
176function areTextBlocksAndClipboardEqual(textBlocks, blockMap) {
177 return textBlocks.length === blockMap.size && blockMap.valueSeq().every(function (block, ii) {
178 return block.getText() === textBlocks[ii];
179 });
180}
181
182module.exports = editOnPaste;
\No newline at end of file