UNPKG

@atlaskit/editor-common

Version:

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

947 lines (935 loc) • 27.2 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.markOrder = exports.isSubSupType = exports.isSameMark = exports.getValidUnknownNode = exports.getValidNode = exports.getValidMark = exports.getValidDocument = exports.getValidContent = exports.getMarksByOrder = exports.ADFStages = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _adfSchema = require("@atlaskit/adf-schema"); var _schemaDefault = require("@atlaskit/adf-schema/schema-default"); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); 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; } var ADFStages = exports.ADFStages = { FINAL: 'final', STAGE_0: 'stage0' }; /* * An ADF Document JSON object. The document is the root node and documents are * composed of nodes. This type accepts an array of ADNode types as content. * * It is basically the same as the JSONNodeDoc interface from editor-json-transformer. * * Do not use this type for content nodes as they require additional attributes. * * Use ADNode instead for content nodes (any node other than the doc). */ /* * An ADF Node object. This type is used as content for the ADDoc interface. * It is basically the same as the JSONNode type from editor-json-transformer * but the types are a little more strict. * * It is a serialisable form of ADFEntity. * * Do not use this for ADF documents - they should use the ADDoc interface. */ /* * It's important that this order follows the marks rank defined here: * https://product-fabric.atlassian.net/wiki/spaces/E/pages/11174043/Document+structure#Documentstructure-Rank */ var markOrder = exports.markOrder = ['fragment', 'link', 'em', 'strong', 'textColor', 'strike', 'subsup', 'underline', 'code', 'confluenceInlineComment', 'annotation', 'dataConsumer']; var isSubSupType = exports.isSubSupType = function isSubSupType(type) { return type === 'sub' || type === 'sup'; }; /* * Sorts mark by the predefined order above */ var getMarksByOrder = exports.getMarksByOrder = function getMarksByOrder(marks) { return (0, _toConsumableArray2.default)(marks).sort(function (a, b) { return markOrder.indexOf(a.type.name) - markOrder.indexOf(b.type.name); }); }; /* * Check if two marks are the same by comparing type and attrs */ var isSameMark = exports.isSameMark = function isSameMark(mark, otherMark) { if (!mark || !otherMark) { return false; } return mark.eq(otherMark); }; var getValidDocument = exports.getValidDocument = function getValidDocument(doc) { var schema = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _schemaDefault.defaultSchema; var adfStage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'final'; var node = getValidNode(doc, schema, adfStage); if (node.type === 'doc') { node.content = wrapInlineNodes(node.content); return node; } return null; }; var wrapInlineNodes = function wrapInlineNodes() { var nodes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; return nodes.map(function (node) { return _adfSchema.inlineNodes.has(node.type) ? { type: 'paragraph', content: [node] } : node; }); }; var getValidContent = exports.getValidContent = function getValidContent(content) { var schema = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _schemaDefault.defaultSchema; var adfStage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'final'; return content.map(function (node) { return getValidNode(node, schema, adfStage); }); }; var TEXT_COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/; var RELATIVE_LINK = /^\//; var ANCHOR_LINK = /^#/; var flattenUnknownBlockTree = function flattenUnknownBlockTree(node) { var schema = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _schemaDefault.defaultSchema; var adfStage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'final'; var output = []; var isPrevLeafNode = false; for (var i = 0; i < node.content.length; i++) { var childNode = node.content[i]; var isLeafNode = !(childNode.content && childNode.content.length); if (i > 0) { if (isPrevLeafNode) { output.push({ type: 'text', text: ' ' }); } else { output.push({ type: 'hardBreak' }); } } if (isLeafNode) { output.push(getValidNode(childNode, schema, adfStage)); } else { output.push.apply(output, (0, _toConsumableArray2.default)(flattenUnknownBlockTree(childNode, schema, adfStage))); } isPrevLeafNode = isLeafNode; } return output; }; /** * Sanitize unknown node tree * * @see https://product-fabric.atlassian.net/wiki/spaces/E/pages/11174043/Document+structure#Documentstructure-ImplementationdetailsforHCNGwebrenderer */ var getValidUnknownNode = exports.getValidUnknownNode = function getValidUnknownNode(node) { var _node$attrs = node.attrs, attrs = _node$attrs === void 0 ? {} : _node$attrs, content = node.content, text = node.text, type = node.type; if (!content || !content.length) { var unknownInlineNode = { type: 'text', text: text || attrs.text || "[".concat(type, "]") }; var textUrl = attrs.textUrl; if (textUrl && (0, _adfSchema.isSafeUrl)(textUrl)) { unknownInlineNode.marks = [{ type: 'link', attrs: { href: textUrl } }]; } return unknownInlineNode; } /* * Find leaf nodes and join them. If leaf nodes' parent node is the same node * join with a blank space, otherwise they are children of different branches, i.e. * we need to join them with a hardBreak node */ return { type: 'unknownBlock', content: flattenUnknownBlockTree(node) }; }; var getValidMarks = function getValidMarks(marks) { var adfStage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'final'; if (marks && marks.length > 0) { return marks.reduce(function (acc, mark) { var validMark = getValidMark(mark, adfStage); if (validMark) { acc.push(validMark); } return acc; }, []); } return marks; }; /* * This method will validate a Node according to the spec defined here * https://product-fabric.atlassian.net/wiki/spaces/E/pages/11174043/Document+structure#Documentstructure-Nodes * * This is also the place to handle backwards compatibility. * * If a node is not recognized or is missing required attributes, we should return 'unknown' * */ var getValidNode = exports.getValidNode = function getValidNode(originalNode) { var schema = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _schemaDefault.defaultSchema; var adfStage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'final'; var attrs = originalNode.attrs, marks = originalNode.marks, text = originalNode.text, type = originalNode.type; var content = originalNode.content; var node = { attrs: attrs, marks: marks, text: text, type: type }; if (content) { node.content = content = getValidContent(content, schema, adfStage); } // If node type doesn't exist in schema, make it an unknown node if (!schema.nodes[type]) { return getValidUnknownNode(node); } if (type) { switch (type) { case 'doc': { var _ref = originalNode, version = _ref.version; if (version && content && content.length) { return { type: type, content: content }; } break; } case 'codeBlock': { if (content) { content = content.reduce(function (acc, val) { if (val.type === 'text') { acc.push({ type: val.type, text: val.text }); } return acc; }, []); } if (attrs && attrs.language) { return { type: type, attrs: attrs, content: content, marks: marks }; } return { type: type, content: content, marks: marks }; } case 'date': { if (attrs && attrs.timestamp) { return { type: type, attrs: attrs }; } break; } case 'status': { if (attrs && attrs.text && attrs.color) { return { type: type, attrs: attrs }; } break; } case 'emoji': { if (attrs && attrs.shortName) { return { type: type, attrs: attrs }; } break; } case 'inlineExtension': case 'extension': { if (attrs && attrs.extensionType && attrs.extensionKey) { return { type: type, attrs: attrs }; } break; } case 'inlineCard': { if ((0, _platformFeatureFlags.getBooleanFF)('platform.editor.allow-inline-comments-for-inline-nodes')) { var inlineCardNode = { type: type }; if (attrs && (attrs.datasource && !attrs.url || attrs.url && (0, _adfSchema.isSafeUrl)(attrs.url) || attrs.data && attrs.data.url && (0, _adfSchema.isSafeUrl)(attrs.data.url))) { inlineCardNode.attrs = _objectSpread({}, attrs); } if (marks) { inlineCardNode.marks = (0, _toConsumableArray2.default)(marks); } return inlineCardNode; } else { if (attrs && (attrs.datasource && !attrs.url || attrs.url && (0, _adfSchema.isSafeUrl)(attrs.url) || attrs.data && attrs.data.url && (0, _adfSchema.isSafeUrl)(attrs.data.url))) { return { type: type, attrs: attrs }; } break; } } case 'blockCard': { if (attrs && (attrs.datasource && !attrs.url || attrs.url && (0, _adfSchema.isSafeUrl)(attrs.url) || attrs.data && attrs.data.url && (0, _adfSchema.isSafeUrl)(attrs.data.url))) { return { type: type, attrs: attrs }; } break; } case 'embedCard': { if (attrs && (attrs.url && (0, _adfSchema.isSafeUrl)(attrs.url) || attrs.data && attrs.data.url && (0, _adfSchema.isSafeUrl)(attrs.data.url)) && attrs.layout) { return { type: type, attrs: attrs }; } break; } case 'bodiedExtension': { if (attrs && attrs.extensionType && attrs.extensionKey && content) { return { type: type, attrs: attrs, content: content }; } break; } case 'multiBodiedExtension': { if (attrs && attrs.extensionType && attrs.extensionKey && content) { return { type: type, attrs: attrs, content: content }; } break; } case 'extensionFrame': { if (content) { return { type: type, attrs: attrs, content: content }; } break; } case 'hardBreak': { return { type: type }; } case 'caption': { if (content) { return { type: type, content: content }; } break; } case 'mediaInline': { var mediaId = ''; var mediaCollection = []; if (attrs) { var id = attrs.id, collection = attrs.collection; mediaId = id; mediaCollection = collection; } if (mediaId && mediaCollection) { return { type: type, attrs: attrs, marks: marks }; } break; } case 'media': { var _mediaId = ''; var mediaType = ''; var _mediaCollection = []; var mediaUrl = ''; if (attrs) { var _id = attrs.id, _collection = attrs.collection, _type = attrs.type, url = attrs.url; _mediaId = _id; mediaType = _type; _mediaCollection = _collection; mediaUrl = url; } if (mediaType === 'external' && !!mediaUrl) { var mediaAttrs = { type: mediaType, url: mediaUrl, width: attrs.width, height: attrs.height }; if (attrs.alt) { mediaAttrs.alt = attrs.alt; } var getMarks = getValidMarks(marks, adfStage); return getMarks ? { type: type, attrs: mediaAttrs, marks: getMarks } : { type: type, attrs: mediaAttrs }; } else if (_mediaId && mediaType) { var _mediaAttrs = { type: mediaType, id: _mediaId, collection: _mediaCollection }; if (attrs.width) { _mediaAttrs.width = attrs.width; } if (attrs.height) { _mediaAttrs.height = attrs.height; } if (attrs.alt) { _mediaAttrs.alt = attrs.alt; } var _getMarks = getValidMarks(marks, adfStage); return _getMarks ? { type: type, attrs: _mediaAttrs, marks: _getMarks } : { type: type, attrs: _mediaAttrs }; } break; } case 'mediaGroup': { if (Array.isArray(content) && !content.some(function (e) { return e.type !== 'media'; })) { return { type: type, content: content }; } break; } case 'mediaSingle': { var containsJustMedia = Array.isArray(content) && content.length === 1 && content[0].type === 'media'; var containsMediaAndCaption = Array.isArray(content) && content.length === 2 && content[0].type === 'media' && content[1].type === 'caption'; if (containsJustMedia || containsMediaAndCaption) { return { type: type, attrs: attrs, content: content, marks: getValidMarks(marks, adfStage) }; } break; } case 'mention': { var mentionText = ''; var mentionId; var mentionAccess; if (attrs) { var _text = attrs.text, displayName = attrs.displayName, _id2 = attrs.id, accessLevel = attrs.accessLevel; mentionText = _text || displayName; mentionId = _id2; mentionAccess = accessLevel; } if (!mentionText) { mentionText = text || '@unknown'; } if (mentionText && mentionId) { var mentionNode = { type: type, attrs: { id: mentionId, text: mentionText, accessLevel: '' } }; if (mentionAccess) { mentionNode.attrs.accessLevel = mentionAccess; } return mentionNode; } break; } case 'paragraph': { return marks ? { type: type, content: content || [], marks: marks } : { type: type, content: content || [] }; } case 'rule': { return { type: type }; } case 'text': { var _marks = node.marks; if (text) { return _marks ? { type: type, text: text, marks: getValidMarks(_marks, adfStage) } : { type: type, text: text }; } break; } case 'heading': { if (attrs) { var level = attrs.level; var between = function between(x, a, b) { return x >= a && x <= b; }; if (level && between(level, 1, 6)) { return marks ? { type: type, content: content, marks: marks, attrs: { level: level } } : { type: type, content: content, attrs: { level: level } }; } } break; } case 'bulletList': { if (content) { return { type: type, content: content }; } break; } case 'orderedList': { if (content) { return { type: type, content: content, attrs: { order: attrs && attrs.order } }; } break; } case 'listItem': { if (content) { return { type: type, content: wrapInlineNodes(content) }; } break; } case 'blockquote': { if (content) { return { type: type, content: content }; } break; } case 'panel': { if (attrs && content) { var panelType = attrs.panelType; if (Object.values(_adfSchema.PanelType).includes(panelType)) { return { type: type, attrs: attrs, content: content }; } } break; } case 'layoutSection': { if (content) { return { type: type, marks: marks, content: content }; } break; } case 'layoutColumn': { if (attrs && content) { if (attrs.width > 0 && attrs.width <= 100) { return { type: type, content: content, attrs: attrs }; } } break; } case 'decisionList': { return { type: type, content: content, attrs: { localId: attrs && attrs.localId || (0, _adfSchema.generateUuid)() } }; } case 'decisionItem': { return { type: type, content: content, attrs: { localId: attrs && attrs.localId || (0, _adfSchema.generateUuid)(), state: attrs && attrs.state || 'DECIDED' } }; } case 'taskList': { return { type: type, content: content, attrs: { localId: attrs && attrs.localId || (0, _adfSchema.generateUuid)() } }; } case 'taskItem': { return { type: type, content: content, attrs: { localId: attrs && attrs.localId || (0, _adfSchema.generateUuid)(), state: attrs && attrs.state || 'TODO' } }; } case 'table': { if (Array.isArray(content) && content.length > 0 && !content.some(function (e) { return e.type !== 'tableRow'; })) { if (adfStage === 'stage0') { return { type: type, content: content, attrs: _objectSpread(_objectSpread({}, attrs), {}, { localId: (attrs === null || attrs === void 0 ? void 0 : attrs.localId) || (0, _adfSchema.generateUuid)(), width: (attrs === null || attrs === void 0 ? void 0 : attrs.width) || null }) }; } return { type: type, content: content, attrs: attrs }; } break; } case 'tableRow': { if (Array.isArray(content) && content.length > 0 && !content.some(function (e) { return e.type !== 'tableCell' && e.type !== 'tableHeader'; })) { return { type: type, content: content }; } break; } case 'tableCell': case 'tableHeader': { if (content) { var cellAttrs = {}; if (attrs) { if (attrs.colspan && attrs.colspan > 1) { cellAttrs.colspan = attrs.colspan; } if (attrs.rowspan && attrs.rowspan > 1) { cellAttrs.rowspan = attrs.rowspan; } if (attrs.background) { cellAttrs.background = attrs.background; } if (attrs.colwidth && Array.isArray(attrs.colwidth)) { cellAttrs.colwidth = attrs.colwidth; } } return { type: type, content: wrapInlineNodes(content), attrs: attrs ? cellAttrs : undefined }; } break; } case 'image': { if (attrs && attrs.src) { return { type: type, attrs: attrs }; } break; } case 'placeholder': { if (attrs && typeof attrs.text !== 'undefined') { return { type: type, attrs: attrs }; } break; } case 'expand': case 'nestedExpand': { return { type: type, attrs: attrs, content: content, marks: marks }; } } } return getValidUnknownNode(node); }; /* * This method will validate a Mark according to the spec defined here * https://product-fabric.atlassian.net/wiki/spaces/E/pages/11174043/Document+structure#Documentstructure-Marks * * This is also the place to handle backwards compatibility. * * If a node is not recognized or is missing required attributes, we should return null * */ var getValidMark = exports.getValidMark = function getValidMark(mark) { var adfStage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'final'; var attrs = mark.attrs, type = mark.type; if (type) { switch (type) { case 'code': { return { type: type }; } case 'em': { return { type: type }; } case 'link': { if (attrs) { var href = attrs.href, url = attrs.url, __confluenceMetadata = attrs.__confluenceMetadata; var linkHref = href || url; if (linkHref && linkHref.indexOf(':') === -1 && !RELATIVE_LINK.test(linkHref) && !ANCHOR_LINK.test(linkHref)) { linkHref = "http://".concat(linkHref); } var linkAttrs = { href: linkHref }; if (__confluenceMetadata) { linkAttrs.__confluenceMetadata = __confluenceMetadata; } if (linkHref && (0, _adfSchema.isSafeUrl)(linkHref)) { return { type: type, attrs: linkAttrs }; } } break; } case 'strike': { return { type: type }; } case 'strong': { return { type: type }; } case 'subsup': { if (attrs && attrs['type']) { var subSupType = attrs['type']; if (isSubSupType(subSupType)) { return { type: type, attrs: { type: subSupType } }; } } break; } case 'textColor': { if (attrs && TEXT_COLOR_PATTERN.test(attrs.color)) { return { type: type, attrs: attrs }; } break; } case 'underline': { return { type: type }; } case 'annotation': { return { type: type, attrs: attrs }; } case 'border': { return { type: type, attrs: attrs }; } } } if (adfStage === 'stage0') { switch (type) { case 'confluenceInlineComment': { return { type: type, attrs: attrs }; } case 'dataConsumer': { return { type: type, attrs: attrs }; } case 'fragment': { return { type: type, attrs: attrs }; } case 'border': { return { type: type, attrs: attrs }; } } } return null; };