@atlaskit/editor-plugin-paste 13.2.3 → 13.3.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.
- package/CHANGELOG.md +20 -0
- package/dist/cjs/pm-plugins/util/edge-cases/index.js +32 -6
- package/dist/cjs/pm-plugins/util/handlers.js +27 -6
- package/dist/cjs/pm-plugins/util/index.js +22 -19
- package/dist/es2019/pm-plugins/util/edge-cases/index.js +35 -7
- package/dist/es2019/pm-plugins/util/handlers.js +27 -6
- package/dist/es2019/pm-plugins/util/index.js +23 -19
- package/dist/esm/pm-plugins/util/edge-cases/index.js +33 -7
- package/dist/esm/pm-plugins/util/handlers.js +27 -6
- package/dist/esm/pm-plugins/util/index.js +23 -19
- package/dist/types/pm-plugins/util/edge-cases/index.d.ts +1 -1
- package/package.json +12 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-paste
|
|
2
2
|
|
|
3
|
+
## 13.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`73743eb8b36e6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/73743eb8b36e6) -
|
|
8
|
+
CLeanup prefer static regex violations
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
11
|
+
## 13.3.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [`3f5a55729ccfa`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3f5a55729ccfa) -
|
|
16
|
+
Fix over-nesting (auto-indent) when pasting a task/action into a bullet list item under the
|
|
17
|
+
flexible list schema (EDITOR-6738), behind kill switch platform_editor_flexible_list_kill_switch_1
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
|
|
3
23
|
## 13.2.3
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
|
@@ -13,6 +13,7 @@ var _model = require("@atlaskit/editor-prosemirror/model");
|
|
|
13
13
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
14
14
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
15
15
|
var _utils2 = require("@atlaskit/editor-prosemirror/utils");
|
|
16
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
16
17
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
17
18
|
var _index = require("../index");
|
|
18
19
|
var _lists = require("./lists");
|
|
@@ -139,10 +140,35 @@ function insertSliceForTaskInsideList(_ref5) {
|
|
|
139
140
|
var tr = _ref5.tr,
|
|
140
141
|
slice = _ref5.slice;
|
|
141
142
|
var schema = tr.doc.type.schema;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
|
|
144
|
+
// The corrected insertion path only applies for the flexible list indentation dogfooding audience
|
|
145
|
+
// (`platform_editor_flexible_list_indentation`), and the kill switch protects that audience from
|
|
146
|
+
// regressions. Fall back to the legacy path when the experiment is OFF or the kill switch is ON.
|
|
147
|
+
if (!(0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true) || (0, _platformFeatureFlags.fg)('platform_editor_flexible_list_kill_switch_1')) {
|
|
148
|
+
//To avoid the list being replaced with the tasklist, enclose the slice within a taskItem.
|
|
149
|
+
var selectionBeforeReplace = tr.selection.from;
|
|
150
|
+
tr.replaceSelection(new _model.Slice(_model.Fragment.from(schema.nodes.taskItem.createAndFill()), 0, 0));
|
|
151
|
+
var nextSelection = _state.Selection.near(tr.doc.resolve(selectionBeforeReplace + 1));
|
|
152
|
+
tr.setSelection(nextSelection);
|
|
153
|
+
tr.replaceSelection(slice);
|
|
154
|
+
return tr;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Collapse redundant nested taskList wrappers from the slice context, always keeping a single
|
|
158
|
+
// outermost taskList so the content can be safely inserted as a sibling of the list item's
|
|
159
|
+
// paragraph (a listItem cannot directly contain bare taskItems).
|
|
160
|
+
var taskList = schema.nodes.taskList;
|
|
161
|
+
var content = slice.content;
|
|
162
|
+
while (content.childCount === 1 && ((_content$firstChild = content.firstChild) === null || _content$firstChild === void 0 ? void 0 : _content$firstChild.type) === taskList && ((_content$firstChild$f = content.firstChild.firstChild) === null || _content$firstChild$f === void 0 ? void 0 : _content$firstChild$f.type) === taskList) {
|
|
163
|
+
var _content$firstChild, _content$firstChild$f;
|
|
164
|
+
content = content.firstChild.content;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// safeInsert keeps the task structure intact at the closest schema-valid position, instead of
|
|
168
|
+
// replacing into an empty taskItem which would add an extra taskList level.
|
|
169
|
+
tr = (0, _utils2.safeInsert)(content, tr.selection.$to.pos)(tr);
|
|
170
|
+
updateSelectionAfterReplace({
|
|
171
|
+
tr: tr
|
|
172
|
+
});
|
|
173
|
+
return tr;
|
|
148
174
|
}
|
|
@@ -107,7 +107,7 @@ function handleMention(slice, schema) {
|
|
|
107
107
|
}
|
|
108
108
|
function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
109
109
|
return function (state, dispatch) {
|
|
110
|
-
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con;
|
|
110
|
+
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con, _transformedSlice$con2;
|
|
111
111
|
var schema = state.schema,
|
|
112
112
|
selection = state.tr.selection;
|
|
113
113
|
var codeMark = schema.marks.code,
|
|
@@ -187,9 +187,21 @@ function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
|
187
187
|
// or the slice's first node is a paragraph,
|
|
188
188
|
// then we can replace the selection with our slice.
|
|
189
189
|
var pastingIntoExtendedPanel = selectionIsPanel && panel.validContent(transformedSlice.content);
|
|
190
|
-
|
|
190
|
+
|
|
191
|
+
// A task slice carrying its own open taskList wrapper would, on a plain replaceSelection,
|
|
192
|
+
// nest that wrapper inside the current item and add an extra taskList level. Unwrapping one
|
|
193
|
+
// level keeps the pasted task(s) at the same level. Guarded by a kill switch.
|
|
194
|
+
var isOpenTaskListSlice = ((_transformedSlice$con = transformedSlice.content.firstChild) === null || _transformedSlice$con === void 0 ? void 0 : _transformedSlice$con.type) === taskList && transformedSlice.openStart >= 2 && transformedSlice.openEnd >= 1;
|
|
195
|
+
// Restrict the unwrap to a taskItem destination: its task content is schema-invalid inside a
|
|
196
|
+
// decisionItem (decisionList only accepts decisionItem), so decisionItem must fall through to
|
|
197
|
+
// the normal replaceSelection path.
|
|
198
|
+
var pastingIntoTaskItem = (0, _utils2.hasParentNodeOfType)([taskItem])(selection);
|
|
199
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true) && !(0, _platformFeatureFlags.fg)('platform_editor_flexible_list_kill_switch_1') && isOpenTaskListSlice && pastingIntoTaskItem && !selectionIsPanel) {
|
|
200
|
+
var unwrappedSlice = new _model.Slice(transformedSlice.content.firstChild.content, transformedSlice.openStart - 1, transformedSlice.openEnd - 1);
|
|
201
|
+
tr.replaceSelection(unwrappedSlice).scrollIntoView();
|
|
202
|
+
} else if ((transformedSliceIsValidNode || selectionIsValidNode) && !pastingIntoExtendedPanel && !(transformedSlice.openStart === 1 && transformedSlice.openEnd === 0 ||
|
|
191
203
|
// Whole codeblock node has reverse slice depths.
|
|
192
|
-
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$
|
|
204
|
+
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$con2 = transformedSlice.content.firstChild) === null || _transformedSlice$con2 === void 0 ? void 0 : _transformedSlice$con2.type) === paragraph) {
|
|
193
205
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
194
206
|
} else {
|
|
195
207
|
var isWholeContentSelected = selection.$from.pos === selection.$from.start() && selection.$to.end() === selection.$to.pos;
|
|
@@ -208,14 +220,14 @@ function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
|
208
220
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
209
221
|
}
|
|
210
222
|
} else {
|
|
211
|
-
var _transformedSlice$
|
|
223
|
+
var _transformedSlice$con3;
|
|
212
224
|
if (pastingIntoExtendedPanel && isWholeContentSelected) {
|
|
213
225
|
// if the entire panel content is selected, doing a replaceSelection removes the panel as well. Hence we do delete followed by safeInsert
|
|
214
226
|
tr.delete(selection.$from.pos, selection.$to.pos);
|
|
215
227
|
}
|
|
216
228
|
// This maintains both the selection (destination) and the slice (paste content).
|
|
217
229
|
(0, _utils2.safeInsert)(transformedSlice.content)(tr).scrollIntoView();
|
|
218
|
-
if (((_transformedSlice$
|
|
230
|
+
if (((_transformedSlice$con3 = transformedSlice.content.lastChild) === null || _transformedSlice$con3 === void 0 || (_transformedSlice$con3 = _transformedSlice$con3.type) === null || _transformedSlice$con3 === void 0 ? void 0 : _transformedSlice$con3.name) === 'rule') {
|
|
219
231
|
tr.setSelection(_state.TextSelection.near(tr.doc.resolve(tr.selection.$from.pos + transformedSlice.content.size)));
|
|
220
232
|
} else {
|
|
221
233
|
// safeInsert doesn't set correct cursor position
|
|
@@ -1231,8 +1243,17 @@ function handleRichText(slice, queueCardsFromChangedTr) {
|
|
|
1231
1243
|
});
|
|
1232
1244
|
if ((0, _utils.insideTableCell)(state) && (0, _utils.isInListItem)(state) && (0, _utils2.canInsert)(selection.$from, slice.content) && (0, _utils2.canInsert)(selection.$to, slice.content) || sliceHasList) {
|
|
1233
1245
|
tr.replaceSelection(slice);
|
|
1246
|
+
} else if ((0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true) && !(0, _platformFeatureFlags.fg)('platform_editor_flexible_list_kill_switch_1') && checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1247
|
+
// Corrected task-into-list insertion is gated behind the flexible list indentation
|
|
1248
|
+
// experiment and protected by the kill switch.
|
|
1249
|
+
tr = (0, _edgeCases.insertSliceForTaskInsideList)({
|
|
1250
|
+
tr: tr,
|
|
1251
|
+
slice: slice
|
|
1252
|
+
});
|
|
1234
1253
|
} else if (checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1235
|
-
(
|
|
1254
|
+
// Legacy path (experiment OFF or kill switch ON): preserve the previous over-nesting
|
|
1255
|
+
// behaviour via the function's internal fallback.
|
|
1256
|
+
tr = (0, _edgeCases.insertSliceForTaskInsideList)({
|
|
1236
1257
|
tr: tr,
|
|
1237
1258
|
slice: slice
|
|
1238
1259
|
});
|
|
@@ -29,6 +29,23 @@ var _utils2 = require("@atlaskit/editor-prosemirror/utils");
|
|
|
29
29
|
var _utils3 = require("@atlaskit/editor-tables/utils");
|
|
30
30
|
var _mediaClient = require("@atlaskit/media-client");
|
|
31
31
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
32
|
+
// Ignored via go/ees005
|
|
33
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
34
|
+
var HTML_CONTAINS_SINGLE_FILE_REGEX = /<img .*>/;
|
|
35
|
+
// Ignored via go/ees005
|
|
36
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
37
|
+
var ESCAPE_LINKS_INNER_REGEX = /^(https?|ftp|jamfselfservice):\/\/[^\s>"]+$/;
|
|
38
|
+
// Allows up to 3 leading spaces before ``` and optional trailing characters
|
|
39
|
+
// Ignored via go/ees005
|
|
40
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
41
|
+
var OPENING_CODE_FENCE_REGEX = /^( {0,3})```.*$/;
|
|
42
|
+
// Allows up to 3 leading spaces before ``` and optional trailing spaces or tabs
|
|
43
|
+
// Ignored via go/ees005
|
|
44
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
45
|
+
var CLOSING_CODE_FENCE_REGEX = /^( {0,3})```[ ]*$/;
|
|
46
|
+
// Ignored via go/ees005
|
|
47
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
48
|
+
var SPLIT_ON_ANCHOR_REGEX = /(?=<a)/;
|
|
32
49
|
function isPastedFromWord(html) {
|
|
33
50
|
return !!html && html.indexOf('urn:schemas-microsoft-com:office:word') >= 0;
|
|
34
51
|
}
|
|
@@ -63,9 +80,7 @@ var isSingleLine = exports.isSingleLine = function isSingleLine(text) {
|
|
|
63
80
|
return !!text && text.trim().split('\n').length === 1;
|
|
64
81
|
};
|
|
65
82
|
function htmlContainsSingleFile(html) {
|
|
66
|
-
|
|
67
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
68
|
-
return !!html.match(/<img .*>/) && !(0, _mediaClient.isMediaBlobUrl)(html);
|
|
83
|
+
return !!html.match(HTML_CONTAINS_SINGLE_FILE_REGEX) && !(0, _mediaClient.isMediaBlobUrl)(html);
|
|
69
84
|
}
|
|
70
85
|
function getPasteSource(event) {
|
|
71
86
|
// Ignored via go/ees005
|
|
@@ -110,9 +125,7 @@ function escapeLinks(text) {
|
|
|
110
125
|
// Ignored via go/ees005
|
|
111
126
|
// eslint-disable-next-line require-unicode-regexp
|
|
112
127
|
return text.replace(/(\[([^\]]+)\]\()?((https?|ftp|jamfselfservice):\/\/[^\s>"]+)/g, function (str) {
|
|
113
|
-
|
|
114
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
115
|
-
return str.match(/^(https?|ftp|jamfselfservice):\/\/[^\s>"]+$/) ? "<".concat(str, ">") : str;
|
|
128
|
+
return str.match(ESCAPE_LINKS_INNER_REGEX) ? "<".concat(str, ">") : str;
|
|
116
129
|
});
|
|
117
130
|
}
|
|
118
131
|
|
|
@@ -127,23 +140,15 @@ function escapeLinks(text) {
|
|
|
127
140
|
*/
|
|
128
141
|
function escapeBackslashAndLinksExceptCodeBlock(textInput) {
|
|
129
142
|
// ref: https://spec.commonmark.org/0.31.2/#fenced-code-blocks
|
|
130
|
-
// Allows up to 3 leading spaces before ``` and optional trailing characters
|
|
131
|
-
// Ignored via go/ees005
|
|
132
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
133
|
-
var openingCodeFenceRegex = /^( {0,3})```.*$/;
|
|
134
|
-
// Allows up to 3 leading spaces before ``` and optional trailing spaces or tabs
|
|
135
|
-
// Ignored via go/ees005
|
|
136
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
137
|
-
var closingCodeFenceRegex = /^( {0,3})```[ \t]*$/;
|
|
138
143
|
var isInsideCodeBlock = false;
|
|
139
144
|
var lines = textInput.split('\n');
|
|
140
145
|
// In the splitted array, we traverse through every line and check if it will be parsed as a codeblock.
|
|
141
146
|
return lines.map(function (line) {
|
|
142
|
-
if (!isInsideCodeBlock &&
|
|
147
|
+
if (!isInsideCodeBlock && OPENING_CODE_FENCE_REGEX.test(line)) {
|
|
143
148
|
isInsideCodeBlock = true;
|
|
144
149
|
return line;
|
|
145
150
|
}
|
|
146
|
-
if (isInsideCodeBlock &&
|
|
151
|
+
if (isInsideCodeBlock && CLOSING_CODE_FENCE_REGEX.test(line)) {
|
|
147
152
|
isInsideCodeBlock = false;
|
|
148
153
|
return line;
|
|
149
154
|
}
|
|
@@ -244,9 +249,7 @@ var htmlHasInvalidLinkTags = exports.htmlHasInvalidLinkTags = function htmlHasIn
|
|
|
244
249
|
// <li><a href="http://www.atlassian.com\"<a> href="http://www.atlassian.com\"http://www.atlassian.com</a></a></li>">
|
|
245
250
|
var removeDuplicateInvalidLinks = exports.removeDuplicateInvalidLinks = function removeDuplicateInvalidLinks(html) {
|
|
246
251
|
if (htmlHasInvalidLinkTags(html)) {
|
|
247
|
-
|
|
248
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
249
|
-
var htmlArray = html.split(/(?=<a)/);
|
|
252
|
+
var htmlArray = html.split(SPLIT_ON_ANCHOR_REGEX);
|
|
250
253
|
var htmlArrayWithoutInvalidLinks = htmlArray.filter(function (item) {
|
|
251
254
|
return !(item.includes('<a') && item.includes('"></a>')) && !(item.includes('<a') && !item.includes('</a>'));
|
|
252
255
|
});
|
|
@@ -3,7 +3,8 @@ import { isListNode } from '@atlaskit/editor-common/utils';
|
|
|
3
3
|
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
4
4
|
import { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
5
5
|
import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
6
|
-
import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
6
|
+
import { findParentNodeOfType, safeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
7
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
8
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
9
|
import { isCursorSelectionAtTextStartOrEnd, isEmptyNode, isSelectionInsidePanel } from '../index';
|
|
9
10
|
import { insertSliceAtNodeEdge, insertSliceInsideOfPanelNodeSelected, insertSliceIntoEmptyNode, insertSliceIntoRangeSelectionInsideList } from './lists';
|
|
@@ -140,10 +141,37 @@ export function insertSliceForTaskInsideList({
|
|
|
140
141
|
const {
|
|
141
142
|
schema
|
|
142
143
|
} = tr.doc.type;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
144
|
+
|
|
145
|
+
// The corrected insertion path only applies for the flexible list indentation dogfooding audience
|
|
146
|
+
// (`platform_editor_flexible_list_indentation`), and the kill switch protects that audience from
|
|
147
|
+
// regressions. Fall back to the legacy path when the experiment is OFF or the kill switch is ON.
|
|
148
|
+
if (!expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) || fg('platform_editor_flexible_list_kill_switch_1')) {
|
|
149
|
+
//To avoid the list being replaced with the tasklist, enclose the slice within a taskItem.
|
|
150
|
+
const selectionBeforeReplace = tr.selection.from;
|
|
151
|
+
tr.replaceSelection(new Slice(Fragment.from(schema.nodes.taskItem.createAndFill()), 0, 0));
|
|
152
|
+
const nextSelection = Selection.near(tr.doc.resolve(selectionBeforeReplace + 1));
|
|
153
|
+
tr.setSelection(nextSelection);
|
|
154
|
+
tr.replaceSelection(slice);
|
|
155
|
+
return tr;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Collapse redundant nested taskList wrappers from the slice context, always keeping a single
|
|
159
|
+
// outermost taskList so the content can be safely inserted as a sibling of the list item's
|
|
160
|
+
// paragraph (a listItem cannot directly contain bare taskItems).
|
|
161
|
+
const {
|
|
162
|
+
taskList
|
|
163
|
+
} = schema.nodes;
|
|
164
|
+
let content = slice.content;
|
|
165
|
+
while (content.childCount === 1 && ((_content$firstChild = content.firstChild) === null || _content$firstChild === void 0 ? void 0 : _content$firstChild.type) === taskList && ((_content$firstChild$f = content.firstChild.firstChild) === null || _content$firstChild$f === void 0 ? void 0 : _content$firstChild$f.type) === taskList) {
|
|
166
|
+
var _content$firstChild, _content$firstChild$f;
|
|
167
|
+
content = content.firstChild.content;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// safeInsert keeps the task structure intact at the closest schema-valid position, instead of
|
|
171
|
+
// replacing into an empty taskItem which would add an extra taskList level.
|
|
172
|
+
tr = safeInsert(content, tr.selection.$to.pos)(tr);
|
|
173
|
+
updateSelectionAfterReplace({
|
|
174
|
+
tr
|
|
175
|
+
});
|
|
176
|
+
return tr;
|
|
149
177
|
}
|
|
@@ -67,7 +67,7 @@ export function handleMention(slice, schema) {
|
|
|
67
67
|
}
|
|
68
68
|
export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
69
69
|
return (state, dispatch) => {
|
|
70
|
-
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con;
|
|
70
|
+
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con, _transformedSlice$con2;
|
|
71
71
|
const {
|
|
72
72
|
schema,
|
|
73
73
|
tr: {
|
|
@@ -154,9 +154,21 @@ export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChange
|
|
|
154
154
|
// or the slice's first node is a paragraph,
|
|
155
155
|
// then we can replace the selection with our slice.
|
|
156
156
|
const pastingIntoExtendedPanel = selectionIsPanel && panel.validContent(transformedSlice.content);
|
|
157
|
-
|
|
157
|
+
|
|
158
|
+
// A task slice carrying its own open taskList wrapper would, on a plain replaceSelection,
|
|
159
|
+
// nest that wrapper inside the current item and add an extra taskList level. Unwrapping one
|
|
160
|
+
// level keeps the pasted task(s) at the same level. Guarded by a kill switch.
|
|
161
|
+
const isOpenTaskListSlice = ((_transformedSlice$con = transformedSlice.content.firstChild) === null || _transformedSlice$con === void 0 ? void 0 : _transformedSlice$con.type) === taskList && transformedSlice.openStart >= 2 && transformedSlice.openEnd >= 1;
|
|
162
|
+
// Restrict the unwrap to a taskItem destination: its task content is schema-invalid inside a
|
|
163
|
+
// decisionItem (decisionList only accepts decisionItem), so decisionItem must fall through to
|
|
164
|
+
// the normal replaceSelection path.
|
|
165
|
+
const pastingIntoTaskItem = hasParentNodeOfType([taskItem])(selection);
|
|
166
|
+
if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) && !fg('platform_editor_flexible_list_kill_switch_1') && isOpenTaskListSlice && pastingIntoTaskItem && !selectionIsPanel) {
|
|
167
|
+
const unwrappedSlice = new Slice(transformedSlice.content.firstChild.content, transformedSlice.openStart - 1, transformedSlice.openEnd - 1);
|
|
168
|
+
tr.replaceSelection(unwrappedSlice).scrollIntoView();
|
|
169
|
+
} else if ((transformedSliceIsValidNode || selectionIsValidNode) && !pastingIntoExtendedPanel && !(transformedSlice.openStart === 1 && transformedSlice.openEnd === 0 ||
|
|
158
170
|
// Whole codeblock node has reverse slice depths.
|
|
159
|
-
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$
|
|
171
|
+
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$con2 = transformedSlice.content.firstChild) === null || _transformedSlice$con2 === void 0 ? void 0 : _transformedSlice$con2.type) === paragraph) {
|
|
160
172
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
161
173
|
} else {
|
|
162
174
|
const isWholeContentSelected = selection.$from.pos === selection.$from.start() && selection.$to.end() === selection.$to.pos;
|
|
@@ -175,14 +187,14 @@ export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChange
|
|
|
175
187
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
176
188
|
}
|
|
177
189
|
} else {
|
|
178
|
-
var _transformedSlice$
|
|
190
|
+
var _transformedSlice$con3, _transformedSlice$con4;
|
|
179
191
|
if (pastingIntoExtendedPanel && isWholeContentSelected) {
|
|
180
192
|
// if the entire panel content is selected, doing a replaceSelection removes the panel as well. Hence we do delete followed by safeInsert
|
|
181
193
|
tr.delete(selection.$from.pos, selection.$to.pos);
|
|
182
194
|
}
|
|
183
195
|
// This maintains both the selection (destination) and the slice (paste content).
|
|
184
196
|
safeInsert(transformedSlice.content)(tr).scrollIntoView();
|
|
185
|
-
if (((_transformedSlice$
|
|
197
|
+
if (((_transformedSlice$con3 = transformedSlice.content.lastChild) === null || _transformedSlice$con3 === void 0 ? void 0 : (_transformedSlice$con4 = _transformedSlice$con3.type) === null || _transformedSlice$con4 === void 0 ? void 0 : _transformedSlice$con4.name) === 'rule') {
|
|
186
198
|
tr.setSelection(TextSelection.near(tr.doc.resolve(tr.selection.$from.pos + transformedSlice.content.size)));
|
|
187
199
|
} else {
|
|
188
200
|
// safeInsert doesn't set correct cursor position
|
|
@@ -1206,8 +1218,17 @@ export function handleRichText(slice, queueCardsFromChangedTr) {
|
|
|
1206
1218
|
});
|
|
1207
1219
|
if (insideTableCell(state) && isInListItem(state) && canInsert(selection.$from, slice.content) && canInsert(selection.$to, slice.content) || sliceHasList) {
|
|
1208
1220
|
tr.replaceSelection(slice);
|
|
1221
|
+
} else if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) && !fg('platform_editor_flexible_list_kill_switch_1') && checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1222
|
+
// Corrected task-into-list insertion is gated behind the flexible list indentation
|
|
1223
|
+
// experiment and protected by the kill switch.
|
|
1224
|
+
tr = insertSliceForTaskInsideList({
|
|
1225
|
+
tr,
|
|
1226
|
+
slice
|
|
1227
|
+
});
|
|
1209
1228
|
} else if (checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1210
|
-
|
|
1229
|
+
// Legacy path (experiment OFF or kill switch ON): preserve the previous over-nesting
|
|
1230
|
+
// behaviour via the function's internal fallback.
|
|
1231
|
+
tr = insertSliceForTaskInsideList({
|
|
1211
1232
|
tr,
|
|
1212
1233
|
slice
|
|
1213
1234
|
});
|
|
@@ -7,6 +7,24 @@ import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
|
7
7
|
import { getSelectedTableInfo, isTableSelected } from '@atlaskit/editor-tables/utils';
|
|
8
8
|
import { isMediaBlobUrl } from '@atlaskit/media-client';
|
|
9
9
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
10
|
+
|
|
11
|
+
// Ignored via go/ees005
|
|
12
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
13
|
+
const HTML_CONTAINS_SINGLE_FILE_REGEX = /<img .*>/;
|
|
14
|
+
// Ignored via go/ees005
|
|
15
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
16
|
+
const ESCAPE_LINKS_INNER_REGEX = /^(https?|ftp|jamfselfservice):\/\/[^\s>"]+$/;
|
|
17
|
+
// Allows up to 3 leading spaces before ``` and optional trailing characters
|
|
18
|
+
// Ignored via go/ees005
|
|
19
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
20
|
+
const OPENING_CODE_FENCE_REGEX = /^( {0,3})```.*$/;
|
|
21
|
+
// Allows up to 3 leading spaces before ``` and optional trailing spaces or tabs
|
|
22
|
+
// Ignored via go/ees005
|
|
23
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
24
|
+
const CLOSING_CODE_FENCE_REGEX = /^( {0,3})```[ ]*$/;
|
|
25
|
+
// Ignored via go/ees005
|
|
26
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
27
|
+
const SPLIT_ON_ANCHOR_REGEX = /(?=<a)/;
|
|
10
28
|
export function isPastedFromWord(html) {
|
|
11
29
|
return !!html && html.indexOf('urn:schemas-microsoft-com:office:word') >= 0;
|
|
12
30
|
}
|
|
@@ -41,9 +59,7 @@ export const isSingleLine = text => {
|
|
|
41
59
|
return !!text && text.trim().split('\n').length === 1;
|
|
42
60
|
};
|
|
43
61
|
export function htmlContainsSingleFile(html) {
|
|
44
|
-
|
|
45
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
46
|
-
return !!html.match(/<img .*>/) && !isMediaBlobUrl(html);
|
|
62
|
+
return !!html.match(HTML_CONTAINS_SINGLE_FILE_REGEX) && !isMediaBlobUrl(html);
|
|
47
63
|
}
|
|
48
64
|
export function getPasteSource(event) {
|
|
49
65
|
// Ignored via go/ees005
|
|
@@ -88,9 +104,7 @@ export function escapeLinks(text) {
|
|
|
88
104
|
// Ignored via go/ees005
|
|
89
105
|
// eslint-disable-next-line require-unicode-regexp
|
|
90
106
|
return text.replace(/(\[([^\]]+)\]\()?((https?|ftp|jamfselfservice):\/\/[^\s>"]+)/g, str => {
|
|
91
|
-
|
|
92
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
93
|
-
return str.match(/^(https?|ftp|jamfselfservice):\/\/[^\s>"]+$/) ? `<${str}>` : str;
|
|
107
|
+
return str.match(ESCAPE_LINKS_INNER_REGEX) ? `<${str}>` : str;
|
|
94
108
|
});
|
|
95
109
|
}
|
|
96
110
|
|
|
@@ -105,23 +119,15 @@ export function escapeLinks(text) {
|
|
|
105
119
|
*/
|
|
106
120
|
export function escapeBackslashAndLinksExceptCodeBlock(textInput) {
|
|
107
121
|
// ref: https://spec.commonmark.org/0.31.2/#fenced-code-blocks
|
|
108
|
-
// Allows up to 3 leading spaces before ``` and optional trailing characters
|
|
109
|
-
// Ignored via go/ees005
|
|
110
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
111
|
-
const openingCodeFenceRegex = /^( {0,3})```.*$/;
|
|
112
|
-
// Allows up to 3 leading spaces before ``` and optional trailing spaces or tabs
|
|
113
|
-
// Ignored via go/ees005
|
|
114
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
115
|
-
const closingCodeFenceRegex = /^( {0,3})```[ \t]*$/;
|
|
116
122
|
let isInsideCodeBlock = false;
|
|
117
123
|
const lines = textInput.split('\n');
|
|
118
124
|
// In the splitted array, we traverse through every line and check if it will be parsed as a codeblock.
|
|
119
125
|
return lines.map(line => {
|
|
120
|
-
if (!isInsideCodeBlock &&
|
|
126
|
+
if (!isInsideCodeBlock && OPENING_CODE_FENCE_REGEX.test(line)) {
|
|
121
127
|
isInsideCodeBlock = true;
|
|
122
128
|
return line;
|
|
123
129
|
}
|
|
124
|
-
if (isInsideCodeBlock &&
|
|
130
|
+
if (isInsideCodeBlock && CLOSING_CODE_FENCE_REGEX.test(line)) {
|
|
125
131
|
isInsideCodeBlock = false;
|
|
126
132
|
return line;
|
|
127
133
|
}
|
|
@@ -234,9 +240,7 @@ export const htmlHasInvalidLinkTags = html => {
|
|
|
234
240
|
// <li><a href="http://www.atlassian.com\"<a> href="http://www.atlassian.com\"http://www.atlassian.com</a></a></li>">
|
|
235
241
|
export const removeDuplicateInvalidLinks = html => {
|
|
236
242
|
if (htmlHasInvalidLinkTags(html)) {
|
|
237
|
-
|
|
238
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
239
|
-
const htmlArray = html.split(/(?=<a)/);
|
|
243
|
+
const htmlArray = html.split(SPLIT_ON_ANCHOR_REGEX);
|
|
240
244
|
const htmlArrayWithoutInvalidLinks = htmlArray.filter(item => {
|
|
241
245
|
return !(item.includes('<a') && item.includes('"></a>')) && !(item.includes('<a') && !item.includes('</a>'));
|
|
242
246
|
});
|
|
@@ -3,7 +3,8 @@ import { isListNode } from '@atlaskit/editor-common/utils';
|
|
|
3
3
|
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
4
4
|
import { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
5
5
|
import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
6
|
-
import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
6
|
+
import { findParentNodeOfType, safeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
7
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
8
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
9
|
import { isCursorSelectionAtTextStartOrEnd, isEmptyNode, isSelectionInsidePanel } from '../index';
|
|
9
10
|
import { insertSliceAtNodeEdge, insertSliceInsideOfPanelNodeSelected, insertSliceIntoEmptyNode, insertSliceIntoRangeSelectionInsideList } from './lists';
|
|
@@ -130,10 +131,35 @@ export function insertSliceForTaskInsideList(_ref5) {
|
|
|
130
131
|
var tr = _ref5.tr,
|
|
131
132
|
slice = _ref5.slice;
|
|
132
133
|
var schema = tr.doc.type.schema;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
|
|
135
|
+
// The corrected insertion path only applies for the flexible list indentation dogfooding audience
|
|
136
|
+
// (`platform_editor_flexible_list_indentation`), and the kill switch protects that audience from
|
|
137
|
+
// regressions. Fall back to the legacy path when the experiment is OFF or the kill switch is ON.
|
|
138
|
+
if (!expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) || fg('platform_editor_flexible_list_kill_switch_1')) {
|
|
139
|
+
//To avoid the list being replaced with the tasklist, enclose the slice within a taskItem.
|
|
140
|
+
var selectionBeforeReplace = tr.selection.from;
|
|
141
|
+
tr.replaceSelection(new Slice(Fragment.from(schema.nodes.taskItem.createAndFill()), 0, 0));
|
|
142
|
+
var nextSelection = Selection.near(tr.doc.resolve(selectionBeforeReplace + 1));
|
|
143
|
+
tr.setSelection(nextSelection);
|
|
144
|
+
tr.replaceSelection(slice);
|
|
145
|
+
return tr;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Collapse redundant nested taskList wrappers from the slice context, always keeping a single
|
|
149
|
+
// outermost taskList so the content can be safely inserted as a sibling of the list item's
|
|
150
|
+
// paragraph (a listItem cannot directly contain bare taskItems).
|
|
151
|
+
var taskList = schema.nodes.taskList;
|
|
152
|
+
var content = slice.content;
|
|
153
|
+
while (content.childCount === 1 && ((_content$firstChild = content.firstChild) === null || _content$firstChild === void 0 ? void 0 : _content$firstChild.type) === taskList && ((_content$firstChild$f = content.firstChild.firstChild) === null || _content$firstChild$f === void 0 ? void 0 : _content$firstChild$f.type) === taskList) {
|
|
154
|
+
var _content$firstChild, _content$firstChild$f;
|
|
155
|
+
content = content.firstChild.content;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// safeInsert keeps the task structure intact at the closest schema-valid position, instead of
|
|
159
|
+
// replacing into an empty taskItem which would add an extra taskList level.
|
|
160
|
+
tr = safeInsert(content, tr.selection.$to.pos)(tr);
|
|
161
|
+
updateSelectionAfterReplace({
|
|
162
|
+
tr: tr
|
|
163
|
+
});
|
|
164
|
+
return tr;
|
|
139
165
|
}
|
|
@@ -78,7 +78,7 @@ export function handleMention(slice, schema) {
|
|
|
78
78
|
}
|
|
79
79
|
export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
80
80
|
return function (state, dispatch) {
|
|
81
|
-
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con;
|
|
81
|
+
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con, _transformedSlice$con2;
|
|
82
82
|
var schema = state.schema,
|
|
83
83
|
selection = state.tr.selection;
|
|
84
84
|
var codeMark = schema.marks.code,
|
|
@@ -158,9 +158,21 @@ export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChange
|
|
|
158
158
|
// or the slice's first node is a paragraph,
|
|
159
159
|
// then we can replace the selection with our slice.
|
|
160
160
|
var pastingIntoExtendedPanel = selectionIsPanel && panel.validContent(transformedSlice.content);
|
|
161
|
-
|
|
161
|
+
|
|
162
|
+
// A task slice carrying its own open taskList wrapper would, on a plain replaceSelection,
|
|
163
|
+
// nest that wrapper inside the current item and add an extra taskList level. Unwrapping one
|
|
164
|
+
// level keeps the pasted task(s) at the same level. Guarded by a kill switch.
|
|
165
|
+
var isOpenTaskListSlice = ((_transformedSlice$con = transformedSlice.content.firstChild) === null || _transformedSlice$con === void 0 ? void 0 : _transformedSlice$con.type) === taskList && transformedSlice.openStart >= 2 && transformedSlice.openEnd >= 1;
|
|
166
|
+
// Restrict the unwrap to a taskItem destination: its task content is schema-invalid inside a
|
|
167
|
+
// decisionItem (decisionList only accepts decisionItem), so decisionItem must fall through to
|
|
168
|
+
// the normal replaceSelection path.
|
|
169
|
+
var pastingIntoTaskItem = hasParentNodeOfType([taskItem])(selection);
|
|
170
|
+
if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) && !fg('platform_editor_flexible_list_kill_switch_1') && isOpenTaskListSlice && pastingIntoTaskItem && !selectionIsPanel) {
|
|
171
|
+
var unwrappedSlice = new Slice(transformedSlice.content.firstChild.content, transformedSlice.openStart - 1, transformedSlice.openEnd - 1);
|
|
172
|
+
tr.replaceSelection(unwrappedSlice).scrollIntoView();
|
|
173
|
+
} else if ((transformedSliceIsValidNode || selectionIsValidNode) && !pastingIntoExtendedPanel && !(transformedSlice.openStart === 1 && transformedSlice.openEnd === 0 ||
|
|
162
174
|
// Whole codeblock node has reverse slice depths.
|
|
163
|
-
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$
|
|
175
|
+
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$con2 = transformedSlice.content.firstChild) === null || _transformedSlice$con2 === void 0 ? void 0 : _transformedSlice$con2.type) === paragraph) {
|
|
164
176
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
165
177
|
} else {
|
|
166
178
|
var isWholeContentSelected = selection.$from.pos === selection.$from.start() && selection.$to.end() === selection.$to.pos;
|
|
@@ -179,14 +191,14 @@ export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChange
|
|
|
179
191
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
180
192
|
}
|
|
181
193
|
} else {
|
|
182
|
-
var _transformedSlice$
|
|
194
|
+
var _transformedSlice$con3;
|
|
183
195
|
if (pastingIntoExtendedPanel && isWholeContentSelected) {
|
|
184
196
|
// if the entire panel content is selected, doing a replaceSelection removes the panel as well. Hence we do delete followed by safeInsert
|
|
185
197
|
tr.delete(selection.$from.pos, selection.$to.pos);
|
|
186
198
|
}
|
|
187
199
|
// This maintains both the selection (destination) and the slice (paste content).
|
|
188
200
|
safeInsert(transformedSlice.content)(tr).scrollIntoView();
|
|
189
|
-
if (((_transformedSlice$
|
|
201
|
+
if (((_transformedSlice$con3 = transformedSlice.content.lastChild) === null || _transformedSlice$con3 === void 0 || (_transformedSlice$con3 = _transformedSlice$con3.type) === null || _transformedSlice$con3 === void 0 ? void 0 : _transformedSlice$con3.name) === 'rule') {
|
|
190
202
|
tr.setSelection(TextSelection.near(tr.doc.resolve(tr.selection.$from.pos + transformedSlice.content.size)));
|
|
191
203
|
} else {
|
|
192
204
|
// safeInsert doesn't set correct cursor position
|
|
@@ -1202,8 +1214,17 @@ export function handleRichText(slice, queueCardsFromChangedTr) {
|
|
|
1202
1214
|
});
|
|
1203
1215
|
if (insideTableCell(state) && isInListItem(state) && canInsert(selection.$from, slice.content) && canInsert(selection.$to, slice.content) || sliceHasList) {
|
|
1204
1216
|
tr.replaceSelection(slice);
|
|
1217
|
+
} else if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) && !fg('platform_editor_flexible_list_kill_switch_1') && checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1218
|
+
// Corrected task-into-list insertion is gated behind the flexible list indentation
|
|
1219
|
+
// experiment and protected by the kill switch.
|
|
1220
|
+
tr = insertSliceForTaskInsideList({
|
|
1221
|
+
tr: tr,
|
|
1222
|
+
slice: slice
|
|
1223
|
+
});
|
|
1205
1224
|
} else if (checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1206
|
-
|
|
1225
|
+
// Legacy path (experiment OFF or kill switch ON): preserve the previous over-nesting
|
|
1226
|
+
// behaviour via the function's internal fallback.
|
|
1227
|
+
tr = insertSliceForTaskInsideList({
|
|
1207
1228
|
tr: tr,
|
|
1208
1229
|
slice: slice
|
|
1209
1230
|
});
|
|
@@ -8,6 +8,24 @@ import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
|
8
8
|
import { getSelectedTableInfo, isTableSelected } from '@atlaskit/editor-tables/utils';
|
|
9
9
|
import { isMediaBlobUrl } from '@atlaskit/media-client';
|
|
10
10
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
11
|
+
|
|
12
|
+
// Ignored via go/ees005
|
|
13
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
14
|
+
var HTML_CONTAINS_SINGLE_FILE_REGEX = /<img .*>/;
|
|
15
|
+
// Ignored via go/ees005
|
|
16
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
17
|
+
var ESCAPE_LINKS_INNER_REGEX = /^(https?|ftp|jamfselfservice):\/\/[^\s>"]+$/;
|
|
18
|
+
// Allows up to 3 leading spaces before ``` and optional trailing characters
|
|
19
|
+
// Ignored via go/ees005
|
|
20
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
21
|
+
var OPENING_CODE_FENCE_REGEX = /^( {0,3})```.*$/;
|
|
22
|
+
// Allows up to 3 leading spaces before ``` and optional trailing spaces or tabs
|
|
23
|
+
// Ignored via go/ees005
|
|
24
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
25
|
+
var CLOSING_CODE_FENCE_REGEX = /^( {0,3})```[ ]*$/;
|
|
26
|
+
// Ignored via go/ees005
|
|
27
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
28
|
+
var SPLIT_ON_ANCHOR_REGEX = /(?=<a)/;
|
|
11
29
|
export function isPastedFromWord(html) {
|
|
12
30
|
return !!html && html.indexOf('urn:schemas-microsoft-com:office:word') >= 0;
|
|
13
31
|
}
|
|
@@ -42,9 +60,7 @@ export var isSingleLine = function isSingleLine(text) {
|
|
|
42
60
|
return !!text && text.trim().split('\n').length === 1;
|
|
43
61
|
};
|
|
44
62
|
export function htmlContainsSingleFile(html) {
|
|
45
|
-
|
|
46
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
47
|
-
return !!html.match(/<img .*>/) && !isMediaBlobUrl(html);
|
|
63
|
+
return !!html.match(HTML_CONTAINS_SINGLE_FILE_REGEX) && !isMediaBlobUrl(html);
|
|
48
64
|
}
|
|
49
65
|
export function getPasteSource(event) {
|
|
50
66
|
// Ignored via go/ees005
|
|
@@ -89,9 +105,7 @@ export function escapeLinks(text) {
|
|
|
89
105
|
// Ignored via go/ees005
|
|
90
106
|
// eslint-disable-next-line require-unicode-regexp
|
|
91
107
|
return text.replace(/(\[([^\]]+)\]\()?((https?|ftp|jamfselfservice):\/\/[^\s>"]+)/g, function (str) {
|
|
92
|
-
|
|
93
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
94
|
-
return str.match(/^(https?|ftp|jamfselfservice):\/\/[^\s>"]+$/) ? "<".concat(str, ">") : str;
|
|
108
|
+
return str.match(ESCAPE_LINKS_INNER_REGEX) ? "<".concat(str, ">") : str;
|
|
95
109
|
});
|
|
96
110
|
}
|
|
97
111
|
|
|
@@ -106,23 +120,15 @@ export function escapeLinks(text) {
|
|
|
106
120
|
*/
|
|
107
121
|
export function escapeBackslashAndLinksExceptCodeBlock(textInput) {
|
|
108
122
|
// ref: https://spec.commonmark.org/0.31.2/#fenced-code-blocks
|
|
109
|
-
// Allows up to 3 leading spaces before ``` and optional trailing characters
|
|
110
|
-
// Ignored via go/ees005
|
|
111
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
112
|
-
var openingCodeFenceRegex = /^( {0,3})```.*$/;
|
|
113
|
-
// Allows up to 3 leading spaces before ``` and optional trailing spaces or tabs
|
|
114
|
-
// Ignored via go/ees005
|
|
115
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
116
|
-
var closingCodeFenceRegex = /^( {0,3})```[ \t]*$/;
|
|
117
123
|
var isInsideCodeBlock = false;
|
|
118
124
|
var lines = textInput.split('\n');
|
|
119
125
|
// In the splitted array, we traverse through every line and check if it will be parsed as a codeblock.
|
|
120
126
|
return lines.map(function (line) {
|
|
121
|
-
if (!isInsideCodeBlock &&
|
|
127
|
+
if (!isInsideCodeBlock && OPENING_CODE_FENCE_REGEX.test(line)) {
|
|
122
128
|
isInsideCodeBlock = true;
|
|
123
129
|
return line;
|
|
124
130
|
}
|
|
125
|
-
if (isInsideCodeBlock &&
|
|
131
|
+
if (isInsideCodeBlock && CLOSING_CODE_FENCE_REGEX.test(line)) {
|
|
126
132
|
isInsideCodeBlock = false;
|
|
127
133
|
return line;
|
|
128
134
|
}
|
|
@@ -223,9 +229,7 @@ export var htmlHasInvalidLinkTags = function htmlHasInvalidLinkTags(html) {
|
|
|
223
229
|
// <li><a href="http://www.atlassian.com\"<a> href="http://www.atlassian.com\"http://www.atlassian.com</a></a></li>">
|
|
224
230
|
export var removeDuplicateInvalidLinks = function removeDuplicateInvalidLinks(html) {
|
|
225
231
|
if (htmlHasInvalidLinkTags(html)) {
|
|
226
|
-
|
|
227
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
228
|
-
var htmlArray = html.split(/(?=<a)/);
|
|
232
|
+
var htmlArray = html.split(SPLIT_ON_ANCHOR_REGEX);
|
|
229
233
|
var htmlArrayWithoutInvalidLinks = htmlArray.filter(function (item) {
|
|
230
234
|
return !(item.includes('<a') && item.includes('"></a>')) && !(item.includes('<a') && !item.includes('</a>'));
|
|
231
235
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-paste",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.1",
|
|
4
4
|
"description": "Paste plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@atlaskit/adf-schema": "^56.0.0",
|
|
23
23
|
"@atlaskit/adf-utils": "^20.0.0",
|
|
24
|
-
"@atlaskit/code": "^18.
|
|
24
|
+
"@atlaskit/code": "^18.2.0",
|
|
25
25
|
"@atlaskit/editor-markdown-transformer": "^6.0.0",
|
|
26
26
|
"@atlaskit/editor-plugin-analytics": "^12.0.0",
|
|
27
27
|
"@atlaskit/editor-plugin-annotation": "^12.0.0",
|
|
@@ -31,24 +31,24 @@
|
|
|
31
31
|
"@atlaskit/editor-plugin-feature-flags": "^11.0.0",
|
|
32
32
|
"@atlaskit/editor-plugin-list": "^14.1.0",
|
|
33
33
|
"@atlaskit/editor-plugin-media": "^14.2.0",
|
|
34
|
-
"@atlaskit/editor-plugin-mentions": "^14.
|
|
34
|
+
"@atlaskit/editor-plugin-mentions": "^14.5.0",
|
|
35
35
|
"@atlaskit/editor-prosemirror": "^8.0.0",
|
|
36
36
|
"@atlaskit/editor-tables": "^3.0.0",
|
|
37
|
-
"@atlaskit/flag": "^18.
|
|
38
|
-
"@atlaskit/icon": "^36.
|
|
37
|
+
"@atlaskit/flag": "^18.1.0",
|
|
38
|
+
"@atlaskit/icon": "^36.1.0",
|
|
39
39
|
"@atlaskit/insm": "^1.0.0",
|
|
40
40
|
"@atlaskit/media-client": "^37.1.0",
|
|
41
|
-
"@atlaskit/media-common": "^14.
|
|
41
|
+
"@atlaskit/media-common": "^14.2.0",
|
|
42
42
|
"@atlaskit/platform-feature-flags": "^2.0.0",
|
|
43
43
|
"@atlaskit/prosemirror-history": "^1.0.0",
|
|
44
|
-
"@atlaskit/tmp-editor-statsig": "^114.
|
|
45
|
-
"@atlaskit/tokens": "^15.
|
|
44
|
+
"@atlaskit/tmp-editor-statsig": "^114.5.0",
|
|
45
|
+
"@atlaskit/tokens": "^15.2.0",
|
|
46
46
|
"@babel/runtime": "^7.0.0",
|
|
47
47
|
"lodash": "^4.17.21",
|
|
48
48
|
"uuid": "^3.1.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@atlaskit/editor-common": "^116.
|
|
51
|
+
"@atlaskit/editor-common": "^116.17.0",
|
|
52
52
|
"react": "^18.2.0",
|
|
53
53
|
"react-dom": "^18.2.0",
|
|
54
54
|
"react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
|
|
@@ -127,6 +127,9 @@
|
|
|
127
127
|
},
|
|
128
128
|
"platform_editor_paste_renderer_analytics": {
|
|
129
129
|
"type": "boolean"
|
|
130
|
+
},
|
|
131
|
+
"platform_editor_flexible_list_kill_switch_1": {
|
|
132
|
+
"type": "boolean"
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
}
|