@atlaskit/editor-plugin-paste
Version:
Paste plugin for @atlaskit/editor-core
1,508 lines • 80.2 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.applyContainerNodeTransformToSlice = applyContainerNodeTransformToSlice;
exports.checkIfSelectionInNestedList = checkIfSelectionInNestedList;
exports.checkTaskListInList = checkTaskListInList;
exports.doesSelectionWhichStartsOrEndsInListContainEntireList = void 0;
exports.flattenNestedListInSlice = flattenNestedListInSlice;
exports.handleCodeBlock = handleCodeBlock;
exports.handleExpandPaste = handleExpandPaste;
exports.handleMacroAutoConvert = handleMacroAutoConvert;
exports.handleMarkdown = handleMarkdown;
exports.handleMediaSingle = handleMediaSingle;
exports.handleMention = handleMention;
exports.handleNestedTablePaste = handleNestedTablePaste;
exports.handleParagraphBlockMarks = handleParagraphBlockMarks;
exports.handlePasteAsPlainText = handlePasteAsPlainText;
exports.handlePasteExpand = handlePasteExpand;
exports.handlePasteIntoCaption = handlePasteIntoCaption;
exports.handlePasteIntoTaskOrDecisionOrPanel = handlePasteIntoTaskOrDecisionOrPanel;
exports.handlePasteLinkOnSelectedText = handlePasteLinkOnSelectedText;
exports.handlePasteNonNestableBlockNodesIntoList = handlePasteNonNestableBlockNodesIntoList;
exports.handlePastePanelOrDecisionContentIntoList = handlePastePanelOrDecisionContentIntoList;
exports.handlePastePreservingMarks = handlePastePreservingMarks;
exports.handleRichText = handleRichText;
exports.handleSelectedTable = void 0;
exports.handleTableContentPasteInBodiedExtension = handleTableContentPasteInBodiedExtension;
exports.splitTablesOutOfPanelHtml = splitTablesOutOfPanelHtml;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _v = _interopRequireDefault(require("uuid/v4"));
var _transforms = require("@atlaskit/adf-utils/transforms");
var _analytics = require("@atlaskit/editor-common/analytics");
var _card = require("@atlaskit/editor-common/card");
var _coreUtils = require("@atlaskit/editor-common/core-utils");
var _lists = require("@atlaskit/editor-common/lists");
var _mark = require("@atlaskit/editor-common/mark");
var _nesting = require("@atlaskit/editor-common/nesting");
var _selection = require("@atlaskit/editor-common/selection");
var _utils = require("@atlaskit/editor-common/utils");
var _nodeTypeUtils = require("@atlaskit/editor-common/utils/node-type-utils");
var _model = require("@atlaskit/editor-prosemirror/model");
var _state = require("@atlaskit/editor-prosemirror/state");
var _utils2 = require("@atlaskit/editor-prosemirror/utils");
var _utils3 = require("@atlaskit/editor-tables/utils");
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _prosemirrorHistory = require("@atlaskit/prosemirror-history");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
var _commands = require("../../editor-commands/commands");
var _pluginFactory = require("../plugin-factory");
var _edgeCases = require("./edge-cases");
var _lists2 = require("./edge-cases/lists");
var _index = require("./index");
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
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; }
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; } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
// TODO: ED-20519 - Needs Macro extraction
var insideExpand = function insideExpand(state) {
var _state$schema$nodes = state.schema.nodes,
expand = _state$schema$nodes.expand,
nestedExpand = _state$schema$nodes.nestedExpand;
return (0, _utils2.hasParentNodeOfType)([expand, nestedExpand])(state.selection);
};
/** Helper type for single arg function */
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Compose 1 to n functions.
* @param func first function
* @param funcs additional functions
*/
function compose(func) {
for (var _len = arguments.length, funcs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
funcs[_key - 1] = arguments[_key];
}
var allFuncs = [func].concat(funcs);
return function composed(raw) {
return allFuncs.reduceRight(function (memo, func) {
return func(memo);
}, raw);
};
}
/* eslint-enable @typescript-eslint/no-explicit-any */
// remove text attribute from mention for copy/paste (GDPR)
function handleMention(slice, schema) {
return (0, _utils.mapSlice)(slice, function (node) {
var _schema$nodes$mention;
// We should move this to the mention plugin when we refactor how paste works in the future
// For now we can just null check mention exists in the schema to ensure we don't crash if it doesn't
// exist.
if (node.type.name === ((_schema$nodes$mention = schema.nodes.mention) === null || _schema$nodes$mention === void 0 ? void 0 : _schema$nodes$mention.name)) {
var mention = node.attrs;
var newMention = _objectSpread(_objectSpread({}, mention), {}, {
text: ''
});
return schema.nodes.mention.create(newMention, node.content, node.marks);
}
return node;
});
}
function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
return function (state, dispatch) {
var _slice$content$firstC, _slice$content$firstC2, _panelNode$type, _transformedSlice$con, _transformedSlice$con2;
var schema = state.schema,
selection = state.tr.selection;
var codeMark = schema.marks.code,
_schema$nodes = schema.nodes,
decisionItem = _schema$nodes.decisionItem,
emoji = _schema$nodes.emoji,
hardBreak = _schema$nodes.hardBreak,
mention = _schema$nodes.mention,
paragraph = _schema$nodes.paragraph,
taskItem = _schema$nodes.taskItem,
text = _schema$nodes.text,
panel = _schema$nodes.panel,
panel_c1 = _schema$nodes.panel_c1,
bulletList = _schema$nodes.bulletList,
orderedList = _schema$nodes.orderedList,
taskList = _schema$nodes.taskList,
listItem = _schema$nodes.listItem,
expand = _schema$nodes.expand,
heading = _schema$nodes.heading,
codeBlock = _schema$nodes.codeBlock;
var selectionIsValidNode = state.selection instanceof _state.NodeSelection && ['decisionList', 'decisionItem', 'taskList', 'taskItem'].includes(state.selection.node.type.name);
// panel_c1 is a schema variant of panel (used when a table is nested inside a panel). When the
// experiment is on we must handle it anywhere we special-case a panel, otherwise partial
// selections copied from inside a table-in-panel are treated as a foreign container and
// re-wrapped on paste.
var selectionHasValidParentNode = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? (0, _utils2.hasParentNodeOfType)([decisionItem, taskItem, panel, panel_c1].filter(Boolean))(state.selection) : (0, _utils2.hasParentNodeOfType)([decisionItem, taskItem, panel])(state.selection);
var selectionIsCodeBlock = (0, _utils2.hasParentNodeOfType)([codeBlock])(state.selection);
var selectionIsListItem = (0, _utils2.hasParentNodeOfType)([listItem])(state.selection);
var panelNode = (0, _index.isSelectionInsidePanel)(selection);
var selectionIsPanel = Boolean(panelNode);
var sliceFirstChildIsPanel = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? !!slice.content.firstChild && (0, _nodeTypeUtils.getBaseNodeTypeName)(slice.content.firstChild.type) === 'panel' : ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.type) === panel;
var isSliceWholePanel = sliceFirstChildIsPanel && slice.openStart === 0 && slice.openEnd === 0;
// we avoid handling codeBlock-in-panel use case in this function
// returning false will allow code to flow into `handleCodeBlock` function
// Partial content copied from panels will have panel in the slice
// Return false to avoid handling this situation when pasted into list in panel and let `handlePastePanelOrDecisionContentIntoList` handle it
if (selectionIsPanel && (selectionIsCodeBlock || selectionIsListItem && !isSliceWholePanel)) {
return false;
}
// Some types of content should be handled by the default handler, not this function.
// Check through slice content to see if it contains an invalid node.
var sliceIsInvalid = false;
var sliceHasTask = false;
slice.content.nodesBetween(0, slice.content.size, function (node) {
if (node.type === bulletList || node.type === orderedList || node.type === expand || node.type === heading && !(selectionIsPanel && (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_fix_header_paste_in_panel')) || node.type === listItem) {
sliceIsInvalid = true;
}
if (selectionIsPanel && node.type === taskList) {
sliceHasTask = true;
}
});
// If the selection is a panel,
// and the slice's first node is a paragraph
// and it is not from a depth that would indicate it being from inside from another node (e.g. text from a decision)
// then we can rely on the default behaviour.
var selectionIsTaskOrDecision = (0, _utils2.hasParentNode)(function (node) {
return node.type === taskItem || node.type === decisionItem;
})(selection);
var sliceIsAPanelReceivingLowDepthText = selectionIsPanel && !selectionIsTaskOrDecision && ((_slice$content$firstC2 = slice.content.firstChild) === null || _slice$content$firstC2 === void 0 ? void 0 : _slice$content$firstC2.type) === paragraph && slice.openEnd < 2;
if (sliceIsInvalid || sliceIsAPanelReceivingLowDepthText || !selectionIsValidNode && !selectionHasValidParentNode) {
return false;
}
var filters = [(0, _utils.linkifyContent)(schema)];
var selectionMarks = selection.$head.marks();
if (selection instanceof _state.TextSelection && Array.isArray(selectionMarks) && selectionMarks.length > 0 && (0, _index.hasOnlyNodesOfType)(paragraph, text, emoji, mention, hardBreak)(slice) && (!codeMark.isInSet(selectionMarks) || (0, _mark.anyMarkActive)(state, codeMark)) // check if there is a code mark anywhere in the selection
) {
filters.push((0, _index.applyTextMarksToSlice)(schema, selection.$head.marks()));
}
var transformedSlice = compose.apply(null, filters)(slice);
var isFirstChildTaskNode = transformedSlice.content.firstChild.type === taskList || transformedSlice.content.firstChild.type === taskItem;
var tr = (0, _prosemirrorHistory.closeHistory)(state.tr);
if (panelNode && sliceHasTask && sliceFirstChildIsPanel && (0, _index.isEmptyNode)(panelNode) && selection.$from.node() === selection.$to.node()) {
return Boolean((0, _lists2.insertSliceInsideOfPanelNodeSelected)(panelNode)({
tr: tr,
slice: slice
}));
}
var transformedSliceIsValidNode = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? (transformedSlice.content.firstChild.type.inlineContent || ['decisionList', 'decisionItem', 'taskItem', 'taskList', 'panel'].includes((0, _nodeTypeUtils.getBaseNodeTypeName)(transformedSlice.content.firstChild.type))) && (!(0, _utils.isInListItem)(state) || (0, _utils.isInListItem)(state) && isFirstChildTaskNode) : (transformedSlice.content.firstChild.type.inlineContent || ['decisionList', 'decisionItem', 'taskItem', 'taskList', 'panel'].includes(transformedSlice.content.firstChild.type.name)) && (!(0, _utils.isInListItem)(state) || (0, _utils.isInListItem)(state) && isFirstChildTaskNode);
// If the slice or the selection are valid nodes to handle,
// and the slice is not a whole node (i.e. openStart is 1 and openEnd is 0)
// or the slice's first node is a paragraph,
// then we can replace the selection with our slice.
// When the experiment is on, validate against the actual destination panel variant (panel or
// panel_c1) since their content expressions differ (panel_c1 additionally allows a nested
// table).
var pastingIntoExtendedPanel = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? selectionIsPanel && ((_panelNode$type = panelNode === null || panelNode === void 0 ? void 0 : panelNode.type) !== null && _panelNode$type !== void 0 ? _panelNode$type : panel).validContent(transformedSlice.content) : selectionIsPanel && panel.validContent(transformedSlice.content);
// A task slice carrying its own open taskList wrapper would, on a plain replaceSelection,
// nest that wrapper inside the current item and add an extra taskList level. Unwrapping one
// level keeps the pasted task(s) at the same level. Guarded by a kill switch.
var isOpenTaskListSlice = ((_transformedSlice$con = transformedSlice.content.firstChild) === null || _transformedSlice$con === void 0 ? void 0 : _transformedSlice$con.type) === taskList && transformedSlice.openStart >= 2 && transformedSlice.openEnd >= 1;
// Restrict the unwrap to a taskItem destination: its task content is schema-invalid inside a
// decisionItem (decisionList only accepts decisionItem), so decisionItem must fall through to
// the normal replaceSelection path.
var pastingIntoTaskItem = (0, _utils2.hasParentNodeOfType)([taskItem])(selection);
if ((0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true) && !(0, _platformFeatureFlags.fg)('platform_editor_flexible_list_kill_switch_1') && isOpenTaskListSlice && pastingIntoTaskItem && !selectionIsPanel) {
var unwrappedSlice = new _model.Slice(transformedSlice.content.firstChild.content, transformedSlice.openStart - 1, transformedSlice.openEnd - 1);
tr.replaceSelection(unwrappedSlice).scrollIntoView();
} else if ((transformedSliceIsValidNode || selectionIsValidNode) && !pastingIntoExtendedPanel && !(transformedSlice.openStart === 1 && transformedSlice.openEnd === 0 ||
// Whole codeblock node has reverse slice depths.
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$con2 = transformedSlice.content.firstChild) === null || _transformedSlice$con2 === void 0 ? void 0 : _transformedSlice$con2.type) === paragraph) {
tr.replaceSelection(transformedSlice).scrollIntoView();
} else {
var isWholeContentSelected = selection.$from.pos === selection.$from.start() && selection.$to.end() === selection.$to.pos;
if (pastingIntoExtendedPanel && selection.$from.pos !== selection.$to.pos && !isWholeContentSelected) {
// Do a replaceSelection if the entire panel content isn't selected
//tr.replaceSelection(transformedSlice).scrollIntoView();
tr.replaceSelection(new _model.Slice(transformedSlice.content, 0, transformedSlice.openEnd)).scrollIntoView();
} else if (['mediaSingle'].includes(transformedSlice.content.firstChild.type.name) && selectionIsPanel) {
var parentNode = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? (0, _utils2.findParentNodeOfType)([panel, panel_c1].filter(Boolean))(selection) : (0, _utils2.findParentNodeOfType)(panel)(selection);
if (selectionIsPanel && parentNode && (0, _utils.isNodeEmpty)(parentNode.node)) {
tr.insert(selection.$from.pos, transformedSlice.content).scrollIntoView();
// Place the cursor at the the end of the insersertion
var endPos = tr.selection.from + transformedSlice.size;
tr.setSelection(new _state.TextSelection(tr.doc.resolve(endPos)));
} else {
tr.replaceSelection(transformedSlice).scrollIntoView();
}
} else {
var _transformedSlice$con3;
if (pastingIntoExtendedPanel && isWholeContentSelected) {
// if the entire panel content is selected, doing a replaceSelection removes the panel as well. Hence we do delete followed by safeInsert
tr.delete(selection.$from.pos, selection.$to.pos);
}
// This maintains both the selection (destination) and the slice (paste content).
(0, _utils2.safeInsert)(transformedSlice.content)(tr).scrollIntoView();
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') {
tr.setSelection(_state.TextSelection.near(tr.doc.resolve(tr.selection.$from.pos + transformedSlice.content.size)));
} else {
// safeInsert doesn't set correct cursor position
// it moves the cursor to beginning of the node
// we manually shift the cursor to end of the node
var nextPos = tr.doc.resolve(tr.selection.$from.end());
tr.setSelection(new _state.TextSelection(nextPos));
}
}
}
queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 || queueCardsFromChangedTr(state, tr, _analytics.INPUT_METHOD.CLIPBOARD);
if (dispatch) {
dispatch(tr);
}
return true;
};
}
function handlePasteNonNestableBlockNodesIntoList(slice) {
return function (state, dispatch) {
var _tr$doc$nodeAt, _slice$content$firstC3, _sliceContent$firstCh, _findParentNodeOfType;
var tr = state.tr;
var selection = tr.selection;
var $from = selection.$from,
$to = selection.$to,
from = selection.from,
to = selection.to;
var _state$schema$nodes2 = state.schema.nodes,
orderedList = _state$schema$nodes2.orderedList,
bulletList = _state$schema$nodes2.bulletList,
listItem = _state$schema$nodes2.listItem;
// Selected nodes
var selectionParentListItemNode = (0, _utils2.findParentNodeOfType)(listItem)(selection);
var selectionParentListNodeWithPos = (0, _utils2.findParentNodeOfType)([bulletList, orderedList])(selection);
var selectionParentListNode = selectionParentListNodeWithPos === null || selectionParentListNodeWithPos === void 0 ? void 0 : selectionParentListNodeWithPos.node;
// Slice info
var sliceContent = slice.content;
var sliceIsListItems = (0, _utils.isListNode)(sliceContent.firstChild) && (0, _utils.isListNode)(sliceContent.lastChild);
// Find case of slices that can be inserted into a list item
// (eg. paragraphs, list items, code blocks, media single)
// These scenarios already get handled elsewhere and don't need to split the list
var sliceContainsBlockNodesOtherThanThoseAllowedInListItem = false;
slice.content.forEach(function (child) {
var _listItem$spec$conten;
if (!listItem || child.isBlock && !((_listItem$spec$conten = listItem.spec.content) !== null && _listItem$spec$conten !== void 0 && _listItem$spec$conten.includes(child.type.name))) {
sliceContainsBlockNodesOtherThanThoseAllowedInListItem = true;
}
});
if (!selectionParentListItemNode || !sliceContent || (0, _utils2.canInsert)($from, sliceContent) ||
// eg. inline nodes that can be inserted in a list item
!sliceContainsBlockNodesOtherThanThoseAllowedInListItem || sliceIsListItems || !selectionParentListNodeWithPos) {
return false;
}
// Offsets
var listWrappingOffset = $to.depth - selectionParentListNodeWithPos.depth + 1; // difference in depth between to position and list node
var listItemWrappingOffset = $to.depth - selectionParentListNodeWithPos.depth; // difference in depth between to position and list item node
// Anything to do with nested lists should safeInsert and not be handled here
if (checkIfSelectionInNestedList(state)) {
return false;
}
// Node after the insert position
var nodeAfterInsertPositionIsListItem = ((_tr$doc$nodeAt = tr.doc.nodeAt(to + listItemWrappingOffset)) === null || _tr$doc$nodeAt === void 0 ? void 0 : _tr$doc$nodeAt.type.name) === 'listItem';
// Get the next list items position (used later to find the split out ordered list)
var indexOfNextListItem = $to.indexAfter($to.depth - listItemWrappingOffset);
var positionOfNextListItem = tr.doc.resolve(selectionParentListNodeWithPos.pos + 1).posAtIndex(indexOfNextListItem);
// These nodes paste as plain text by default so need to be handled differently
var sliceContainsNodeThatPastesAsPlainText = sliceContent.firstChild && ['taskItem', 'taskList', 'heading', 'blockquote'].includes(sliceContent.firstChild.type.name);
// Work out position to replace up to
var replaceTo;
if (sliceContainsNodeThatPastesAsPlainText && nodeAfterInsertPositionIsListItem) {
replaceTo = to + listItemWrappingOffset;
} else if (sliceContainsNodeThatPastesAsPlainText || !nodeAfterInsertPositionIsListItem) {
replaceTo = to;
} else {
replaceTo = to + listWrappingOffset;
}
// handle the insertion of the slice
if (((_slice$content$firstC3 = slice.content.firstChild) === null || _slice$content$firstC3 === void 0 ? void 0 : _slice$content$firstC3.type.name) === 'blockquote' && (0, _utils2.contains)(slice.content.firstChild, state.schema.nodes.listItem)) {
(0, _edgeCases.insertSliceInsideBlockquote)({
tr: tr,
slice: slice
});
} else if (sliceContainsNodeThatPastesAsPlainText || nodeAfterInsertPositionIsListItem || sliceContent.childCount > 1 && ((_sliceContent$firstCh = sliceContent.firstChild) === null || _sliceContent$firstCh === void 0 ? void 0 : _sliceContent$firstCh.type.name) !== 'paragraph') {
tr.replaceWith(from, replaceTo, sliceContent).scrollIntoView();
} else {
// When the selection is not at the end of a list item
// eg. middle of list item, start of list item
tr.replaceSelection(slice).scrollIntoView();
}
// Find the ordered list node after the pasted content so we can set it's order
var mappedPositionOfNextListItem = tr.mapping.map(positionOfNextListItem);
if (mappedPositionOfNextListItem > tr.doc.nodeSize) {
return false;
}
var nodeAfterPastedContentResolvedPos = (0, _utils2.findParentNodeOfTypeClosestToPos)(tr.doc.resolve(mappedPositionOfNextListItem), [orderedList]);
// Work out the new split out lists 'order' (the number it starts from)
var originalParentOrderedListNodeOrder = selectionParentListNode === null || selectionParentListNode === void 0 ? void 0 : selectionParentListNode.attrs.order;
var numOfListItemsInOriginalList = (_findParentNodeOfType = (0, _utils2.findParentNodeOfTypeClosestToPos)(tr.doc.resolve(from - 1), [orderedList])) === null || _findParentNodeOfType === void 0 ? void 0 : _findParentNodeOfType.node.childCount;
// Set the new split out lists order attribute
if (typeof originalParentOrderedListNodeOrder === 'number' && numOfListItemsInOriginalList && nodeAfterPastedContentResolvedPos) {
tr.setNodeMarkup(nodeAfterPastedContentResolvedPos.pos, orderedList, _objectSpread(_objectSpread({}, nodeAfterPastedContentResolvedPos.node.attrs), {}, {
order: originalParentOrderedListNodeOrder + numOfListItemsInOriginalList
}));
}
// dispatch transaction
if (tr.docChanged) {
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
}
var doesSelectionWhichStartsOrEndsInListContainEntireList = exports.doesSelectionWhichStartsOrEndsInListContainEntireList = function doesSelectionWhichStartsOrEndsInListContainEntireList(selection, findRootParentListNode) {
var $from = selection.$from,
$to = selection.$to,
from = selection.from,
to = selection.to;
var selectionParentListItemNodeResolvedPos = findRootParentListNode ? findRootParentListNode($from) || findRootParentListNode($to) : null;
var selectionParentListNode = selectionParentListItemNodeResolvedPos === null || selectionParentListItemNodeResolvedPos === void 0 ? void 0 : selectionParentListItemNodeResolvedPos.parent;
if (!selectionParentListItemNodeResolvedPos || !selectionParentListNode) {
return false;
}
var startOfEntireList = $from.pos < $to.pos ? selectionParentListItemNodeResolvedPos.pos + $from.depth - 1 : selectionParentListItemNodeResolvedPos.pos + $to.depth - 1;
var endOfEntireList = $from.pos < $to.pos ? selectionParentListItemNodeResolvedPos.pos + selectionParentListNode.nodeSize - $to.depth - 1 : selectionParentListItemNodeResolvedPos.pos + selectionParentListNode.nodeSize - $from.depth - 1;
if (!startOfEntireList || !endOfEntireList) {
return false;
}
if (from < to) {
return startOfEntireList >= $from.pos && endOfEntireList <= $to.pos;
} else if (from > to) {
return startOfEntireList >= $to.pos && endOfEntireList <= $from.pos;
} else {
return false;
}
};
function handlePastePanelOrDecisionContentIntoList(slice, findRootParentListNode) {
return function (state, dispatch) {
var _blockNode$type$name;
var schema = state.schema,
tr = state.tr;
var selection = tr.selection;
// Check this pasting action is related to copy content from panel node into a selected the list node
var blockNode = slice.content.firstChild;
var isSliceWholeNode = slice.openStart === 0 && slice.openEnd === 0;
var selectionParentListItemNode = selection.$to.node(selection.$to.depth - 1);
var sliceIsWholeNodeButShouldNotReplaceSelection = isSliceWholeNode && !doesSelectionWhichStartsOrEndsInListContainEntireList(selection, findRootParentListNode);
// When the experiment is on, treat panel_c1 (table-in-panel variant) like a panel.
var blockNodeIsPanelOrDecision = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? ['panel', 'decisionList'].includes(blockNode ? (0, _nodeTypeUtils.getBaseNodeTypeName)(blockNode.type) : '') : ['panel', 'decisionList'].includes((_blockNode$type$name = blockNode === null || blockNode === void 0 ? void 0 : blockNode.type.name) !== null && _blockNode$type$name !== void 0 ? _blockNode$type$name : '');
if (!selectionParentListItemNode || (selectionParentListItemNode === null || selectionParentListItemNode === void 0 ? void 0 : selectionParentListItemNode.type) !== schema.nodes.listItem || !blockNode || !blockNodeIsPanelOrDecision || slice.content.childCount > 1 || (blockNode === null || blockNode === void 0 ? void 0 : blockNode.content.firstChild) === undefined || sliceIsWholeNodeButShouldNotReplaceSelection) {
return false;
}
// Paste the panel node contents extracted instead of pasting the entire panel node
tr.replaceSelection(slice).scrollIntoView();
if (dispatch) {
dispatch(tr);
}
return true;
};
}
var innerTextRangeOfTextblock = function innerTextRangeOfTextblock(doc, posOfBlock) {
var block = doc.nodeAt(posOfBlock);
if (!block || !block.isTextblock) {
return null;
}
// raw content bounds
var contentStart = posOfBlock + 1; // +1 to move from node's start token to content start
var contentEnd = contentStart + block.content.size;
// clamp to doc coord space
var start = Math.max(0, Math.min(contentStart, doc.content.size));
var end = Math.max(0, Math.min(contentEnd, doc.content.size));
if (end <= start) {
return null;
}
// snap to nearest valid text positions
var startSel = _state.TextSelection.findFrom(doc.resolve(start), 1, true);
var endSel = _state.TextSelection.findFrom(doc.resolve(end), -1, true);
if (!startSel || !endSel) {
return null;
}
var from = startSel.$from.pos;
var to = endSel.$to.pos;
return to > from ? {
from: from,
to: to
} : null;
};
function resolveSingleTextblockRangeIfAllSelected(state) {
var sel = state.selection;
if (!(sel instanceof _state.AllSelection)) {
return null;
}
var count = 0;
var posOfBlock = -1;
state.doc.nodesBetween(sel.from, sel.to, function (node, pos) {
if (!node.isTextblock) {
return true;
}
count++;
if (count > 1) {
return false;
}
posOfBlock = pos;
return true;
});
if (count !== 1) {
return null;
}
return innerTextRangeOfTextblock(state.doc, posOfBlock);
}
// If we paste a link onto some selected text, apply the link as a mark
function handlePasteLinkOnSelectedText(slice) {
return function (state, dispatch) {
var _selectAllRange$from, _selectAllRange$to;
var schema = state.schema,
selection = state.selection,
_state$selection = state.selection,
from = _state$selection.from,
to = _state$selection.to,
tr = state.tr;
var linkMark;
// check if we have a link on the clipboard
if (slice.content.childCount === 1 && (0, _utils.isParagraph)(slice.content.child(0), schema)) {
var paragraph = slice.content.child(0);
if (paragraph.content.childCount === 1 && (0, _utils.isText)(paragraph.content.child(0), schema)) {
var text = paragraph.content.child(0);
// If pasteType is plain text, then
// @atlaskit/editor-markdown-transformer in getMarkdownSlice decode
// url before setting text property of text node.
// However href of marks will be without decoding.
// So, if there is character (e.g space) in url eligible escaping then
// mark.attrs.href will not be equal to text.text.
// That's why decoding mark.attrs.href before comparing.
// However, if pasteType is richText, that means url in text.text
// and href in marks, both won't be decoded.
linkMark = text.marks.find(function (mark) {
return (0, _utils.isLinkMark)(mark, schema) && (mark.attrs.href === text.text || decodeURI(mark.attrs.href) === text.text);
});
}
}
// derive a linkable range if possible for Select‑All over a single textblock
var selectAllRange = (0, _platformFeatureFlags.fg)('platform_editor_link_paste_select_all') ? resolveSingleTextblockRangeIfAllSelected(state) : null;
var rangeFrom = (_selectAllRange$from = selectAllRange === null || selectAllRange === void 0 ? void 0 : selectAllRange.from) !== null && _selectAllRange$from !== void 0 ? _selectAllRange$from : from;
var rangeTo = (_selectAllRange$to = selectAllRange === null || selectAllRange === void 0 ? void 0 : selectAllRange.to) !== null && _selectAllRange$to !== void 0 ? _selectAllRange$to : to;
// if we have a link, apply it to the selected text if we have any and it's allowed
if (linkMark && (selection instanceof _state.TextSelection || Boolean(selectAllRange)) && !selection.empty && (0, _utils.canLinkBeCreatedInRange)(rangeFrom, rangeTo)(state)) {
tr.addMark(rangeFrom, rangeTo, linkMark);
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
}
function handlePasteAsPlainText(slice, _event, editorAnalyticsAPI) {
return function (state, dispatch, view) {
var _input;
if (!view) {
return false;
}
// prosemirror-bump-fix
// Yes, this is wrong by default. But, we need to keep the private PAI usage to unblock the prosemirror bump
// So, this code will make sure we are checking for both version (current and the newest prosemirror-view version
var isShiftKeyPressed =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
view.shiftKey || ((_input = view.input) === null || _input === void 0 ? void 0 : _input.shiftKey);
// In case of SHIFT+CMD+V ("Paste and Match Style") we don't want to run the usual
// fuzzy matching of content. ProseMirror already handles this scenario and will
// provide us with slice containing paragraphs with plain text, which we decorate
// with "stored marks".
// @see prosemirror-view/src/clipboard.js:parseFromClipboard()).
// @see prosemirror-view/src/input.js:doPaste().
if (isShiftKeyPressed) {
var tr = (0, _prosemirrorHistory.closeHistory)(state.tr);
var _tr = tr,
selection = _tr.selection;
// <- using the same internal flag that prosemirror-view is using
// if user has selected table we need custom logic to replace the table
tr = (0, _utils3.replaceSelectedTable)(state, slice);
// add analytics after replacing selected table
tr = (0, _index.addReplaceSelectedTableAnalytics)(state, tr, editorAnalyticsAPI);
// otherwise just replace the selection
if (!tr.docChanged) {
tr.replaceSelection(slice);
}
(state.storedMarks || []).forEach(function (mark) {
tr.addMark(selection.from, selection.from + slice.size, mark);
});
tr.scrollIntoView();
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
}
function handlePastePreservingMarks(slice, queueCardsFromChangedTr) {
return function (state, dispatch) {
var schema = state.schema,
selection = state.tr.selection;
var _schema$marks = schema.marks,
codeMark = _schema$marks.code,
annotationMark = _schema$marks.annotation,
_schema$nodes2 = schema.nodes,
bulletList = _schema$nodes2.bulletList,
emoji = _schema$nodes2.emoji,
hardBreak = _schema$nodes2.hardBreak,
heading = _schema$nodes2.heading,
listItem = _schema$nodes2.listItem,
mention = _schema$nodes2.mention,
orderedList = _schema$nodes2.orderedList,
text = _schema$nodes2.text;
if (!(selection instanceof _state.TextSelection)) {
return false;
}
var selectionMarks = selection.$head.marks();
if (selectionMarks.length === 0) {
return false;
}
// special case for codeMark: will preserve mark only if codeMark is currently active
// won't preserve mark if cursor is on the edge on the mark (namely inactive)
var hasActiveCodeMark = codeMark && codeMark.isInSet(selectionMarks) && (0, _mark.anyMarkActive)(state, codeMark);
var hasAnnotationMark = annotationMark && annotationMark.isInSet(selectionMarks);
var selectionIsHeading = (0, _utils2.hasParentNodeOfType)([heading])(state.selection);
// if the pasted data is one of the node types below
// we apply current selection marks to the pasted slice
if ((0, _index.hasOnlyNodesOfType)(bulletList, hardBreak, heading, listItem, text, emoji, mention, orderedList)(slice) || selectionIsHeading || hasActiveCodeMark || hasAnnotationMark) {
var transformedSlice = (0, _index.applyTextMarksToSlice)(schema, selectionMarks)(slice);
var tr = (0, _prosemirrorHistory.closeHistory)(state.tr).replaceSelection(transformedSlice).setStoredMarks(selectionMarks).scrollIntoView();
queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 || queueCardsFromChangedTr(state, tr, _analytics.INPUT_METHOD.CLIPBOARD);
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
}
function getSmartLinkAdf(_x, _x2, _x3) {
return _getSmartLinkAdf.apply(this, arguments);
}
function _getSmartLinkAdf() {
_getSmartLinkAdf = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(text, type, cardOptions) {
var provider;
return _regenerator.default.wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
if (cardOptions.provider) {
_context.next = 1;
break;
}
throw Error('No card provider found');
case 1:
_context.next = 2;
return cardOptions.provider;
case 2:
provider = _context.sent;
_context.next = 3;
return provider.resolve(text, type);
case 3:
return _context.abrupt("return", _context.sent);
case 4:
case "end":
return _context.stop();
}
}, _callee);
}));
return _getSmartLinkAdf.apply(this, arguments);
}
function insertAutoMacro(slice, macro, view, from, to) {
if (view) {
// insert the text or linkified/md-converted clipboard data
var selection = view.state.tr.selection;
var tr;
var before;
if (typeof from === 'number' && typeof to === 'number') {
tr = view.state.tr.replaceRange(from, to, slice);
before = tr.mapping.map(from, -1);
} else {
tr = view.state.tr.replaceSelection(slice);
before = tr.mapping.map(selection.from, -1);
}
view.dispatch(tr);
// replace the text with the macro as a separate transaction
// so the autoconversion generates 2 undo steps
var macroTr = (0, _prosemirrorHistory.closeHistory)(view.state.tr).replaceRangeWith(before, before + slice.size, macro).scrollIntoView();
(0, _card.addLinkMetadata)(view.state.selection, macroTr, {
inputMethod: _analytics.INPUT_METHOD.CLIPBOARD,
cardAction: 'AUTO_CONVERT'
});
view.dispatch(macroTr);
return true;
}
return false;
}
function handleMacroAutoConvert(text, slice, queueCardsFromChangedTr, runMacroAutoConvert, cardsOptions, extensionAutoConverter) {
return function (state, dispatch, view) {
var macro = null;
// try to use auto convert from extension provider first
if (extensionAutoConverter) {
var extension = extensionAutoConverter(text);
if (extension) {
macro = _model.Node.fromJSON(state.schema, extension);
}
}
// then try from macro provider (which will be removed some time in the future)
if (!macro) {
var _runMacroAutoConvert;
macro = (_runMacroAutoConvert = runMacroAutoConvert === null || runMacroAutoConvert === void 0 ? void 0 : runMacroAutoConvert(state, text)) !== null && _runMacroAutoConvert !== void 0 ? _runMacroAutoConvert : null;
}
if (macro) {
/**
* if FF enabled, run through smart links and check for result
*/
if (cardsOptions && cardsOptions.resolveBeforeMacros && cardsOptions.resolveBeforeMacros.length) {
if (cardsOptions.resolveBeforeMacros.indexOf(macro.attrs.extensionKey) < 0) {
return insertAutoMacro(slice, macro, view);
}
if (!view) {
throw new Error('View is missing');
}
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
var trackingId = (0, _v.default)();
var trackingFrom = "handleMacroAutoConvert-from-".concat(trackingId);
var trackingTo = "handleMacroAutoConvert-to-".concat(trackingId);
(0, _commands.startTrackingPastedMacroPositions)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, trackingFrom, state.selection.from), trackingTo, state.selection.to))(state, dispatch);
getSmartLinkAdf(text, 'inline', cardsOptions).then(function () {
// we use view.state rather than state because state becomes a stale
// state reference after getSmartLinkAdf's async work
var _getPastePluginState = (0, _pluginFactory.getPluginState)(view.state),
pastedMacroPositions = _getPastePluginState.pastedMacroPositions;
if (dispatch) {
handleMarkdown(slice, queueCardsFromChangedTr, pastedMacroPositions[trackingFrom], pastedMacroPositions[trackingTo])(view.state, dispatch);
}
}).catch(function () {
var _getPastePluginState2 = (0, _pluginFactory.getPluginState)(view.state),
pastedMacroPositions = _getPastePluginState2.pastedMacroPositions;
insertAutoMacro(slice, macro, view, pastedMacroPositions[trackingFrom], pastedMacroPositions[trackingTo]);
}).finally(function () {
(0, _commands.stopTrackingPastedMacroPositions)([trackingFrom, trackingTo])(view.state, dispatch);
});
return true;
}
return insertAutoMacro(slice, macro, view);
}
return !!macro;
};
}
function handleCodeBlock(text) {
return function (state, dispatch) {
var codeBlock = state.schema.nodes.codeBlock;
if (text && (0, _utils2.hasParentNodeOfType)(codeBlock)(state.selection)) {
var tr = (0, _prosemirrorHistory.closeHistory)(state.tr);
tr.scrollIntoView();
if (dispatch) {
dispatch(tr.insertText(text));
}
return true;
}
return false;
};
}
function isOnlyMedia(state, slice) {
var media = state.schema.nodes.media;
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return slice.content.childCount === 1 && slice.content.firstChild.type === media;
}
function isOnlyMediaSingle(state, slice) {
var mediaSingle = state.schema.nodes.mediaSingle;
return (
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
mediaSingle && slice.content.childCount === 1 && slice.content.firstChild.type === mediaSingle
);
}
function handleMediaSingle(inputMethod, insertMediaAsMediaSingle) {
return function (slice) {
return function (state, dispatch, view) {
if (view) {
if (isOnlyMedia(state, slice)) {
var _insertMediaAsMediaSi;
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return (_insertMediaAsMediaSi = insertMediaAsMediaSingle === null || insertMediaAsMediaSingle === void 0 ? void 0 : insertMediaAsMediaSingle(view, slice.content.firstChild, inputMethod)) !== null && _insertMediaAsMediaSi !== void 0 ? _insertMediaAsMediaSi : false;
}
if ((0, _coreUtils.insideTable)(state) && isOnlyMediaSingle(state, slice)) {
var tr = state.tr.replaceSelection(slice);
var nextPos = tr.doc.resolve(tr.mapping.map(state.selection.$from.pos));
if (dispatch) {
dispatch(tr.setSelection(new _selection.GapCursorSelection(nextPos, _selection.Side.RIGHT)));
}
return true;
}
}
return false;
};
};
}
var hasTopLevelExpand = function hasTopLevelExpand(slice) {
var hasExpand = false;
slice.content.forEach(function (node) {
if (node.type.name === 'expand' || node.type.name === 'nestedExpand') {
hasExpand = true;
}
});
return hasExpand;
};
function handleTableContentPasteInBodiedExtension(slice) {
return function (state, dispatch) {
var isInsideBodyExtension = (0, _utils2.hasParentNodeOfType)(state.schema.nodes.bodiedExtension)(state.selection);
if (!(0, _coreUtils.insideTable)(state) || !isInsideBodyExtension) {
return false;
}
var bodiedExtension = state.schema.nodes.bodiedExtension;
var newSlice = (0, _utils.mapSlice)(slice, function (maybeNode) {
if (maybeNode.type === bodiedExtension) {
return bodiedExtension.createChecked(maybeNode.attrs, maybeNode.content, maybeNode.marks);
}
return maybeNode;
});
if (dispatch) {
dispatch(state.tr.replaceSelection(newSlice));
return true;
}
return false;
};
}
function handleNestedTablePaste(slice, isNestingTablesSupported) {
return function (state, dispatch) {
if (!isNestingTablesSupported || !(0, _coreUtils.insideTable)(state)) {
return false;
}
var schema = state.schema,
selection = state.selection;
var sliceHasTable = false;
slice.content.forEach(function (node) {
if (node.type === state.schema.nodes.table) {
sliceHasTable = true;
}
});
if (sliceHasTable) {
// if slice has table - if pasting to deeply nested location place paste after top table
if ((0, _nesting.getParentOfTypeCount)(schema.nodes.table)(selection.$from) > 1) {
var positionAfterTopTable = (0, _nesting.getPositionAfterTopParentNodeOfType)(schema.nodes.table)(selection.$from);
var tr = state.tr;
tr = (0, _utils2.safeInsert)(slice.content, positionAfterTopTable)(tr);
tr.scrollIntoView();
if (dispatch) {
dispatch(tr);
return true;
}
}
}
return false;
};
}
function handleExpandPaste(slice) {
return function (state, dispatch) {
var isInsideNestableExpand = !!insideExpand(state);
// Do not handle expand if it's not being pasted into a table or expand
// OR if it's nested within another node when being pasted into a table/expand
if (!(0, _coreUtils.insideTable)(state) && !isInsideNestableExpand || !hasTopLevelExpand(slice)) {
return false;
}
var _state$schema$nodes3 = state.schema.nodes,
expand = _state$schema$nodes3.expand,
nestedExpand = _state$schema$nodes3.nestedExpand;
var tr = state.tr;
var hasExpand = false;
var newSlice = (0, _utils.mapSlice)(slice, function (maybeNode) {
if (maybeNode.type === expand || maybeNode.type === nestedExpand) {
hasExpand = true;
try {
return nestedExpand.createChecked(maybeNode.attrs, maybeNode.content, maybeNode.marks);
} catch (_unused) {
tr = (0, _utils2.safeInsert)(maybeNode, tr.selection.$to.pos)(tr);
return _model.Fragment.empty;
}
}
return maybeNode;
});
if (hasExpand && dispatch) {
// If the slice is a subset, we can let PM replace the selection
// it will insert as text where it can't place the node.
// Otherwise we use safeInsert to insert below instead of
// replacing/splitting the current node.
if (slice.openStart > 1 && slice.openEnd > 1) {
dispatch(tr.replaceSelection(newSlice));
} else {
dispatch((0, _utils2.safeInsert)(newSlice.content)(tr));
}
return true;
}
return false;
};
}
function handleMarkdown(markdownSlice, queueCardsFromChangedTr, from, to) {
return function (state, dispatch) {
var tr = (0, _prosemirrorHistory.closeHistory)(state.tr);
var pastesFrom = typeof from === 'number' ? from : tr.selection.from;
if (typeof from === 'number' && typeof to === 'number') {
tr.replaceRange(from, to, markdownSlice);
} else {
tr.replaceSelection(markdownSlice);
}
var textPosition = tr.doc.resolve(Math.min(pastesFrom + markdownSlice.size, tr.doc.content.size));
tr.setSelection(_state.TextSelection.near(textPosition, -1));
queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 || queueCardsFromChangedTr(state, tr, _analytics.INPUT_METHOD.CLIPBOARD);
if (dispatch) {
dispatch(tr.scrollIntoView());
}
return true;
};
}
function removePrecedingBackTick(tr) {
var _tr$selection = tr.selection,
nodeBefore = _tr$selection.$from.nodeBefore,
from = _tr$selection.from;
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (nodeBefore && nodeBefore.isText && nodeBefore.text.endsWith('`')) {
tr.delete(from - 1, from);
}
}
function hasInlineCode(state, slice) {
return slice.content.firstChild && slice.content.firstChild.marks.some(function (m) {
return m.type === state.schema.marks.code;
});
}
function rollupLeafListItems(list, leafListItems) {
list.content.forEach(function (child) {
if ((0, _utils.isListNode)(child) || (0, _utils.isListItemNode)(child) && (0, _utils.isListNode)(child.firstChild)) {
rollupLeafListItems(child, leafListItems);
} else {
leafListItems.push(child);
}
});
}
function shouldFlattenList(state, slice) {
var node = slice.content.firstChild;
return node && (0, _coreUtils.insideTable)(state) && (0, _utils.isListNode)(node) && slice.openStart > slice.openEnd;
}
function sliceHasTopLevelMarks(slice) {
var hasTopLevelMarks = false;
slice.content.descendants(function (node) {
if (node.marks.length > 0) {
hasTopLevelMarks = true;
}
return false;
});
return hasTopLevelMarks;
}
function getTopLevelMarkTypesInSlice(slice) {
var markTypes = new Set();
slice.content.descendants(function (node) {
node.marks.map(function (mark) {
return mark.type;
}).forEach(function (markType) {
return markTypes.add(markType);
});
return false;
});
return markTypes;
}
/**
* Peels container wrapper nodes (e.g. panel, expand) added by ProseMirror's addContext()
* so that fontSize-marked paragraphs become top-level, preserving the mark on paste.
*/
function unwrapContainerNodesWithBlockMarks(slice, schema, fontSize) {
var content = slice.content;
var levelsUnwrapped = 0;
while (content.childCount === 1 && content.firstChild && !content.firstChild.isTextblock && slice.openStart - levelsUnwrapped > 1) {
var hasBlockMarkedParagraph = false;
for (var i = 0; i < content.firstChild.childCount; i++) {
var child = content.firstChild.child(i);
if (child.type === schema.nodes.paragraph && child.marks.some(function (m) {
return m.type === fontSize;
})) {
hasBlockMarkedParagraph = true;
break;
}
}
if (!hasBlockMarkedParagraph) {
break;
}
content = content.firstChild.content;
levelsUnwrapped++;
}
if (levelsUnwrapped === 0) {
return slice;
}
return new _model.Slice(content, slice.openStart - levelsUnwrapped, Math.max(0, slice.openEnd - levelsUnwrapped));
}
/**
* Returns the fontSize attrs to apply at the paste destination, or false if none.
* Checks list and task destinations in priority order.
*/
function getDestinationFontSizeAttrs(destinationListNode, isInSmallTaskContext, $from, currentNode, fontSize) {
if (destinationListNode) {
return (0, _lists.getFirstParagraphBlockMarkAttrs)(destinationListNode, fontSize);
}
if (isInSmallTaskContext) {
return (0, _lists.getBlockMarkAttrs)($from.parent, fontSize) || (0, _lists.getFirstParagraphBlockMarkAttrs)(currentNode, fontSize);
}
return false;
}
/**
* Resolves which marks to apply to a paragraph node after filtering forbidden marks.
* When a destination block mark is provided, replaces any existing fontSize mark with it.
* When normalizing for the target context, removes the fontSize mark entirely.
* Otherwise returns the filtered marks unchanged.
*/
function resolveParagraphMarks(marks, destinationBlockMarkAttrs, shouldNormalize, fontSize) {
if (destinationBlockMarkAttrs) {
return marks.filter(function (m) {
return m.type !== fontSize;
}).concat(fontSize.create(destinationBlockMarkAttrs));
}
if (shouldNormalize) {
return marks.filter(function (m) {
return m.type !== fontSize;
});
}
return marks;
}
function handleParagraphBlockMarks(state, slice) {
var _findParentNodeOfType2;
if (slice.content.size === 0) {
return slice;
}
var schema = state.schema,
selection = state.selection,
$from = state.selection.$from;
var _schema$nodes3 = schema.nodes,
bulletList = _schema$nodes3.bulletList,
orderedList = _schema$nodes3.orderedList,
blockTaskItem = _schema$nodes3.blockTaskItem,
taskItem = _schema$nodes3.taskItem,
paragraph = _schema$nodes3.paragraph,
heading = _schema$nodes3.heading;
var fontSize = schema.marks.fontSize;
var isSmallFontSizeEnabled = !!fontSize && (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true);
// When copying from inside a container (e.g. panel, expand), ProseMirror wraps the
// content back in the container via addContext(), increasing openStart/openEnd. Unwrap
// so the paragraph (with its fontSize mark) becomes top-level.
if (isSmallFontSizeEnabled) {
slice = unwrapContainerNodesWithBlockMarks(slice, schema, fontSize);
}
var destinationListNode = (_findParentNodeOfType2 = (0, _utils2.findParentNodeOfType)([bulletList, orderedList])(selection)) === null || _findParentNodeOfType2 === void 0 ? void 0 : _findParentNodeOfType2.node;
var currentNode = typeof $from.node === 'function' ? $from.node() : undefined;
var isInNormalTaskContext = (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === taskItem || $from.parent.type === taskItem;
var isInSmallTaskContext = !!blockTaskItem && ((currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === blockTaskItem || $from.parent.type === blockTaskItem || $from.parent.type === paragraph && $from.depth > 0 && $from.node($from.depth - 1).type === blockTaskItem);
var destinationBlockMarkAttrs = isSmallFontSizeEnabled ? getDestinationFontSizeAttrs(destinationListNode, isInSmallTaskContext, $from, currentNode, fontSize) : false;
var isInHeadingContext = $from.parent.type === heading;
// If no paragraph in the slice contains marks, there's no need for special handling
// unless we're pasting into a small-text list and need to add the destination block mark.
// Note: this doesn't check for marks applied to lower level nodes such as text
if (!sliceHasTopLevelMarks(slice) && !destinationBlockMarkAttrs) {
return slice;
}
var shouldNormalizeFontSizeForTarget = isSmallFontSizeEnabled && (!!destinationListNode || isInNormalTaskContext || isInSmallTaskContext || isInHeadingContext);
// If pasting a single paragraph into pre-existing content, match destination formatting.
// For bullet/ordered lists under small-text, we still need to normalize the paragraph block mark
// so pasted content adopts the destination list state.
var destinationHasContent = $from.parent.textContent.length > 0;
if (slice.content.childCount === 1 && destinationHasContent && !shouldNormalizeFontSizeForTarget) {
return slice;
}
// Check the parent of (paragraph -> text) because block marks are assigned to a wrapper
// element around the paragraph node
var grandparent = $from.node(Math.max(0, $from.depth - 1));
var markTypesInSlice = getTopLevelMarkTypesInSlice(slice);
var forbiddenMarkTypes = [];
var _iterator = _createForOfIteratorHelper(markTypesInSlice),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var markType = _step.value;
if (!grandparent.type.allowsMarkType(markType)) {
forbiddenMarkTypes.push(markType);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
var normalizedContent = (0, _utils.mapSlice)(slice, function (node) {
if (node.type === paragraph) {
var paragraphMarks = node.marks.filter(function (mark) {
return !forbiddenMarkTypes.includes(mark.type);
});
return paragraph.createChecked(undefined, node.content, resolveParagraphMarks(paragraphMarks, destinationBlockMarkAttrs, shouldNormalizeFontSizeForTarget, fontSize));
} else if (node.type === heading) {
// Preserve heading attributes to keep formatting
return heading.createChecked(node.attrs, node.content, node.marks.filter(function (mark) {
return !forbiddenMarkTypes.includes(mark.type);
}));
}
return node;
});
if (forbiddenMarkTypes.length === 0 && !shouldNormalizeFontSizeForTarget) {
// In a slice containing one or more paragraphs at the document level (not wrapped in
// another node), the first paragraph will only have its text content captured and pasted
// since openStart is 1. We decrement the open depth of the slice so it retains any block
// marks applied to it. We only care about the depth at the start of the selection so
// there's no need to change openEnd - the rest of the slice gets pasted correctly.
var openStart = Math.max(0, slice.openStart - 1);
return new _model.Slice(slice.content, openStart, slice.openEnd);
}
if (forbiddenMarkTypes.length === 0 && shouldNormalizeFontSizeForTarget) {
// When pasting into a heading, keep the original openStart so ProseMirror merges inline
// content into the heading node rather than replacing it with a paragraph.
var _openStart = isInHeadingContext ? slice.openStart : Math.max(0, slice.openStart - 1);
return new _model.Slice(normalizedContent.content, _openStart, slice.openEnd);
}
// If the paragraph or heading contains marks forbidden by the parent node
// (e.g. alignment/indentation), drop those marks from the slice. For lists under the small
// text experiment, also normalize fontSize to the destination list state.
return new _model.Slice(normalizedContent.content, slice.openStart, slice.openEnd);
}
/**
* ED-6300: When a nested list is pasted in a table cell and the slice has openStart > openEnd,
* it splits the table. As a workaround, we flatten the list to even openStart and openEnd.
*
* Note: this only happens if the first child is a list
*
* Example: copying "one" and "two"
* - zero
* - one
* - two
*
* Before:
* ul
* ┗━ li
* ┗━ ul
* ┗━ li
* ┗━ p -> "one"
* ┗━ li
* ┗━ p -> "two"
*
* After:
* ul
* ┗━ li
* ┗━ p -> "one"
* ┗━ li
* ┗━p -> "two"
*/
function flattenNestedListInSlice(slice) {
if (!slice.content.firstChild) {
return slice;
}
var listToFlatten = slice.content.firstChild;
var leafListItems = [];
rollupLeafListItems(listToFlatten, leafListItems);
var contentWithFlattenedList = slice.content.replaceChild(0, listToFlatten.type.createChecked(listToFlatten.attrs, leafListItems));
return new _model.Slice(contentWithFlattenedList, slice.openEnd, slice.openEnd);
}
var doesSliceContainBlockquoteListNodes = function doesSliceContainBlockquoteListNodes(slice, listContainerNodeTypes) {
var _firstChildOfSlice$ty, _lastChildOfSlice$typ;
var firstChildOfSlice = slice.content.firstChild;
var lastChildOfSlice = slice.content.lastChild;
var isFirstChildBlockquoteListNode = (firstChildOfSlice === null || firstChildOfSlice === void 0 || (_firstChildOfSlice$ty = firstChildOfSlice.type) === null || _firstChildOfSlice$ty === void 0 ? void 0 : _firstChildOfSlice$ty.name) === 'blockquote' && listContainerNodeTypes.some(function (nodeType) {
var _firstChildOfSlice$co;
return nodeType === (firstChildOfSlice === null || firstChildOfSlice === void 0 || (_firstChildOfSlice$co = firstChildOfSlice.content.firstChild) === null || _firstChildOfSlice$co === void 0 ? void 0 : _firstChildOfSlice$co.type);
});
var isLastChildBlockquoteListNode = (lastChildOfSlice === null || lastChildOfSlice === void 0 || (_lastChildOfSlice$typ = lastChildOfSlice.type) === null || _lastChildOfSlice$typ === void 0 ? void 0 : _lastChildOfSlice$typ.name) === 'blockquote' && listContainerNodeTypes.some(function (nodeType) {
var _lastChildOfSlice$con;
return nodeType === (lastChildOfSlice === null || lastChildOfSlice === void 0 || (_lastChildOfSlice$con = lastChildOfSlice.content.firstChild) === null || _lastChildOfSlice$con === void 0 ? void 0 : _lastChildOfSlice$con.type);
});
return isFirstChildBlockquoteListNode || isLastChildBlockquoteListNode;
};
function handleRichText(slice, queueCardsFromChangedTr) {
return function (state, dispatch) {
var _slice$content, _slice$content2, _findParentNodeOfType3, _firstChildOfSlice$ty2, _lastChildOfSlice$typ2, _panelParentOverCurre;
var _state$schema$nodes4 = state.schema.nodes,
codeBlock = _state$schema$nodes4.codeBlock,
heading = _state$schema$nodes4.heading,
paragraph = _state$schema$nodes4.paragraph,
panel = _state$schema$nodes4.panel,
panel_c1 = _state$schema$nodes4.panel_c1,
bulletList = _state$schema$nodes4.bulletList,
orderedList = _state$schema$nodes4.orderedList;
var fontSize = state.schema.marks.fontSize;
var selection = state.selection,
schema = state.schema;
var firstChildOfSlice = (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.firstChild;
var lastChildOfSlice = (_slice$content2 = slice.content) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.lastChild;
var listContainerNodeTypes = [bulletList, orderedList];
var isSmallFontSizeEnabled = !!fontSize && (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true);
var destinationListNode = isSmallFontSizeEnabled ? (_findParentNodeOfType3 = (0, _utils2.findParentNodeOfType)(listContainerNodeTypes)(selection)) === null || _findParentNodeOfType3 === void 0 ? void 0 : _findParentNodeOfType3.node : undefined;
var destinationListFontSizeAttrs = isSmallFontSizeEnabled ? (0, _lists.getFirstParagraphBlockMarkAttrs)(destinationListNode, fontSize) : false;
// In case user is pasting inline code,
// any backtick ` immediately preceding it should be removed.
var tr = state.tr;
if (hasInlineCode(state, slice)) {
removePrecedingBackTick(tr);
}
if (shouldFlattenList(state, slice)) {
slice = flattenNestedListInSlice(slice);
}
(0, _prosemirrorHistory.closeHistory)(tr);
var isFirstChildListNode = (0, _utils.isListNode)(firstChildOfSlice);
var isLastChildListNode = (0, _utils.isListNode)(lastChildOfSlice);
var isSliceContentListNodes = isFirstChildListNode || isLastChildListNode;
var isFirstChildTaskListNode = (firstChildOfSlice === null || firstChildOfSlice === void 0 || (_firstChildOfSlice$ty2 = firstChildOfSlice.type) === null || _firstChildOfSlice$ty2 === void 0 ? void 0 : _firstChildOfSlice$ty2.name) === 'taskList';
var isLastChildTaskListNode = (lastChildOfSlice === null || lastChildOfSlice === void 0 || (_lastChildOfSlice$typ2 = lastChildOfSlice.type) === null || _lastChildOfSlice$typ2 === void 0 ? void 0 : _lastChildOfSlice$typ2.name) === 'taskList';
var isSliceContentTaskListNodes = isFirstChildTaskListNode || isLastChildTaskListNode;
var sliceContentBlockquoteListNodes = doesSliceContainBlockquoteListNodes(slice, listContainerNodeTypes);
// Compute once and reuse below to avoid traversing the slice twice.
var sliceMarkTypes = isSmallFontSizeEnabled ? getTopLevelMarkTypesInSlice(slice) : new Set();
var destinationIsEmpty = isSmallFontSizeEnabled && selection.$from.parent.type === paragraph && selection.$from.parent.textContent.length === 0;
// Capture the destination paragraph's non-fontSize block marks (e.g. alignment, indentation)
// before the paste so they can be restored if the paste replaces the paragraph entirely
// (which happens when small text is pasted with openStart=0).
var destinationNonFontSizeBlockMarks = isSmallFontSizeEnabled && selection.$from.parent.type === paragraph && sliceMarkTypes.has(fontSize) ? selection.$from.parent.marks.filter(function (m) {
return m.type !== fontSize;
}) : [];
// We want to use safeInsert to insert invalid content, as it inserts at the closest non schema violating position
// rather than spliting the selection parent node in half (which is what replaceSelection does)
// Exception is paragraph and heading nodes, these should be split, provided their parent supports the pasted content
var textNodes = [heading, paragraph];
var selectionParent = selection.$to.node(selection.$to.depth - 1);
var noNeedForSafeInsert = selection.$to.node().type.validContent(slice.content) || textNodes.includes(selection.$to.node().type) && selectionParent.type.validContent(slice.content);
// panel_c1 is a schema variant of panel; when the experiment is on, treat both together.
var panelParentOverCurrentSelection = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? (0, _utils2.findParentNodeOfType)([panel, panel_c1].filter(Boolean))(tr.selection) : (0, _utils2.findParentNodeOfType)(panel)(tr.selection);
var isTargetPanelEmpty = panelParentOverCurrentSelection && ((_panelParentOverCurre = panelParentOverCurrentSelection.node) === null || _panelParentOverCurre === void 0 ? void 0 : _panelParentOverCurre.content.size) === 2;
if (!isSliceContentTaskListNodes && (isSliceContentListNodes || isTargetPanelEmpty)) {
(0, _edgeCases.insertSliceForLists)({
tr: tr,
slice: slice,
schema: schema
});
} else if (noNeedForSafeInsert && !((0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true) && checkTaskListInList(state, slice))) {
var _firstChildOfSlice$ty3, _firstChildOfSlice$co2, _firstChildOfSlice$co3;
if ((firstChildOfSlice === null || firstChildOfSlice === void 0 || (_firstChildOfSlice$ty3 = firstChildOfSlice.type) === null || _firstChildOfSlice$ty3 === void 0 ? void 0 : _firstChildOfSlice$ty3.name) === 'blockquote' && firstChildOfSlice !== null && firstChildOfSlice !== void 0 && (_firstChildOfSlice$co2 = firstChildOfSlice.content.firstChild) !== null && _firstChildOfSlice$co2 !== void 0 && _firstChildOfSlice$co2.type.name && ['bulletList', 'orderedList', 'mediaSingle'].includes(firstChildOfSlice === null || firstChildOfSlice === void 0 || (_firstChildOfSlice$co3 = firstChildOfSlice.content.firstChild) === null || _firstChildOfSlice$co3 === void 0 ? void 0 : _firstChildOfSlice$co3.type.name)) {
// checks if parent node is a blockquote and child node is either a bulletlist or orderedlist or mediaSingle
(0, _edgeCases.insertSliceInsideBlockquote)({
tr: tr,
slice: slice
});
} else {
var _slice$content$lastCh;
tr.replaceSelection(slice);
// when cursor is inside a table cell, and slice.content.lastChild is a panel, expand, or decisionList
// need to make sure the cursor position is is right after the panel, expand, or decisionList
// still in the same table cell, see issue: https://product-fabric.atlassian.net/browse/ED-17862
var shouldUpdateCursorPosAfterPaste = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? ['panel', 'nestedExpand', 'decisionList', 'codeBlock'].includes(slice.content.lastChild ? (0, _nodeTypeUtils.getBaseNodeTypeName)(slice.content.lastChild.type) : '') : ['panel', 'nestedExpand', 'decisionList', 'codeBlock'].includes(((_slice$content$lastCh = slice.content.lastChild) === null || _slice$content$lastCh === void 0 || (_slice$content$lastCh = _slice$content$lastCh.type) === null || _slice$content$lastCh === void 0 ? void 0 : _slice$content$lastCh.name) || '');
var lastChild = slice.content.lastChild;
var $nextPos = tr.doc.resolve(tr.mapping.map(selection.from));
var nextSelection = lastChild !== null && lastChild !== void 0 && lastChild.type.isTextblock ? _state.TextSelection.findFrom($nextPos, -1, true) : new _selection.GapCursorSelection($nextPos, _selection.Side.RIGHT);
if (nextSelection) {
tr.setSelection(nextSelection);
} else if ((0, _utils.insideTableCell)(state) && shouldUpdateCursorPosAfterPaste) {
var nextPos = tr.doc.resolve(tr.mapping.map(selection.$from.pos));
tr.setSelection(new _selection.GapCursorSelection(nextPos, _selection.Side.RIGHT));
}
}
} else {
// need to scan the slice if there's a block node or list items inside it
var sliceHasList = false;
slice.content.nodesBetween(0, slice.content.size, function (node) {
if (node.type === state.schema.nodes.listItem) {
sliceHasList = true;
return false;
}
});
if ((0, _utils.insideTableCell)(state) && (0, _utils.isInListItem)(state) && (0, _utils2.canInsert)(selection.$from, slice.content) && (0, _utils2.canInsert)(selection.$to, slice.content) || sliceHasList) {
tr.replaceSelection(slice);
} 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)) {
// Corrected task-into-list insertion is gated behind the flexible list indentation
// experiment and protected by the kill switch.
tr = (0, _edgeCases.insertSliceForTaskInsideList)({
tr: tr,
slice: slice
});
} else if (checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
// Legacy path (experiment OFF or kill switch ON): preserve the previous over-nesting
// behaviour via the function's internal fallback.
tr = (0, _edgeCases.insertSliceForTaskInsideList)({
tr: tr,
slice: slice
});
} else {
// need safeInsert rather than replaceSelection, so that nodes aren't split in half
// e.g. when pasting a layout into a table, replaceSelection splits the table in half and adds the layout in the middle
tr = (0, _utils2.safeInsert)(slice.content, tr.selection.$to.pos)(tr);
if (checkTaskListInList(state, slice)) {
(0, _edgeCases.updateSelectionAfterReplace)({
tr: tr
});
}
}
}
// font size handling for pasting into lists or blockquotes
if (isSmallFontSizeEnabled && (isSliceContentListNodes || sliceContentBlockquoteListNodes)) {
var containingList = (0, _utils2.findParentNodeOfTypeClosestToPos)(tr.selection.$from, listContainerNodeTypes);
if (containingList) {
(0, _lists.reconcileBlockMarkForContainerAtPos)(tr, containingList.pos, fontSize, isSliceContentListNodes ? destinationListFontSizeAttrs : false);
}
}
// font size handling for pasting into paragraphs (normal text) - preserve source style
if (isSmallFontSizeEnabled && destinationIsEmpty && !sliceMarkTypes.has(fontSize) && !destinationListNode && selection.$from.parent.type === paragraph && (0, _lists.getBlockMarkAttrs)(selection.$from.parent, fontSize)) {
(0, _lists.reconcileBlockMarkForParagraphAtPos)(tr, tr.mapping.map(selection.$from.pos), fontSize, false);
}
// Restore destination block marks (e.g. alignment) that were lost when pasting small text
// replaced the paragraph entirely (openStart=0 from container unwrap).
if (isSmallFontSizeEnabled && destinationNonFontSizeBlockMarks.length > 0) {
var pastedFrom = tr.mapping.map(selection.from, -1);
var pastedTo = tr.mapping.map(selection.to, 1);
var _iterator2 = _createForOfIteratorHelper(destinationNonFontSizeBlockMarks),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var mark = _step2.value;
(0, _lists.reconcileBlockMarkInRange)(tr, pastedFrom, pastedTo, mark.type, mark.attrs);
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
}
tr.setStoredMarks([]);
if (tr.selection.empty && tr.selection.$from.parent.type === codeBlock) {
tr.setSelection(_state.TextSelection.near(tr.selection.$from, 1));
}
tr.scrollIntoView();
// queue link cards, ignoring any errors
queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 || queueCardsFromChangedTr(state, tr, _analytics.INPUT_METHOD.CLIPBOARD);
if (dispatch) {
dispatch(tr);
}
return true;
};
}
function isUrlString(text) {
try {
new URL(text);
return true;
} catch (_unused2) {
return false;
}
}
function isLinkOrUrlString(slice, schema) {
if (slice.content.childCount !== 1 || !(0, _utils.isParagraph)(slice.content.child(0), schema)) {
return false;
}
var paragraph = slice.content.child(0);
if (paragraph.content.childCount !== 1 || !(0, _utils.isText)(paragraph.content.child(0), schema)) {
return false;
}
var _paragraph$content$ch = paragraph.content.child(0),
marks = _paragraph$content$ch.marks,
_paragraph$content$ch2 = _paragraph$content$ch.text,
text = _paragraph$content$ch2 === void 0 ? '' : _paragraph$content$ch2;
var hasLinkMark = marks.some(function (mark) {
return (0, _utils.isLinkMark)(mark, schema);
});
return hasLinkMark || isUrlString(text);
}
function handlePasteIntoCaption(slice) {
return function (state, dispatch) {
if (isLinkOrUrlString(slice, state.schema)) {
return false;
}
var caption = state.schema.nodes.caption;
var tr = state.tr;
if ((0, _utils2.hasParentNodeOfType)(caption)(state.selection)) {
// We let PM replace the selection and it will insert as text where it can't place the node
// This is totally fine as caption is just a simple block that only contains inline contents
// And it is more in line with WYSIWYG expectations
tr.replaceSelection(slice).scrollIntoView();
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
}
var handleSelectedTable = exports.handleSelectedTable = function handleSelectedTable(editorAnalyticsAPI) {
return function (slice) {
return function (state, dispatch) {
var tr = (0, _utils3.replaceSelectedTable)(state, slice);
// add analytics after replacing selected table
tr = (0, _index.addReplaceSelectedTableAnalytics)(state, tr, editorAnalyticsAPI);
if (tr.docChanged) {
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
};
};
function checkTaskListInList(state, slice) {
var _slice$content$firstC4;
return Boolean((0, _utils.isInListItem)(state) && ['taskList', 'taskItem'].includes(((_slice$content$firstC4 = slice.content.firstChild) === null || _slice$content$firstC4 === void 0 || (_slice$content$firstC4 = _slice$content$firstC4.type) === null || _slice$content$firstC4 === void 0 ? void 0 : _slice$content$firstC4.name) || ''));
}
function checkIfSelectionInNestedList(state) {
var selection = state.selection,
tr = state.tr;
var _state$schema$nodes5 = state.schema.nodes,
orderedList = _state$schema$nodes5.orderedList,
bulletList = _state$schema$nodes5.bulletList,
listItem = _state$schema$nodes5.listItem;
var selectionParentListItemNode = (0, _utils2.findParentNodeOfType)(listItem)(selection);
var selectionParentListNodeWithPos = (0, _utils2.findParentNodeOfType)([bulletList, orderedList])(selection);
if (!selectionParentListItemNode || !selectionParentListNodeWithPos) {
return false;
}
var grandParentListNode = (0, _utils2.findParentNodeOfTypeClosestToPos)(tr.doc.resolve(selectionParentListNodeWithPos.pos), [bulletList, orderedList]);
var selectionIsInNestedList = !!grandParentListNode;
var selectedListItemHasNestedList = false;
selectionParentListItemNode.node.content.forEach(function (child) {
if ((0, _utils.isListNode)(child)) {
selectedListItemHasNestedList = true;
}
});
return selectedListItemHasNestedList || selectionIsInNestedList;
}
// Helper function to filter expand nodes from slice when not allowed
function handlePasteExpand(slice) {
return (0, _utils.mapSlice)(slice, function (node) {
if (node.type.name === 'expand' || node.type.name === 'nestedExpand') {
var children = [];
if (node.attrs.title) {
children.push(node.type.schema.nodes.paragraph.createChecked(undefined, _model.Fragment.from(node.type.schema.text(node.attrs.title))));
}
node.content.forEach(function (inner) {
return children.push(inner);
});
return children;
}
return node;
});
}
/*
* Transform container nodes promoting them to _c1 if required as per
* transformContainerNodes from adf-utils
*/
function applyContainerNodeTransformToSlice(slice, schema, destinationParentType) {
var _sliceJson$content;
var sliceJson = slice.toJSON();
if (!sliceJson) {
return slice;
}
var wrappedAdf = {
type: destinationParentType,
content: (_sliceJson$content = sliceJson.content) !== null && _sliceJson$content !== void 0 ? _sliceJson$content : []
};
try {
var _transformContainerNo = (0, _transforms.transformContainerNodes)(wrappedAdf, schema),
transformedAdf = _transformContainerNo.transformedAdf,
isTransformed = _transformContainerNo.isTransformed;
return isTransformed && transformedAdf && transformedAdf.content && transformedAdf.content.length > 0 ? _model.Slice.fromJSON(schema, _objectSpread(_objectSpread({}, sliceJson), {}, {
content: transformedAdf.content
})) : slice;
} catch (_unused3) {
return slice;
}
}
/*
* When platform_editor_nest_table_in_panel is OFF, move table elements out of panel divs in the
* clipboard HTML before ProseMirror parses it. This prevents the panel schema from
* dropping table content entirely.
*/
function splitTablesOutOfPanelHtml(html) {
if (!html.includes('data-panel-type')) {
return html;
}
var doc = new DOMParser().parseFromString(html, 'text/html');
var panelDivs = Array.from(doc.querySelectorAll('div[data-panel-type]'));
var changed = false;
for (var _i = 0, _panelDivs = panelDivs; _i < _panelDivs.length; _i++) {
var panelDiv = _panelDivs[_i];
var editorTableWrappers = Array.from(panelDiv.querySelectorAll('[data-prosemirror-node-name="table"]'));
var rendererTableWrappers = (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_nest_in_table_renderer_paste') ? Array.from(panelDiv.querySelectorAll('.pm-table-container')).filter(function (wrapper) {
return wrapper.querySelector('table') !== null;
}) : [];
var tableWrappers = [].concat(editorTableWrappers, (0, _toConsumableArray2.default)(rendererTableWrappers));
if (tableWrappers.length === 0) {
continue;
}
changed = true;
var anchor = panelDiv;
var _iterator3 = _createForOfIteratorHelper(tableWrappers),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var _tableWrapper$parentN, _anchor$parentNode;
var tableWrapper = _step3.value;
(_tableWrapper$parentN = tableWrapper.parentNode) === null || _tableWrapper$parentN === void 0 || _tableWrapper$parentN.removeChild(tableWrapper);
(_anchor$parentNode = anchor.parentNode) === null || _anchor$parentNode === void 0 || _anchor$parentNode.insertBefore(tableWrapper, anchor.nextSibling);
anchor = tableWrapper;
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
}
return changed ? doc.body.innerHTML : html;
}