UNPKG

@atlaskit/editor-confluence-transformer

Version:
585 lines (578 loc) 24.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = _default; var _adfSchema = require("@atlaskit/adf-schema"); var _consts = require("@atlaskit/editor-shared-styles/consts"); var _model = require("@atlaskit/editor-prosemirror/model"); var _parseCxhtml = _interopRequireDefault(require("./parse-cxhtml")); var _encodeCxhtml = _interopRequireWildcard(require("./encode-cxhtml")); var _utils = require("./utils"); var _contentWrapper = require("./content-wrapper"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } var supportedSingleMediaLayouts = ['center', 'wrap-left', 'wrap-right', 'wide', 'full-width']; var convertedNodes = new WeakMap(); // This reverted mapping is used to map Unsupported Node back to it's original cxhtml var convertedNodesReverted = new WeakMap(); function _default(cxhtml, schema) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var dom = (0, _parseCxhtml.default)(cxhtml).querySelector('body'); return schema.nodes.doc.createChecked({}, parseDomNode(schema, dom)); } function parseDomNode(schema, dom) { var nodes = (0, _utils.findTraversalPath)(Array.prototype.slice.call(dom.childNodes, 0)); // Process through nodes in reverse (so deepest child elements are first). for (var i = nodes.length - 1; i >= 0; i--) { var node = nodes[i]; var _content = (0, _utils.getContent)(node, convertedNodes); var candidate = converter(schema, _content, node); if (typeof candidate !== 'undefined' && candidate !== null) { convertedNodes.set(node, candidate); convertedNodesReverted.set(candidate, node); } } var content = (0, _utils.getContent)(dom, convertedNodes); var compatibleContent = content.childCount > 0 ? // Dangling inline nodes can't be directly inserted into a document, so // we attempt to wrap in a paragraph. schema.nodes.doc.validContent(content) ? content : (0, _contentWrapper.docContentWrapper)(schema, content, convertedNodesReverted) : // The document must have at least one block element. schema.nodes.paragraph.createChecked({}); return compatibleContent; } function converter(schema, content, node) { // text if (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.CDATA_SECTION_NODE) { var text = node.textContent; return text ? schema.text(text) : null; } // All unsupported content is wrapped in an `unsupportedInline` node. Wrapping // `unsupportedInline` inside `paragraph` where appropriate is handled when // the content is inserted into a parent. var unsupportedInline = schema.nodes.confluenceUnsupportedInline.createChecked({ cxhtml: (0, _encodeCxhtml.default)(node) }); // marks and nodes if (node instanceof Element) { var tag = (0, _utils.getNodeName)(node); switch (tag) { // Marks case 'DEL': case 'S': return content ? (0, _utils.addMarks)(content, [schema.marks.strike.create()]) : null; case 'B': case 'STRONG': return content ? (0, _utils.addMarks)(content, [schema.marks.strong.create()]) : null; case 'I': case 'EM': return content ? (0, _utils.addMarks)(content, [schema.marks.em.create()]) : null; case 'CODE': return content ? (0, _utils.addMarks)(content, [schema.marks.code.create()]) : null; case 'SUB': case 'SUP': var type = tag === 'SUB' ? 'sub' : 'sup'; return content ? (0, _utils.addMarks)(content, [schema.marks.subsup.create({ type: type })]) : null; case 'U': return content ? (0, _utils.addMarks)(content, [schema.marks.underline.create()]) : null; case 'A': var href = node.getAttribute('href'); if (content) { return href ? (0, _utils.addMarks)(content, [schema.marks.link.create({ href: href })]) : content; } return null; // Nodes case 'BLOCKQUOTE': return schema.nodes.blockquote.createChecked({}, schema.nodes.blockquote.validContent(content) ? content : (0, _contentWrapper.blockquoteContentWrapper)(schema, content, convertedNodesReverted)); case 'SPAN': return (0, _utils.addMarks)(content, (0, _utils.marksFromStyle)(schema, node.style)); case 'H1': case 'H2': case 'H3': case 'H4': case 'H5': case 'H6': var level = Number(tag.charAt(1)); var supportedMarks = [schema.marks.link].filter(function (mark) { return !!mark; }); return schema.nodes.heading.createChecked({ level: level }, schema.nodes.heading.validContent(content) ? content : (0, _contentWrapper.ensureInline)(schema, content, convertedNodesReverted, // TODO: Fix any, potential issue. ED-5048 // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any supportedMarks)); case 'BR': return schema.nodes.hardBreak.createChecked(); case 'HR': return schema.nodes.rule.createChecked(); case 'UL': return schema.nodes.bulletList.createChecked({}, schema.nodes.bulletList.validContent(content) ? content : (0, _contentWrapper.listContentWrapper)(schema, content, convertedNodesReverted)); case 'OL': return schema.nodes.orderedList.createChecked({}, schema.nodes.orderedList.validContent(content) ? content : (0, _contentWrapper.listContentWrapper)(schema, content, convertedNodesReverted)); case 'LI': return schema.nodes.listItem.createChecked({}, schema.nodes.listItem.validContent(content) ? content : (0, _contentWrapper.listItemContentWrapper)(schema, content, convertedNodesReverted)); case 'P': var textNodes = []; if (!node.childNodes.length) { return schema.nodes.paragraph.createChecked({}, content); } content.forEach(function (childNode) { textNodes.push(childNode); }); // combine remaining text nodes if (textNodes.length) { return schema.nodes.paragraph.createChecked({}, (0, _contentWrapper.ensureInline)(schema, _model.Fragment.fromArray(textNodes), convertedNodesReverted)); } return null; case 'AC:HIPCHAT-EMOTICON': case 'AC:EMOTICON': var emoji = { id: node.getAttribute('ac:emoji-id') || '', shortName: node.getAttribute('ac:emoji-shortname') || '', text: node.getAttribute('ac:emoji-fallback') || '' }; if (!emoji.id) { var acName = node.getAttribute('ac:name'); var acShortcut = node.getAttribute('ac:shortcut'); if (acName) { emoji = (0, _adfSchema.acNameToEmoji)(acName); } if (acShortcut) { emoji = (0, _adfSchema.acShortcutToEmoji)(acShortcut); } } return schema.nodes.emoji.createChecked(emoji); case 'AC:STRUCTURED-MACRO': return convertConfluenceMacro(schema, node) || unsupportedInline; case 'FAB:LINK': if (node.firstChild && node.firstChild instanceof Element && (0, _utils.getNodeName)(node.firstChild) === 'FAB:MENTION') { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var _cdata = node.firstChild.firstChild; return schema.nodes.mention.createChecked({ id: node.firstChild.getAttribute('atlassian-id'), // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion text: _cdata.nodeValue }); } break; case 'FAB:MENTION': // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var cdata = node.firstChild; return schema.nodes.mention.createChecked({ id: node.getAttribute('atlassian-id'), // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion text: cdata.nodeValue }); case 'FAB:MEDIA-GROUP': var mediaNodes = []; if (!node.childNodes.length) { throw new Error('<fab:media-group> must have at least one <fab:media> as child'); } content.forEach(function (childNode) { if (childNode.type === schema.nodes.media) { mediaNodes.push(childNode); } else { throw new Error('<fab:media-group> can only have <fab:media> as child'); } }); if (mediaNodes.length) { return schema.nodes.mediaGroup.createChecked({}, mediaNodes); } return null; case 'FAB:MEDIA-SINGLE': if (node.childNodes.length !== 1) { throw new Error('<fab:media-single> must have only one <fab:media> as child'); } var mediaNode = content.firstChild; if (!mediaNode || mediaNode.type !== schema.nodes.media) { throw new Error('<fab:media-single> can only have <fab:media> as child'); } var layout = node.getAttribute('layout') || ''; var mediaSingleAttrs = { layout: supportedSingleMediaLayouts.indexOf(layout) > -1 ? layout : 'center' }; return schema.nodes.mediaSingle.createChecked(mediaSingleAttrs, mediaNode); case 'FAB:MEDIA': var mediaAttrs = { id: node.getAttribute('media-id') || '', type: node.getAttribute('media-type') || 'file', collection: node.getAttribute('media-collection') || '' }; if (node.hasAttribute('width')) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion mediaAttrs.width = parseInt(node.getAttribute('width'), 10); } if (node.hasAttribute('height')) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion mediaAttrs.height = parseInt(node.getAttribute('height'), 10); } if (node.hasAttribute('file-name')) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion mediaAttrs.__fileName = node.getAttribute('file-name'); } if (node.hasAttribute('file-size')) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion mediaAttrs.__fileSize = parseInt(node.getAttribute('file-size'), 10); } if (node.hasAttribute('file-mime-type')) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion mediaAttrs.__fileMimeType = node.getAttribute('file-mime-type'); } return schema.nodes.media.createChecked(mediaAttrs); case 'AC:INLINE-COMMENT-MARKER': if (!content) { return null; } var attrs = { reference: node.getAttribute('ac:ref') }; return (0, _utils.addMarks)(content, [schema.marks.confluenceInlineComment.create(attrs)]); case 'AC:TASK-LIST': return convertTaskList(schema, node) || unsupportedInline; case 'AC:PLACEHOLDER': var _text = node.textContent; if (_text) { return schema.nodes.placeholder.createChecked({ text: _text }); } return null; case 'FAB:ADF': return convertADF(schema, node) || unsupportedInline; case 'PRE': return schema.nodes.codeBlock.createChecked({ language: null }, schema.text(node.textContent || '')); case 'TABLE': if ((0, _utils.hasClass)(node, 'wysiwyg-macro')) { return convertWYSIWYGMacro(schema, node) || unsupportedInline; } else { return convertTable(schema, node); } case 'TIME': var dateStr = node.getAttribute('datetime'); if (dateStr) { var timestamp = Date.parse(dateStr); return schema.nodes.date.createChecked({ timestamp: timestamp }); } return unsupportedInline; case 'DIV': if ((0, _utils.hasClass)(node, 'codeHeader')) { var codeHeader = schema.text(node.textContent || '', [schema.marks.strong.create()]); var _supportedMarks = [schema.marks.link].filter(function (mark) { return !!mark; }); return schema.nodes.heading.createChecked({ level: 5 }, (0, _contentWrapper.ensureInline)(schema, _model.Fragment.from(codeHeader), convertedNodesReverted, // TODO: Fix any, potential issue. ED-5048 // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any _supportedMarks)); } else if (node.querySelector('.syntaxhighlighter')) { var codeblockNode = node.querySelector('.syntaxhighlighter'); return convertCodeFromView(schema, codeblockNode) || unsupportedInline; } else if ((0, _utils.hasClass)(node, 'preformatted')) { return convertNoFormatFromView(schema, node) || unsupportedInline; } else if ((0, _utils.hasClass)(node, 'content-wrapper')) { var _parseDomNode = parseDomNode(schema, node), _content2 = _parseDomNode.content; return _model.Fragment.from(_content2); } return unsupportedInline; } } return unsupportedInline; } function convertConfluenceMacro(schema, node) { var _parseMacro = (0, _utils.parseMacro)(node), macroName = _parseMacro.macroName, macroId = _parseMacro.macroId, params = _parseMacro.params, properties = _parseMacro.properties; // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var richBodyNode = (0, _utils.getAcTagNode)(node, 'ac:rich-text-body'); var richTextBody = richBodyNode ? parseDomNode(schema, richBodyNode).content : null; var plainTextBody = properties['ac:plain-text-body'] || ''; var schemaVersion = node.getAttributeNS(_encodeCxhtml.AC_XMLNS, 'schema-version'); switch (macroName.toUpperCase()) { case 'CODE': var language = params.language, title = params.title; return (0, _utils.createCodeFragment)(schema, plainTextBody, language, title); case 'WARNING': case 'INFO': case 'NOTE': case 'TIP': var panelTitle = params.title; var panelBody = []; if (panelTitle) { panelBody.push(schema.nodes.heading.createChecked({ level: 3 }, schema.text(panelTitle))); } if (richTextBody) { panelBody = panelBody.concat(richTextBody); } else { panelBody.push(schema.nodes.paragraph.createChecked({})); } return schema.nodes.panel.createChecked({ panelType: (0, _utils.mapPanelTypeToPm)(macroName) }, // TODO: Fix any, potential issue. ED-5048 // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any panelBody); case 'PANEL': return schema.nodes.panel.createChecked({ panelType: 'note' }, richTextBody || [schema.nodes.paragraph.createChecked()]); case 'JIRA': var issueKey = params.key; // if this is an issue list, render it as unsupported node // @see https://product-fabric.atlassian.net/browse/ED-1193?focusedCommentId=26672&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-26672 if (!issueKey) { return schema.nodes.confluenceUnsupportedInline.createChecked({ cxhtml: (0, _encodeCxhtml.default)(node) }); } return schema.text(issueKey); } if (plainTextBody) { return schema.nodes.codeBlock.createChecked({ language: null }, schema.text(plainTextBody)); } switch (properties['fab:display-type']) { case 'INLINE': return schema.nodes.inlineExtension.createChecked({ extensionType: 'com.atlassian.confluence.macro.core', extensionKey: macroName, parameters: { macroParams: (0, _utils.getExtensionMacroParams)(params), macroMetadata: { macroId: { value: macroId }, schemaVersion: { value: schemaVersion }, placeholder: [{ data: { url: properties['fab:placeholder-url'] }, type: 'image' }] } } }); case 'BLOCK': var attrs = { extensionType: 'com.atlassian.confluence.macro.core', extensionKey: macroName, parameters: { macroParams: (0, _utils.getExtensionMacroParams)(params), macroMetadata: { macroId: { value: macroId }, schemaVersion: { value: schemaVersion }, placeholder: [{ data: { url: properties['fab:placeholder-url'] }, type: 'image' }] } } }; return richTextBody ? schema.nodes.bodiedExtension.createChecked(attrs, _model.Fragment.from(richTextBody)) : schema.nodes.extension.createChecked(attrs); } return null; } function convertWYSIWYGMacro(schema, node) { var name = (0, _utils.getMacroAttribute)(node, 'name').toUpperCase(); switch (name) { case 'CODE': case 'NOFORMAT': // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var codeContent = node.querySelector('pre').textContent || ' '; var _getMacroParameters = (0, _utils.getMacroParameters)(node), language = _getMacroParameters.language, title = _getMacroParameters.title; return (0, _utils.createCodeFragment)(schema, codeContent, language, title); } return null; } function convertCodeFromView(schema, node) { var container = node.querySelector('.container'); var content = ''; if (container) { var childNodes = container.childNodes; for (var i = 0, len = childNodes.length; i < len; i++) { content += childNodes[i].textContent + (i === len - 1 ? '' : '\n'); } } var language; if (node.className) { // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp language = (node.className.match(/\w+$/) || [''])[0]; } return (0, _utils.createCodeFragment)(schema, content, language); } function convertNoFormatFromView(schema, node) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var codeContent = node.querySelector('pre').textContent || ' '; return (0, _utils.createCodeFragment)(schema, codeContent); } var RELATIVE_TABLE_WIDTH = _consts.akEditorFullPageMaxWidth; var NUMBER_COL_WIDTH = _consts.akEditorTableNumberColumnWidth; function convertTable(schema, node) { var _schema$nodes = schema.nodes, table = _schema$nodes.table, tableRow = _schema$nodes.tableRow, tableCell = _schema$nodes.tableCell, tableHeader = _schema$nodes.tableHeader; var rowNodes = []; var rows = node.querySelectorAll('tr'); var colgroup = node.querySelector('colgroup'); var columnInfos = colgroup ? colgroup.querySelectorAll('col') : []; var tableBaseWidth = (0, _utils.calcPixelsFromCSSValue)(node.style.width || '100%', RELATIVE_TABLE_WIDTH); var columnSizes = []; for (var i = 0, len = columnInfos.length; i < len; i++) { var columnInfo = columnInfos[i]; if (columnInfo.style.width) { columnSizes.push((0, _utils.calcPixelsFromCSSValue)(columnInfo.style.width, tableBaseWidth)); } else { columnSizes.push(0); } } var isNumberColumnEnabled; for (var _i = 0, rowsCount = rows.length; _i < rowsCount; _i++) { // skip nested tables from query selector if (rows[_i].parentNode !== null) { var parent = void 0; // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (rows[_i].parentNode.nodeName === 'tbody') { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion parent = rows[_i].parentNode.parentNode; } else { parent = rows[_i].parentNode; } if (parent !== node) { continue; } } var cellNodes = []; var cols = rows[_i].querySelectorAll('td,th'); if (typeof isNumberColumnEnabled === 'undefined') { isNumberColumnEnabled = cols[0].classList.contains('numberingColumn'); } if (isNumberColumnEnabled && columnSizes.length) { columnSizes[0] = NUMBER_COL_WIDTH; } var colwidthIdx = 0; for (var j = 0, colsCount = cols.length; j < colsCount; j++) { // skip nested tables from query selector if (cols[j].parentElement && cols[j].parentElement !== rows[_i]) { continue; } var cell = cols[j].nodeName === 'td' ? tableCell : tableHeader; var pmNode = parseDomNode(schema, cols[j]); var colspan = parseInt(cols[j].getAttribute('colspan') || '1', 10); var background = cols[j].getAttribute('data-highlight-colour') || null; if (background) { // convert confluence color name to editor color background = _adfSchema.tableBackgroundColorNames.get(background.toLowerCase()) || background; } var colwidth = columnSizes.length ? columnSizes.slice(colwidthIdx, colwidthIdx + colspan) : null; var attrs = { colspan: colspan, colwidth: colwidth && colwidth.length && colwidth.every(function (width) { return width > 0; }) ? colwidth : null, background: background, rowspan: parseInt(cols[j].getAttribute('rowspan') || '1', 10) }; colwidthIdx += colspan; cellNodes.push(cell.createChecked(attrs, pmNode)); } rowNodes.push(tableRow.createChecked(undefined, _model.Fragment.from(cellNodes))); } return table.createChecked({ isNumberColumnEnabled: isNumberColumnEnabled, __autoSize: columnSizes.length === 0 || columnSizes.every(function (width) { return width === 0; }) }, _model.Fragment.from(rowNodes)); } function convertTaskList(schema, node) { var nodes = []; for (var i = 0, count = node.childNodes.length; i < count; i++) { var child = node.childNodes[i]; if (child.nodeName.toLowerCase() === 'ac:task') { nodes.push(convertTaskItem(schema, child)); } } return nodes.length ? schema.nodes.taskList.createChecked({}, nodes) : null; } function convertTaskItem(schema, node) { var id = (0, _utils.getAcTagNode)(node, 'ac:task-id'); var status = (0, _utils.getAcTagNode)(node, 'ac:task-status'); var body = (0, _utils.getAcTagNode)(node, 'ac:task-body'); var nodes = []; if (body) { var _parseDomNode2 = parseDomNode(schema, body), content = _parseDomNode2.content; content.forEach(function (child) { child.descendants(function (node) { // only nested inline nodes are supported (for now) if (node.isInline) { nodes.push(node); } }); }); } var attrs = {}; if (id) { attrs['localId'] = id.textContent; } if (status) { attrs['state'] = status.textContent === 'complete' ? 'DONE' : 'TODO'; } return schema.nodes.taskItem.createChecked(attrs, nodes); } function convertADF(schema, node) { var str = node.textContent || ''; var json = JSON.parse(str); return schema.nodeFromJSON(json); }