UNPKG

@atlaskit/adf-schema

Version:

Shared package that contains the ADF-schema (json) and ProseMirror node/mark specs

327 lines (321 loc) 9.14 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } import { Schema } from 'prosemirror-model'; import { COLOR, FONT_STYLE, SEARCH_QUERY, LINK } from './groups'; import { link, em, strong, textColor, strike, subsup, underline, code, typeAheadQuery, confluenceInlineComment, breakout, alignment, indentation, annotation, unsupportedMark, unsupportedNodeAttribute, dataConsumer, fragment, border } from './marks'; import { confluenceJiraIssue, confluenceUnsupportedBlock, confluenceUnsupportedInline, doc, paragraph, text, bulletList, orderedListWithOrder, listItem, heading, blockquote, codeBlock, panel, rule, image, mention, media, mediaInline, mediaGroup, mediaSingleWithCaption, hardBreak, emoji, table, tableCell, tableHeader, tableRow, decisionList, decisionItem, taskList, taskItem, unknownBlock, extension, inlineExtension, bodiedExtension, date, placeholder, layoutSection, layoutColumn, inlineCard, blockCard, unsupportedBlock, unsupportedInline, status, expand, nestedExpand, embedCard, caption } from './nodes'; function addItems(builtInItems, config) { var customSpecs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; if (!config) { return {}; } /** * Add built-in Node / Mark specs */ var items = builtInItems.reduce(function (items, _ref) { var name = _ref.name, spec = _ref.spec; if (config.indexOf(name) !== -1) { items[name] = customSpecs[name] || spec; } return items; }, {}); /** * Add Custom Node / Mark specs */ return Object.keys(customSpecs).reduce(function (items, name) { if (items[name]) { return items; } items[name] = customSpecs[name]; return items; }, items); } // We use groups to allow schemas to be constructed in different shapes without changing node/mark // specs, but this means nodes/marks are defined with groups that might never be used in the schema. // In this scenario ProseMirror will complain and prevent the schema from being constructed. // // To avoid the problem, we include items that serve to "declare" the groups in the schema. This // approach unfortunately leaves unused items in the schema, but has the benefit of avoiding the // need to manipulate `exclude` or content expression values for potentially every schema item. function groupDeclaration(name) { return { name: "__".concat(name, "GroupDeclaration"), spec: { group: name } }; } var markGroupDeclarations = [groupDeclaration(COLOR), groupDeclaration(FONT_STYLE), groupDeclaration(SEARCH_QUERY), groupDeclaration(LINK)]; var markGroupDeclarationsNames = markGroupDeclarations.map(function (groupMark) { return groupMark.name; }); var nodesInOrder = [{ name: 'doc', spec: doc }, { name: 'paragraph', spec: paragraph }, { name: 'text', spec: text }, { name: 'bulletList', spec: bulletList }, { name: 'orderedList', spec: orderedListWithOrder }, { name: 'listItem', spec: listItem }, { name: 'heading', spec: heading }, { name: 'blockquote', spec: blockquote }, { name: 'codeBlock', spec: codeBlock }, { name: 'panel', spec: panel(true) }, { name: 'rule', spec: rule }, { name: 'image', spec: image }, { name: 'mention', spec: mention }, { name: 'caption', spec: caption }, { name: 'media', spec: media }, { name: 'mediaGroup', spec: mediaGroup }, { name: 'mediaSingle', spec: mediaSingleWithCaption }, { name: 'mediaInline', spec: mediaInline }, { name: 'placeholder', spec: placeholder }, { name: 'layoutSection', spec: layoutSection }, { name: 'layoutColumn', spec: layoutColumn }, { name: 'hardBreak', spec: hardBreak }, { name: 'emoji', spec: emoji }, { name: 'table', spec: table }, { name: 'tableCell', spec: tableCell }, { name: 'tableRow', spec: tableRow }, { name: 'tableHeader', spec: tableHeader }, { name: 'confluenceJiraIssue', spec: confluenceJiraIssue }, { name: 'confluenceUnsupportedInline', spec: confluenceUnsupportedInline }, { name: 'confluenceUnsupportedBlock', spec: confluenceUnsupportedBlock }, { name: 'decisionList', spec: decisionList }, { name: 'decisionItem', spec: decisionItem }, { name: 'taskList', spec: taskList }, { name: 'taskItem', spec: taskItem }, { name: 'date', spec: date }, { name: 'status', spec: status }, { name: 'expand', spec: expand }, { name: 'nestedExpand', spec: nestedExpand }, { name: 'extension', spec: extension }, { name: 'inlineExtension', spec: inlineExtension }, { name: 'bodiedExtension', spec: bodiedExtension }, { name: 'inlineCard', spec: inlineCard }, { name: 'blockCard', spec: blockCard }, { name: 'embedCard', spec: embedCard }, { name: 'unknownBlock', spec: unknownBlock }, { name: 'unsupportedBlock', spec: unsupportedBlock }, { name: 'unsupportedInline', spec: unsupportedInline }]; var marksInOrder = [{ name: 'link', spec: link }, { name: 'em', spec: em }, { name: 'strong', spec: strong }, { name: 'textColor', spec: textColor }, { name: 'strike', spec: strike }, { name: 'subsup', spec: subsup }, { name: 'underline', spec: underline }, { name: 'code', spec: code }, { name: 'typeAheadQuery', spec: typeAheadQuery }, { name: 'alignment', spec: alignment }, { name: 'annotation', spec: annotation }, { name: 'confluenceInlineComment', spec: confluenceInlineComment }].concat(markGroupDeclarations, [{ name: 'breakout', spec: breakout }, { name: 'dataConsumer', spec: dataConsumer }, { name: 'fragment', spec: fragment }, { name: 'indentation', spec: indentation }, { name: 'border', spec: border }, { name: 'unsupportedMark', spec: unsupportedMark }, { name: 'unsupportedNodeAttribute', spec: unsupportedNodeAttribute }]); /** * Creates a schema preserving order of marks and nodes. */ export function createSchema(config) { var customNodeSpecs = config.customNodeSpecs, customMarkSpecs = config.customMarkSpecs; var nodesConfig = Object.keys(customNodeSpecs || {}).concat(config.nodes); var marksConfig = Object.keys(customMarkSpecs || {}).concat(config.marks || []).concat(markGroupDeclarationsNames); var nodes = addItems(nodesInOrder, nodesConfig, customNodeSpecs); var marks = addItems(marksInOrder, marksConfig, customMarkSpecs); nodes = sanitizeNodes(nodes, marks); return new Schema({ nodes: nodes, marks: marks }); } export function sanitizeNodes(nodes, supportedMarks) { var nodeNames = Object.keys(nodes); nodeNames.forEach(function (nodeKey) { var nodeSpec = _objectSpread({}, nodes[nodeKey]); if (nodeSpec.marks && nodeSpec.marks !== '_') { nodeSpec.marks = nodeSpec.marks.split(' ').filter(function (mark) { return !!supportedMarks[mark]; }).join(' '); } if (nodeSpec.content) { nodeSpec.content = sanitizeNodeSpecContent(nodes, nodeSpec.content); } nodes[nodeKey] = nodeSpec; }); return nodes; } export function sanitizeNodeSpecContent(nodes, rawContent) { var content = rawContent.replace(/\W/g, ' '); var contentKeys = content.split(' '); var unsupportedContentKeys = contentKeys.filter(function (contentKey) { return !isContentSupported(nodes, contentKey); }); return unsupportedContentKeys.reduce(function (newContent, nodeName) { return sanitizedContent(newContent, nodeName); }, rawContent); } function sanitizedContent(content, invalidContent) { if (!invalidContent.length) { return content || ''; } if (!content || !content.match(/\w/)) { return ''; } var pattern = "(".concat(invalidContent, "((\\s)*\\|)+)|((\\|(\\s)*)+").concat(invalidContent, ")|(").concat(invalidContent, "$)|(").concat(invalidContent, "(\\+|\\*))"); return content.replace(new RegExp(pattern, 'g'), '').replace(' ', ' ').trim(); } function isContentSupported(nodes, contentKey) { var nodeKeys = Object.keys(nodes); // content is with valid node if (nodeKeys.indexOf(contentKey) > -1) { return true; } // content is with valid group for (var supportedKey in nodes) { var nodeSpec = nodes[supportedKey]; if (nodeSpec && nodeSpec.group === contentKey) { return true; } } return false; } export var allowCustomPanel = true;