UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

258 lines (251 loc) • 11.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.areNodesEqualIgnoreAttrs = areNodesEqualIgnoreAttrs; exports.bracketTyped = bracketTyped; exports.getChangedNodes = getChangedNodes; exports.getStepRange = void 0; exports.hasDocAsParent = hasDocAsParent; exports.hasVisibleContent = hasVisibleContent; exports.isEmptyDocument = isEmptyDocument; exports.isSelectionEndOfParagraph = exports.isReplaceDocOperation = void 0; exports.nodesBetweenChanged = nodesBetweenChanged; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _clamp = _interopRequireDefault(require("lodash/clamp")); var _transform = require("@atlaskit/editor-prosemirror/transform"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _editorCoreUtils = require("./editor-core-utils"); 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; } 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; } var getStepRange = exports.getStepRange = function getStepRange(transaction) { var from = -1; var to = -1; transaction.mapping.maps.forEach(function (stepMap, index) { stepMap.forEach(function (oldStart, oldEnd) { var newStart = transaction.mapping.slice(index).map(oldStart, -1); var newEnd = transaction.mapping.slice(index).map(oldEnd); var docSize = transaction.doc.content.size; from = (0, _clamp.default)(newStart < from || from === -1 ? newStart : from, 0, docSize); to = (0, _clamp.default)(newEnd > to || to === -1 ? newEnd : to, 0, docSize); }); }); if (from !== -1) { return { from: from, to: to }; } return null; }; // Checks to see if the parent node is the document, ie not contained within another entity function hasDocAsParent($anchor) { return $anchor.depth === 1; } /** * Checks if a node looks like an empty document */ function isEmptyDocument(node) { var nodeChild = node.content.firstChild; if (node.childCount !== 1 || !nodeChild) { return false; } return (0, _editorCoreUtils.isEmptyParagraph)(nodeChild); } function bracketTyped(state) { var selection = state.selection; var _ref = selection, $cursor = _ref.$cursor, $anchor = _ref.$anchor; if (!$cursor) { return false; } var node = $cursor.nodeBefore; if (!node) { return false; } if (node.type.name === 'text' && node.text === '{') { var paragraphNode = $anchor.node(); return paragraphNode.marks.length === 0 && hasDocAsParent($anchor); } return false; } function nodesBetweenChanged(tr, f, startPos) { var stepRange = getStepRange(tr); if (!stepRange) { return; } tr.doc.nodesBetween(stepRange.from, stepRange.to, f, startPos); } /** * Returns false if node contains only empty inline nodes and hardBreaks. */ function hasVisibleContent(node) { var isInlineNodeHasVisibleContent = function isInlineNodeHasVisibleContent(inlineNode) { return inlineNode.isText ? !!inlineNode.textContent.trim() : inlineNode.type.name !== 'hardBreak'; }; if (node.isInline) { return isInlineNodeHasVisibleContent(node); } else if (node.isBlock && (node.isLeaf || node.isAtom)) { return true; } else if (!node.childCount) { return false; } for (var _index = 0; _index < node.childCount; _index++) { var child = node.child(_index); var invisibleNodeTypes = ['paragraph', 'text', 'hardBreak']; if (!invisibleNodeTypes.includes(child.type.name) || hasVisibleContent(child)) { return true; } } return false; } var isSelectionEndOfParagraph = exports.isSelectionEndOfParagraph = function isSelectionEndOfParagraph(state) { return state.selection.$to.parent.type === state.schema.nodes.paragraph && state.selection.$to.pos === state.doc.resolve(state.selection.$to.pos).end(); }; function getChangedNodesIn(_ref2) { var tr = _ref2.tr, doc = _ref2.doc; var nodes = []; var stepRange = getStepRange(tr); if (!stepRange) { return nodes; } var from = Math.min(doc.nodeSize - 2, stepRange.from); var to = Math.min(doc.nodeSize - 2, stepRange.to); doc.nodesBetween(from, to, function (node, pos) { nodes.push({ node: node, pos: pos }); }); return nodes; } function getChangedNodes(tr) { return getChangedNodesIn({ tr: tr, doc: tr.doc }); } // When document first load in Confluence, initially it is an empty document // and Collab service triggers a transaction to replace the empty document with the real document that should be rendered. // isReplaceDocumentOperation is checking if the transaction is the one that replace the empty document with the real document var isReplaceDocOperation = exports.isReplaceDocOperation = function isReplaceDocOperation(transactions, oldState) { return transactions.some(function (tr) { if (tr.getMeta('replaceDocument')) { return true; } var hasStepReplacingEntireDocument = tr.steps.some(function (step) { if (!(step instanceof _transform.ReplaceStep)) { return false; } var isStepReplacingFromDocStart = step.from === 0; var isStepReplacingUntilTheEndOfDocument = step.to === oldState.doc.content.size; if (!isStepReplacingFromDocStart || !isStepReplacingUntilTheEndOfDocument) { return false; } return true; }); return hasStepReplacingEntireDocument; }); }; function marksEqualInOrder(m1, m2) { if (m1.length !== m2.length) return false; return m1.every(function (m, i) { return m.eq(m2[i]); }); } function marksEqualIgnoringOrder(m1, m2) { if (m1.length !== m2.length) { return false; } var m2Used = new Set(); var _iterator = _createForOfIteratorHelper(m1), _step; try { var _loop = function _loop() { var mark1 = _step.value; var idx = m2.findIndex(function (mark2, i) { return !m2Used.has(i) && mark1.eq(mark2); }); if (idx === -1) { return { v: false }; } m2Used.add(idx); }, _ret; for (_iterator.s(); !(_step = _iterator.n()).done;) { _ret = _loop(); if (_ret) return _ret.v; } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return true; } /** * Compares two ProseMirror documents for equality, ignoring attributes * which don't affect the document structure. * * This is almost a copy of the .eq() PM function - tweaked to ignore attrs * * @param doc1 PMNode * @param doc2 PMNode * @param attributesToIgnore Specific array of attribute keys to ignore - defaults to ignoring all * @param opts.ignoreMarkOrder If mark order should be ignored to still be equal (e.g. reversed annotation marks). When not provided, controlled by platform_editor_are_nodes_equal_ignore_mark_order feature gate (defaults to true when gate is on). * @returns boolean */ function areNodesEqualIgnoreAttrs(node1, node2, attributesToIgnore, opts) { var _opts$ignoreMarkOrder; var ignoreMarkOrder = (_opts$ignoreMarkOrder = opts === null || opts === void 0 ? void 0 : opts.ignoreMarkOrder) !== null && _opts$ignoreMarkOrder !== void 0 ? _opts$ignoreMarkOrder : (0, _expValEquals.expValEquals)('platform_editor_are_nodes_equal_ignore_mark_order', 'isEnabled', true); if (node1.isText) { if (ignoreMarkOrder) { return node1.text === node2.text && marksEqualIgnoringOrder(node1.marks, node2.marks); } return node1.eq(node2); } var marksEqual = ignoreMarkOrder ? marksEqualIgnoringOrder(node1.marks, node2.marks) : marksEqualInOrder(node1.marks, node2.marks); // If no attributes to ignore, compare all attributes if (!attributesToIgnore || attributesToIgnore.length === 0) { if ((0, _expValEquals.expValEquals)('platform_editor_are_nodes_equal_ignore_mark_order', 'isEnabled', true)) { return node1 === node2 || node1.hasMarkup(node2.type, node1.attrs, node1.marks) && marksEqual && areFragmentsEqual(node1.content, node2.content, undefined, opts); } else { return node1 === node2 || node1.hasMarkup(node2.type, node1.attrs, node2.marks) && areFragmentsEqual(node1.content, node2.content); } } // Build attrs to compare by excluding ignored attributes var attrsToCompare = (0, _expValEquals.expValEquals)('platform_editor_show_diff_fix_missing_attrs', 'isEnabled', true) ? _objectSpread({}, node2.attrs) : node2.attrs; var ignoreSet = new Set(attributesToIgnore); for (var key in node2.attrs) { if (ignoreSet.has(key)) { attrsToCompare[key] = node1.attrs[key]; } } if ((0, _expValEquals.expValEquals)('platform_editor_are_nodes_equal_ignore_mark_order', 'isEnabled', true)) { return node1 === node2 || node1.type === node2.type && node1.hasMarkup(node2.type, attrsToCompare, node1.marks) && marksEqual && areFragmentsEqual(node1.content, node2.content, attributesToIgnore, opts); } else { return node1 === node2 || node1.hasMarkup(node2.type, attrsToCompare, node2.marks) && areFragmentsEqual(node1.content, node2.content, attributesToIgnore); } } function areFragmentsEqual(frag1, frag2, attributesToIgnore, opts) { if (frag1.content.length !== frag2.content.length) { return false; } var childrenEqual = true; frag1.content.forEach(function (child, i) { var otherChild = frag2.child(i); if (child === otherChild || otherChild && areNodesEqualIgnoreAttrs(child, otherChild, attributesToIgnore, opts)) { return; } childrenEqual = false; }); return childrenEqual; }