@atlaskit/editor-plugin-paste 0.1.22 → 0.2.1

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.
Files changed (78) hide show
  1. package/.eslintrc.js +18 -0
  2. package/CHANGELOG.md +12 -0
  3. package/dist/cjs/actions.js +12 -0
  4. package/dist/cjs/commands.js +255 -0
  5. package/dist/cjs/edge-cases/index.js +88 -0
  6. package/dist/cjs/edge-cases/lists.js +107 -0
  7. package/dist/cjs/handlers.js +939 -0
  8. package/dist/cjs/index.js +8 -1
  9. package/dist/cjs/plugin.js +43 -0
  10. package/dist/cjs/plugins/media.js +207 -0
  11. package/dist/cjs/pm-plugins/analytics.js +376 -0
  12. package/dist/cjs/pm-plugins/clipboard-text-serializer.js +43 -0
  13. package/dist/cjs/pm-plugins/main.js +484 -0
  14. package/dist/cjs/pm-plugins/plugin-factory.js +42 -0
  15. package/dist/cjs/reducer.js +41 -0
  16. package/dist/cjs/util/index.js +214 -0
  17. package/dist/cjs/util/tinyMCE.js +183 -0
  18. package/dist/es2019/actions.js +6 -0
  19. package/dist/es2019/commands.js +236 -0
  20. package/dist/es2019/edge-cases/index.js +87 -0
  21. package/dist/es2019/edge-cases/lists.js +113 -0
  22. package/dist/es2019/handlers.js +919 -0
  23. package/dist/es2019/index.js +1 -1
  24. package/dist/es2019/plugin.js +38 -0
  25. package/dist/es2019/plugins/media.js +204 -0
  26. package/dist/es2019/pm-plugins/analytics.js +332 -0
  27. package/dist/es2019/pm-plugins/clipboard-text-serializer.js +37 -0
  28. package/dist/es2019/pm-plugins/main.js +453 -0
  29. package/dist/es2019/pm-plugins/plugin-factory.js +30 -0
  30. package/dist/es2019/reducer.js +32 -0
  31. package/dist/es2019/util/index.js +209 -0
  32. package/dist/es2019/util/tinyMCE.js +168 -0
  33. package/dist/esm/actions.js +6 -0
  34. package/dist/esm/commands.js +249 -0
  35. package/dist/esm/edge-cases/index.js +81 -0
  36. package/dist/esm/edge-cases/lists.js +98 -0
  37. package/dist/esm/handlers.js +918 -0
  38. package/dist/esm/index.js +1 -1
  39. package/dist/esm/plugin.js +37 -0
  40. package/dist/esm/plugins/media.js +199 -0
  41. package/dist/esm/pm-plugins/analytics.js +364 -0
  42. package/dist/esm/pm-plugins/clipboard-text-serializer.js +37 -0
  43. package/dist/esm/pm-plugins/main.js +471 -0
  44. package/dist/esm/pm-plugins/plugin-factory.js +36 -0
  45. package/dist/esm/reducer.js +34 -0
  46. package/dist/esm/util/index.js +194 -0
  47. package/dist/esm/util/tinyMCE.js +176 -0
  48. package/dist/types/actions.d.ts +21 -0
  49. package/dist/types/commands.d.ts +29 -0
  50. package/dist/types/edge-cases/index.d.ts +11 -0
  51. package/dist/types/edge-cases/lists.d.ts +18 -0
  52. package/dist/types/handlers.d.ts +55 -0
  53. package/dist/types/index.d.ts +1 -0
  54. package/dist/types/plugin.d.ts +2 -0
  55. package/dist/types/plugins/media.d.ts +23 -0
  56. package/dist/types/pm-plugins/analytics.d.ts +44 -0
  57. package/dist/types/pm-plugins/clipboard-text-serializer.d.ts +13 -0
  58. package/dist/types/pm-plugins/main.d.ts +12 -0
  59. package/dist/types/pm-plugins/plugin-factory.d.ts +3 -0
  60. package/dist/types/reducer.d.ts +3 -0
  61. package/dist/types/util/index.d.ts +21 -0
  62. package/dist/types/util/tinyMCE.d.ts +32 -0
  63. package/dist/types-ts4.5/actions.d.ts +21 -0
  64. package/dist/types-ts4.5/commands.d.ts +29 -0
  65. package/dist/types-ts4.5/edge-cases/index.d.ts +11 -0
  66. package/dist/types-ts4.5/edge-cases/lists.d.ts +18 -0
  67. package/dist/types-ts4.5/handlers.d.ts +55 -0
  68. package/dist/types-ts4.5/index.d.ts +1 -0
  69. package/dist/types-ts4.5/plugin.d.ts +2 -0
  70. package/dist/types-ts4.5/plugins/media.d.ts +23 -0
  71. package/dist/types-ts4.5/pm-plugins/analytics.d.ts +44 -0
  72. package/dist/types-ts4.5/pm-plugins/clipboard-text-serializer.d.ts +13 -0
  73. package/dist/types-ts4.5/pm-plugins/main.d.ts +12 -0
  74. package/dist/types-ts4.5/pm-plugins/plugin-factory.d.ts +3 -0
  75. package/dist/types-ts4.5/reducer.d.ts +3 -0
  76. package/dist/types-ts4.5/util/index.d.ts +21 -0
  77. package/dist/types-ts4.5/util/tinyMCE.d.ts +32 -0
  78. package/package.json +18 -6
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.clipboardTextSerializer = clipboardTextSerializer;
7
+ /**
8
+ * Returns a plain text serialization of a given slice. This is used for populating the plain text
9
+ * section of the clipboard on copy.
10
+ * The current implementation is bare bones - only inlineCards, blockCards and mentions are tested (they
11
+ * previously were empty on plain text copy).
12
+ * Unknown nodes are passed to node.textBetween().
13
+ *
14
+ * By default (without this function passed to the editor), the editor uses
15
+ * `slice.content.textBetween(0, slice.content.size, "\n\n")`
16
+ * (see https://prosemirror.net/docs/ref/#view.EditorProps.clipboardTextSerializer)
17
+ */
18
+ function clipboardTextSerializer(slice) {
19
+ var text = '';
20
+ var blockSeparater = '\n\n';
21
+ slice.content.nodesBetween(0, slice.content.size, function (node) {
22
+ if (node.type.isBlock) {
23
+ text += blockSeparater;
24
+ }
25
+ if (node.type.name === 'paragraph') {
26
+ return true;
27
+ } else if (node.type.name === 'hardBreak') {
28
+ text += '\n';
29
+ } else if (node.type.name === 'text') {
30
+ text += node.text;
31
+ } else if (node.type.name === 'inlineCard') {
32
+ text += node.attrs.url;
33
+ } else if (node.type.name === 'blockCard') {
34
+ text += node.attrs.url;
35
+ } else if (node.type.name === 'mention') {
36
+ text += node.attrs.text;
37
+ } else {
38
+ text += node.textBetween(0, node.content.size, '\n\n');
39
+ }
40
+ return false;
41
+ });
42
+ return text.trim();
43
+ }
@@ -0,0 +1,484 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.createPlugin = createPlugin;
8
+ exports.isInsideBlockQuote = void 0;
9
+ Object.defineProperty(exports, "stateKey", {
10
+ enumerable: true,
11
+ get: function get() {
12
+ return _pluginFactory.pluginKey;
13
+ }
14
+ });
15
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
16
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
17
+ var _uuid = _interopRequireDefault(require("uuid"));
18
+ var _analytics = require("@atlaskit/editor-common/analytics");
19
+ var _card = require("@atlaskit/editor-common/card");
20
+ var _coreUtils = require("@atlaskit/editor-common/core-utils");
21
+ var _extensions = require("@atlaskit/editor-common/extensions");
22
+ var _paste = require("@atlaskit/editor-common/paste");
23
+ var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
24
+ var _transforms = require("@atlaskit/editor-common/transforms");
25
+ var _utils = require("@atlaskit/editor-common/utils");
26
+ var _editorMarkdownTransformer = require("@atlaskit/editor-markdown-transformer");
27
+ var _model = require("@atlaskit/editor-prosemirror/model");
28
+ var _utils2 = require("@atlaskit/editor-prosemirror/utils");
29
+ var _utils3 = require("@atlaskit/editor-tables/utils");
30
+ var _actions = require("../actions");
31
+ var _commands = require("../commands");
32
+ var _handlers = require("../handlers");
33
+ var _media = require("../plugins/media");
34
+ var _util = require("../util");
35
+ var _tinyMCE = require("../util/tinyMCE");
36
+ var _analytics2 = require("./analytics");
37
+ var _clipboardTextSerializer = require("./clipboard-text-serializer");
38
+ var _pluginFactory = require("./plugin-factory");
39
+ var isInsideBlockQuote = exports.isInsideBlockQuote = function isInsideBlockQuote(state) {
40
+ var blockquote = state.schema.nodes.blockquote;
41
+ return (0, _utils2.hasParentNodeOfType)(blockquote)(state.selection);
42
+ };
43
+ var PASTE = 'Editor Paste Plugin Paste Duration';
44
+ function createPlugin(schema, dispatchAnalyticsEvent, dispatch, featureFlags, pluginInjectionApi, cardOptions, sanitizePrivateContent, providerFactory) {
45
+ var _pluginInjectionApi$a;
46
+ var editorAnalyticsAPI = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions;
47
+ var atlassianMarkDownParser = new _editorMarkdownTransformer.MarkdownTransformer(schema, _paste.md);
48
+ function getMarkdownSlice(text, openStart, openEnd) {
49
+ var textInput = escapeBackslashExceptCodeblock(text);
50
+ var doc = atlassianMarkDownParser.parse((0, _util.escapeLinks)(textInput));
51
+ if (doc && doc.content) {
52
+ return new _model.Slice(doc.content, openStart, openEnd);
53
+ }
54
+ return;
55
+ }
56
+ function escapeBackslashExceptCodeblock(textInput) {
57
+ var codeToken = '```';
58
+ if (!textInput.includes(codeToken)) {
59
+ return textInput.replace(/\\/g, '\\\\');
60
+ }
61
+ var isInsideCodeblock = false;
62
+ var textSplitByNewLine = textInput.split('\n');
63
+ // In the splitted array, we traverse through every line and check if it will be parsed as a codeblock.
64
+ textSplitByNewLine = textSplitByNewLine.map(function (text) {
65
+ if (text === codeToken) {
66
+ isInsideCodeblock = !isInsideCodeblock;
67
+ } else if (text.startsWith(codeToken) && isInsideCodeblock === false) {
68
+ // if there is some text after the ``` mark , it gets counted as language attribute only at the start of codeblock
69
+ isInsideCodeblock = true;
70
+ }
71
+ if (!isInsideCodeblock) {
72
+ // only escape text which is not inside a codeblock
73
+ text = text.replace(/\\/g, '\\\\');
74
+ }
75
+ return text;
76
+ });
77
+ textInput = textSplitByNewLine.join('\n');
78
+ return textInput;
79
+ }
80
+ var extensionAutoConverter;
81
+ function setExtensionAutoConverter(_x, _x2) {
82
+ return _setExtensionAutoConverter.apply(this, arguments);
83
+ }
84
+ function _setExtensionAutoConverter() {
85
+ _setExtensionAutoConverter = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(name, extensionProviderPromise) {
86
+ return _regenerator.default.wrap(function _callee$(_context) {
87
+ while (1) switch (_context.prev = _context.next) {
88
+ case 0:
89
+ if (!(name !== 'extensionProvider' || !extensionProviderPromise)) {
90
+ _context.next = 2;
91
+ break;
92
+ }
93
+ return _context.abrupt("return");
94
+ case 2:
95
+ _context.prev = 2;
96
+ _context.next = 5;
97
+ return (0, _extensions.getExtensionAutoConvertersFromProvider)(extensionProviderPromise);
98
+ case 5:
99
+ extensionAutoConverter = _context.sent;
100
+ _context.next = 11;
101
+ break;
102
+ case 8:
103
+ _context.prev = 8;
104
+ _context.t0 = _context["catch"](2);
105
+ // eslint-disable-next-line no-console
106
+ console.error(_context.t0);
107
+ case 11:
108
+ case "end":
109
+ return _context.stop();
110
+ }
111
+ }, _callee, null, [[2, 8]]);
112
+ }));
113
+ return _setExtensionAutoConverter.apply(this, arguments);
114
+ }
115
+ if (providerFactory) {
116
+ providerFactory.subscribe('extensionProvider', setExtensionAutoConverter);
117
+ }
118
+ var mostRecentPasteEvent;
119
+ var pastedFromBitBucket = false;
120
+ return new _safePlugin.SafePlugin({
121
+ key: _pluginFactory.pluginKey,
122
+ state: (0, _pluginFactory.createPluginState)(dispatch, {
123
+ pastedMacroPositions: {},
124
+ lastContentPasted: null
125
+ }),
126
+ props: {
127
+ // For serialising to plain text
128
+ clipboardTextSerializer: _clipboardTextSerializer.clipboardTextSerializer,
129
+ handleDOMEvents: {
130
+ paste: function paste(view, event) {
131
+ mostRecentPasteEvent = event;
132
+ return false;
133
+ }
134
+ },
135
+ handlePaste: function handlePaste(view, rawEvent, slice) {
136
+ var _text, _pluginInjectionApi$a2, _analyticsPlugin$perf, _schema$nodes, _schema$nodes2, _schema$nodes3, _pluginInjectionApi$m;
137
+ var event = rawEvent;
138
+ if (!event.clipboardData) {
139
+ return false;
140
+ }
141
+ var text = event.clipboardData.getData('text/plain');
142
+ var html = event.clipboardData.getData('text/html');
143
+ var uriList = event.clipboardData.getData('text/uri-list');
144
+ // Links copied from iOS Safari share button only have the text/uri-list data type
145
+ // ProseMirror don't do anything with this type so we want to make our own open slice
146
+ // with url as text content so link is pasted inline
147
+ if (uriList && !text && !html) {
148
+ text = uriList;
149
+ slice = new _model.Slice(_model.Fragment.from(schema.text(text)), 1, 1);
150
+ }
151
+ if ((_text = text) !== null && _text !== void 0 && _text.includes('\r')) {
152
+ text = text.replace(/\r/g, '');
153
+ }
154
+ var isPastedFile = (0, _paste.isPastedFile)(event);
155
+ var isPlainText = text && !html;
156
+ var isRichText = !!html;
157
+
158
+ // Bail if copied content has files
159
+ if (isPastedFile) {
160
+ if (!html) {
161
+ /**
162
+ * Microsoft Office, Number, Pages, etc. adds an image to clipboard
163
+ * with other mime-types so we don't let the event reach media.
164
+ * The detection ration here is that if the payload has both `html` and
165
+ * `files`, then it could be one of above or an image copied from web.
166
+ * Here, we don't have html, so we return true to allow default event behaviour
167
+ */
168
+ return true;
169
+ }
170
+
171
+ /**
172
+ * We want to return false for external copied image to allow
173
+ * it to be uploaded by the client.
174
+ */
175
+ if ((0, _util.htmlContainsSingleFile)(html)) {
176
+ return true;
177
+ }
178
+ event.stopImmediatePropagation();
179
+ }
180
+ var state = view.state;
181
+ var analyticsPlugin = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a2 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a2 === void 0 || (_pluginInjectionApi$a2 = _pluginInjectionApi$a2.sharedState) === null || _pluginInjectionApi$a2 === void 0 ? void 0 : _pluginInjectionApi$a2.currentState();
182
+ var pasteTrackingEnabled = analyticsPlugin === null || analyticsPlugin === void 0 || (_analyticsPlugin$perf = analyticsPlugin.performanceTracking) === null || _analyticsPlugin$perf === void 0 || (_analyticsPlugin$perf = _analyticsPlugin$perf.pasteTracking) === null || _analyticsPlugin$perf === void 0 ? void 0 : _analyticsPlugin$perf.enabled;
183
+ if (pasteTrackingEnabled) {
184
+ var content = (0, _analytics2.getContentNodeTypes)(slice.content);
185
+ var pasteId = (0, _uuid.default)();
186
+ var measureName = "".concat(PASTE, "_").concat(pasteId);
187
+ (0, _utils.measureRender)(measureName, function (_ref) {
188
+ var duration = _ref.duration,
189
+ distortedDuration = _ref.distortedDuration;
190
+ var payload = (0, _analytics2.createPasteMeasurePayload)({
191
+ view: view,
192
+ duration: duration,
193
+ content: content,
194
+ distortedDuration: distortedDuration
195
+ });
196
+ if (payload) {
197
+ dispatchAnalyticsEvent(payload);
198
+ }
199
+ });
200
+ }
201
+ // creating a custom dispatch because we want to add a meta whenever we do a paste.
202
+ var dispatch = function dispatch(tr) {
203
+ var _state$doc$resolve$no, _input;
204
+ // https://product-fabric.atlassian.net/browse/ED-12633
205
+ // don't add closeHistory call if we're pasting a text inside placeholder text as we want the whole action
206
+ // to be atomic
207
+ var placeholder = state.schema.nodes.placeholder;
208
+ var isPastingTextInsidePlaceholderText = ((_state$doc$resolve$no = state.doc.resolve(state.selection.$anchor.pos).nodeAfter) === null || _state$doc$resolve$no === void 0 ? void 0 : _state$doc$resolve$no.type) === placeholder;
209
+
210
+ // Don't add closeHistory if we're pasting over layout columns, as we will appendTransaction
211
+ // to cleanup the layout's structure and we want to keep the paste and re-structuring as
212
+ // one event.
213
+ var isPastingOverLayoutColumns = (0, _utils2.hasParentNodeOfType)(state.schema.nodes.layoutColumn)(state.selection);
214
+
215
+ // don't add closeHistory call if we're pasting a table, as some tables may involve additional
216
+ // appendedTransactions to repair them (if they're partial or incomplete) and we don't want
217
+ // to split those repairing transactions in prosemirror-history when they're being added to the
218
+ // "done" stack
219
+ var isPastingTable = tr.steps.some(function (step) {
220
+ var _slice$content;
221
+ var slice = (0, _utils.extractSliceFromStep)(step);
222
+ var tableExists = false;
223
+ slice === null || slice === void 0 || (_slice$content = slice.content) === null || _slice$content === void 0 || _slice$content.forEach(function (node) {
224
+ if (node.type === state.schema.nodes.table) {
225
+ tableExists = true;
226
+ }
227
+ });
228
+ return tableExists;
229
+ });
230
+ if (!isPastingTextInsidePlaceholderText && !isPastingTable && !isPastingOverLayoutColumns && pluginInjectionApi !== null && pluginInjectionApi !== void 0 && pluginInjectionApi.betterTypeHistory) {
231
+ var _pluginInjectionApi$b;
232
+ tr = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$b = pluginInjectionApi.betterTypeHistory) === null || _pluginInjectionApi$b === void 0 ? void 0 : _pluginInjectionApi$b.actions.flagPasteEvent(tr);
233
+ }
234
+ (0, _card.addLinkMetadata)(view.state.selection, tr, {
235
+ action: isPlainText ? _analytics.ACTION.PASTED_AS_PLAIN : _analytics.ACTION.PASTED,
236
+ inputMethod: _analytics.INPUT_METHOD.CLIPBOARD
237
+ });
238
+ var pasteStartPos = Math.min(state.selection.anchor, state.selection.head);
239
+ var pasteEndPos = tr.selection.to;
240
+ var contentPasted = {
241
+ pasteStartPos: pasteStartPos,
242
+ pasteEndPos: pasteEndPos,
243
+ text: text,
244
+ isShiftPressed: Boolean(
245
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
246
+ view.shiftKey || ((_input = view.input) === null || _input === void 0 ? void 0 : _input.shiftKey)),
247
+ isPlainText: Boolean(isPlainText),
248
+ pastedSlice: tr.doc.slice(pasteStartPos, pasteEndPos),
249
+ pastedAt: Date.now(),
250
+ pasteSource: (0, _util.getPasteSource)(event)
251
+ };
252
+ tr.setMeta(_pluginFactory.pluginKey, {
253
+ type: _actions.PastePluginActionTypes.ON_PASTE,
254
+ contentPasted: contentPasted
255
+ });
256
+ view.dispatch(tr);
257
+ };
258
+ slice = (0, _handlers.handleParagraphBlockMarks)(state, slice);
259
+ var plainTextPasteSlice = (0, _utils.linkifyContent)(state.schema)(slice);
260
+ if ((0, _analytics2.handlePasteAsPlainTextWithAnalytics)(editorAnalyticsAPI)(view, event, plainTextPasteSlice)(state, dispatch, view)) {
261
+ return true;
262
+ }
263
+
264
+ // transform slices based on destination
265
+ slice = (0, _media.transformSliceForMedia)(slice, schema)(state.selection);
266
+ var markdownSlice;
267
+ if (isPlainText) {
268
+ var _markdownSlice;
269
+ markdownSlice = getMarkdownSlice(text, slice.openStart, slice.openEnd);
270
+
271
+ // https://product-fabric.atlassian.net/browse/ED-15134
272
+ // Lists are not allowed within Blockquotes at this time. Attempting to
273
+ // paste a markdown list ie. ">- foo" will yeild a markdownSlice of size 0.
274
+ // Rather then blocking the paste action with no UI feedback, this will instead
275
+ // force a "paste as plain text" action by clearing the markdownSlice.
276
+ markdownSlice = !((_markdownSlice = markdownSlice) !== null && _markdownSlice !== void 0 && _markdownSlice.size) ? undefined : markdownSlice;
277
+ if (markdownSlice) {
278
+ var _pluginInjectionApi$c, _pluginInjectionApi$e;
279
+ // linkify text prior to converting to macro
280
+ if ((0, _analytics2.handlePasteLinkOnSelectedTextWithAnalytics)(editorAnalyticsAPI)(view, event, markdownSlice, _analytics.PasteTypes.markdown)(state, dispatch)) {
281
+ return true;
282
+ }
283
+
284
+ // run macro autoconvert prior to other conversions
285
+ if ((0, _handlers.handleMacroAutoConvert)(text, markdownSlice, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c = pluginInjectionApi.card) === null || _pluginInjectionApi$c === void 0 || (_pluginInjectionApi$c = _pluginInjectionApi$c.actions) === null || _pluginInjectionApi$c === void 0 ? void 0 : _pluginInjectionApi$c.queueCardsFromChangedTr, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$e = pluginInjectionApi.extension) === null || _pluginInjectionApi$e === void 0 || (_pluginInjectionApi$e = _pluginInjectionApi$e.actions) === null || _pluginInjectionApi$e === void 0 ? void 0 : _pluginInjectionApi$e.runMacroAutoConvert, cardOptions, extensionAutoConverter)(state, dispatch, view)) {
286
+ // TODO: handleMacroAutoConvert dispatch twice, so we can't use the helper
287
+ (0, _analytics2.sendPasteAnalyticsEvent)(editorAnalyticsAPI)(view, event, markdownSlice, {
288
+ type: _analytics.PasteTypes.markdown
289
+ });
290
+ return true;
291
+ }
292
+ }
293
+ }
294
+ slice = (0, _util.transformUnsupportedBlockCardToInline)(slice, state, cardOptions);
295
+
296
+ // Handles edge case so that when copying text from the top level of the document
297
+ // it can be pasted into nodes like panels/actions/decisions without removing them.
298
+ // Overriding openStart to be 1 when only pasting a paragraph makes the preferred
299
+ // depth favour the text, rather than the paragraph node.
300
+ // https://github.com/ProseMirror/prosemirror-transform/blob/master/src/replace.js#:~:text=Transform.prototype.-,replaceRange,-%3D%20function(from%2C%20to
301
+ var selectionDepth = state.selection.$head.depth;
302
+ var selectionParentNode = state.selection.$head.node(selectionDepth - 1);
303
+ var selectionParentType = selectionParentNode === null || selectionParentNode === void 0 ? void 0 : selectionParentNode.type;
304
+ var edgeCaseNodeTypes = [(_schema$nodes = schema.nodes) === null || _schema$nodes === void 0 ? void 0 : _schema$nodes.panel, (_schema$nodes2 = schema.nodes) === null || _schema$nodes2 === void 0 ? void 0 : _schema$nodes2.taskList, (_schema$nodes3 = schema.nodes) === null || _schema$nodes3 === void 0 ? void 0 : _schema$nodes3.decisionList];
305
+ if (slice.openStart === 0 && slice.openEnd !== 1 && selectionParentNode && edgeCaseNodeTypes.includes(selectionParentType)) {
306
+ // @ts-ignore - [unblock prosemirror bump] assigning to readonly prop
307
+ slice.openStart = 1;
308
+ }
309
+ if ((0, _analytics2.handlePasteIntoTaskAndDecisionWithAnalytics)(view, event, slice, isPlainText ? _analytics.PasteTypes.plain : _analytics.PasteTypes.richText, pluginInjectionApi)(state, dispatch)) {
310
+ return true;
311
+ }
312
+
313
+ // If we're in a code block, append the text contents of clipboard inside it
314
+ if ((0, _analytics2.handleCodeBlockWithAnalytics)(editorAnalyticsAPI)(view, event, slice, text)(state, dispatch)) {
315
+ return true;
316
+ }
317
+ if ((0, _analytics2.handleMediaSingleWithAnalytics)(editorAnalyticsAPI)(view, event, slice, isPastedFile ? _analytics.PasteTypes.binary : _analytics.PasteTypes.richText, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$m = pluginInjectionApi.media) === null || _pluginInjectionApi$m === void 0 ? void 0 : _pluginInjectionApi$m.actions.insertMediaAsMediaSingle)(state, dispatch, view)) {
318
+ return true;
319
+ }
320
+ if ((0, _analytics2.handleSelectedTableWithAnalytics)(editorAnalyticsAPI)(view, event, slice)(state, dispatch)) {
321
+ return true;
322
+ }
323
+
324
+ // If the clipboard only contains plain text, attempt to parse it as Markdown
325
+ if (isPlainText && markdownSlice) {
326
+ if ((0, _analytics2.handlePastePreservingMarksWithAnalytics)(view, event, markdownSlice, _analytics.PasteTypes.markdown, pluginInjectionApi)(state, dispatch)) {
327
+ return true;
328
+ }
329
+ return (0, _analytics2.handleMarkdownWithAnalytics)(view, event, markdownSlice, pluginInjectionApi)(state, dispatch);
330
+ }
331
+ if (isRichText && isInsideBlockQuote(state)) {
332
+ //If pasting inside blockquote
333
+ //Skip the blockquote node and keep remaining nodes as they are
334
+ var blockquote = schema.nodes.blockquote;
335
+ var children = [];
336
+ (0, _utils.mapChildren)(slice.content, function (node) {
337
+ if (node.type === blockquote) {
338
+ for (var i = 0; i < node.childCount; i++) {
339
+ children.push(node.child(i));
340
+ }
341
+ } else {
342
+ children.push(node);
343
+ }
344
+ });
345
+ slice = new _model.Slice(_model.Fragment.fromArray(children), slice.openStart, slice.openEnd);
346
+ }
347
+
348
+ // finally, handle rich-text copy-paste
349
+ if (isRichText) {
350
+ var _pluginInjectionApi$c2, _pluginInjectionApi$e2, _pluginInjectionApi$l;
351
+ // linkify the text where possible
352
+ slice = (0, _utils.linkifyContent)(state.schema)(slice);
353
+ if ((0, _analytics2.handlePasteLinkOnSelectedTextWithAnalytics)(editorAnalyticsAPI)(view, event, slice, _analytics.PasteTypes.richText)(state, dispatch)) {
354
+ return true;
355
+ }
356
+
357
+ // run macro autoconvert prior to other conversions
358
+ if ((0, _handlers.handleMacroAutoConvert)(text, slice, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c2 = pluginInjectionApi.card) === null || _pluginInjectionApi$c2 === void 0 || (_pluginInjectionApi$c2 = _pluginInjectionApi$c2.actions) === null || _pluginInjectionApi$c2 === void 0 ? void 0 : _pluginInjectionApi$c2.queueCardsFromChangedTr, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$e2 = pluginInjectionApi.extension) === null || _pluginInjectionApi$e2 === void 0 || (_pluginInjectionApi$e2 = _pluginInjectionApi$e2.actions) === null || _pluginInjectionApi$e2 === void 0 ? void 0 : _pluginInjectionApi$e2.runMacroAutoConvert, cardOptions, extensionAutoConverter)(state, dispatch, view)) {
359
+ // TODO: handleMacroAutoConvert dispatch twice, so we can't use the helper
360
+ (0, _analytics2.sendPasteAnalyticsEvent)(editorAnalyticsAPI)(view, event, slice, {
361
+ type: _analytics.PasteTypes.richText
362
+ });
363
+ return true;
364
+ }
365
+
366
+ // get editor-tables to handle pasting tables if it can
367
+ // otherwise, just the replace the selection with the content
368
+ if ((0, _utils3.handlePaste)(view, null, slice)) {
369
+ (0, _analytics2.sendPasteAnalyticsEvent)(editorAnalyticsAPI)(view, event, slice, {
370
+ type: _analytics.PasteTypes.richText
371
+ });
372
+ return true;
373
+ }
374
+
375
+ // remove annotation marks from the pasted data if they are not present in the document
376
+ // for the cases when they are pasted from external pages
377
+ if (slice.content.size && (0, _utils.containsAnyAnnotations)(slice, state)) {
378
+ var _pluginInjectionApi$a3;
379
+ pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a3 = pluginInjectionApi.annotation) === null || _pluginInjectionApi$a3 === void 0 || _pluginInjectionApi$a3.actions.stripNonExistingAnnotations(slice, state);
380
+ }
381
+
382
+ // ED-4732
383
+ if ((0, _analytics2.handlePastePreservingMarksWithAnalytics)(view, event, slice, _analytics.PasteTypes.richText, pluginInjectionApi)(state, dispatch)) {
384
+ return true;
385
+ }
386
+
387
+ // Check that we are pasting in a location that does not accept
388
+ // breakout marks, if so we strip the mark and paste. Note that
389
+ // breakout marks are only valid in the root document.
390
+ if (selectionParentType !== state.schema.nodes.doc) {
391
+ var sliceCopy = _model.Slice.fromJSON(state.schema, slice.toJSON() || {});
392
+ sliceCopy.content.descendants(function (node) {
393
+ // @ts-ignore - [unblock prosemirror bump] assigning to readonly prop
394
+ node.marks = node.marks.filter(function (mark) {
395
+ return mark.type.name !== 'breakout';
396
+ });
397
+ // as breakout marks should only be on top level nodes,
398
+ // we don't traverse the entire document
399
+ return false;
400
+ });
401
+ slice = sliceCopy;
402
+ }
403
+ if ((0, _analytics2.handleExpandWithAnalytics)(editorAnalyticsAPI)(view, event, slice)(state, dispatch)) {
404
+ return true;
405
+ }
406
+ if (!(0, _coreUtils.insideTable)(state)) {
407
+ slice = (0, _transforms.transformSliceNestedExpandToExpand)(slice, state.schema);
408
+ }
409
+
410
+ // Create a custom handler to avoid handling with handleRichText method
411
+ // As SafeInsert is used inside handleRichText which caused some bad UX like this:
412
+ // https://product-fabric.atlassian.net/browse/MEX-1520
413
+ if ((0, _analytics2.handlePasteIntoCaptionWithAnalytics)(editorAnalyticsAPI)(view, event, slice, _analytics.PasteTypes.richText)(state, dispatch)) {
414
+ return true;
415
+ }
416
+ if ((0, _analytics2.handlePastePanelOrDecisionIntoListWithAnalytics)(editorAnalyticsAPI)(view, event, slice, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$l = pluginInjectionApi.list) === null || _pluginInjectionApi$l === void 0 ? void 0 : _pluginInjectionApi$l.actions.findRootParentListNode)(state, dispatch)) {
417
+ return true;
418
+ }
419
+ if ((0, _analytics2.handlePasteNonNestableBlockNodesIntoListWithAnalytics)(editorAnalyticsAPI)(view, event, slice)(state, dispatch)) {
420
+ return true;
421
+ }
422
+ return (0, _analytics2.handleRichTextWithAnalytics)(view, event, slice, pluginInjectionApi)(state, dispatch);
423
+ }
424
+ return false;
425
+ },
426
+ transformPasted: function transformPasted(slice) {
427
+ if (sanitizePrivateContent) {
428
+ slice = (0, _handlers.handleMention)(slice, schema);
429
+ }
430
+
431
+ /* Bitbucket copies diffs as multiple adjacent code blocks
432
+ * so we merge ALL adjacent code blocks to support paste here */
433
+ if (pastedFromBitBucket) {
434
+ slice = (0, _transforms.transformSliceToJoinAdjacentCodeBlocks)(slice);
435
+ }
436
+ slice = (0, _transforms.transformSingleLineCodeBlockToCodeMark)(slice, schema);
437
+ slice = (0, _media.transformSliceToCorrectMediaWrapper)(slice, schema);
438
+ slice = (0, _media.transformSliceToMediaSingleWithNewExperience)(slice, schema);
439
+ slice = (0, _transforms.transformSliceToDecisionList)(slice, schema);
440
+
441
+ // splitting linebreaks into paragraphs must happen before upgrading text to lists
442
+ slice = (0, _commands.splitParagraphs)(slice, schema);
443
+ slice = (0, _commands.upgradeTextToLists)(slice, schema);
444
+ if (slice.content.childCount && slice.content.lastChild.type === schema.nodes.codeBlock) {
445
+ slice = new _model.Slice(slice.content, 0, 0);
446
+ }
447
+ return slice;
448
+ },
449
+ transformPastedHTML: function transformPastedHTML(html) {
450
+ // Fix for issue ED-4438
451
+ // text from google docs should not be pasted as inline code
452
+ if (html.indexOf('id="docs-internal-guid-') >= 0) {
453
+ html = html.replace(/white-space:pre/g, '');
454
+ html = html.replace(/white-space:pre-wrap/g, '');
455
+ }
456
+
457
+ // Partial fix for ED-7331: During a copy/paste from the legacy tinyMCE
458
+ // confluence editor, if we encounter an incomplete table (e.g. table elements
459
+ // not wrapped in <table>), we try to rebuild a complete, valid table if possible.
460
+ if (mostRecentPasteEvent && (0, _tinyMCE.isPastedFromTinyMCEConfluence)(mostRecentPasteEvent, html) && (0, _tinyMCE.htmlHasIncompleteTable)(html)) {
461
+ var completeTableHtml = (0, _tinyMCE.tryRebuildCompleteTableHtml)(html);
462
+ if (completeTableHtml) {
463
+ html = completeTableHtml;
464
+ }
465
+ }
466
+ if (!(0, _util.isPastedFromWord)(html) && !(0, _util.isPastedFromExcel)(html) && html.indexOf('<img ') >= 0) {
467
+ html = (0, _media.unwrapNestedMediaElements)(html);
468
+ }
469
+
470
+ // https://product-fabric.atlassian.net/browse/ED-11714
471
+ // Checking for edge case when copying a list item containing links from Notion
472
+ // The html from this case is invalid with duplicate nested links
473
+ if ((0, _util.htmlHasInvalidLinkTags)(html)) {
474
+ html = (0, _util.removeDuplicateInvalidLinks)(html);
475
+ }
476
+
477
+ // Fix for ED-13568: Code blocks being copied/pasted when next to each other get merged
478
+ pastedFromBitBucket = html.indexOf('data-qa="code-line"') >= 0;
479
+ mostRecentPasteEvent = null;
480
+ return html;
481
+ }
482
+ }
483
+ });
484
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.pluginKey = exports.getPluginState = exports.createPluginState = exports.createCommand = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _utils = require("@atlaskit/editor-common/utils");
11
+ var _state = require("@atlaskit/editor-prosemirror/state");
12
+ var _reducer = require("../reducer");
13
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15
+ var pluginKey = exports.pluginKey = new _state.PluginKey('pastePlugin');
16
+ var _pluginFactory = (0, _utils.pluginFactory)(pluginKey, _reducer.reducer, {
17
+ mapping: function mapping(tr, pluginState) {
18
+ if (tr.docChanged) {
19
+ var atLeastOnePositionChanged = false;
20
+ var positionsMappedThroughChanges = Object.entries(pluginState.pastedMacroPositions).reduce(function (acc, _ref) {
21
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
22
+ key = _ref2[0],
23
+ position = _ref2[1];
24
+ var mappedPosition = tr.mapping.map(position);
25
+ if (position !== mappedPosition) {
26
+ atLeastOnePositionChanged = true;
27
+ }
28
+ acc[key] = tr.mapping.map(position);
29
+ return acc;
30
+ }, {});
31
+ if (atLeastOnePositionChanged) {
32
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
33
+ pastedMacroPositions: positionsMappedThroughChanges
34
+ });
35
+ }
36
+ }
37
+ return pluginState;
38
+ }
39
+ }),
40
+ createPluginState = exports.createPluginState = _pluginFactory.createPluginState,
41
+ createCommand = exports.createCommand = _pluginFactory.createCommand,
42
+ getPluginState = exports.getPluginState = _pluginFactory.getPluginState;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.reducer = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _actions = require("./actions");
11
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
12
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
13
+ var reducer = exports.reducer = function reducer(state, action) {
14
+ switch (action.type) {
15
+ case _actions.PastePluginActionTypes.START_TRACKING_PASTED_MACRO_POSITIONS:
16
+ {
17
+ return _objectSpread(_objectSpread({}, state), {}, {
18
+ pastedMacroPositions: _objectSpread(_objectSpread({}, state.pastedMacroPositions), action.pastedMacroPositions)
19
+ });
20
+ }
21
+ case _actions.PastePluginActionTypes.STOP_TRACKING_PASTED_MACRO_POSITIONS:
22
+ {
23
+ var filteredMacroPositions = Object.fromEntries(Object.entries(state.pastedMacroPositions).filter(function (_ref) {
24
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 1),
25
+ key = _ref2[0];
26
+ return !action.pastedMacroPositionKeys.includes(key);
27
+ }));
28
+ return _objectSpread(_objectSpread({}, state), {}, {
29
+ pastedMacroPositions: filteredMacroPositions
30
+ });
31
+ }
32
+ case _actions.PastePluginActionTypes.ON_PASTE:
33
+ {
34
+ return _objectSpread(_objectSpread({}, state), {}, {
35
+ lastContentPasted: action.contentPasted
36
+ });
37
+ }
38
+ default:
39
+ return state;
40
+ }
41
+ };