@atlaskit/adf-schema
Version:
Shared package that contains the ADF-schema (json) and ProseMirror node/mark specs
327 lines (321 loc) • 9.14 kB
JavaScript
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;