UNPKG

@atlaskit/editor-jira-transformer

Version:
412 lines (400 loc) 17.2 kB
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; } import { normalizeHexColor } from '@atlaskit/adf-schema'; import { isSchemaWithLists, isSchemaWithMentions, isSchemaWithLinks, isSchemaWithAdvancedTextFormattingMarks, isSchemaWithCodeBlock, isSchemaWithBlockQuotes, isSchemaWithMedia, isSchemaWithSubSupMark, isSchemaWithTextColor, isSchemaWithTables } from '@atlaskit/adf-schema/schema-jira'; import { Fragment } from '@atlaskit/editor-prosemirror/model'; import { mapImageToEmoji } from './emojiHelper'; /** * Ensure that each node in the fragment is a block, wrapping * in a block node if necessary. */ export function ensureBlocks(fragment, schema, nodeType) { // If all the nodes are inline, we want to wrap in a single paragraph. if (schema.nodes.paragraph.validContent(fragment)) { return Fragment.fromArray([schema.nodes.paragraph.createChecked({}, fragment)]); } // Either all the nodes are blocks, or a mix of inline and blocks. // We convert each (if any) inline nodes to blocks. var blockNodes = []; // Following if condition has been added as fix for #ED-3431. // First child of list-item should be paragraph, // if that is not the case paragraph requires to be added. if (nodeType && nodeType === schema.nodes.listItem && fragment.firstChild && (fragment.firstChild.type === schema.nodes.bulletList || fragment.firstChild.type === schema.nodes.orderedList)) { blockNodes.push(schema.nodes.paragraph.createAndFill()); } fragment.forEach(function (child) { if (child.isBlock) { blockNodes.push(child); } else { blockNodes.push(schema.nodes.paragraph.createChecked({}, child)); } }); return Fragment.fromArray(blockNodes); } /** * This function will convert all content to inline nodes */ export var ensureInline = function ensureInline(schema, content, supportedMarks) { var result = []; content.forEach(function (node) { if (node.isInline) { var filteredMarks = node.marks.filter(function (mark) { return mark.isInSet(supportedMarks); }); result.push(node.mark(filteredMarks)); return; } // We replace an non-inline node with UnsupportedInline node result.push(schema.text(node.textContent)); }); return Fragment.fromArray(result); }; export function convert(content, node, schema) { // text if (node.nodeType === Node.TEXT_NODE) { var text = node.textContent; return text ? schema.text(text) : null; } // marks and nodes if (node instanceof HTMLElement) { var tag = node.tagName.toUpperCase(); switch (tag) { // Marks case 'DEL': if (!isSchemaWithAdvancedTextFormattingMarks(schema)) { return null; } // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return addMarks(content, [schema.marks.strike.create()]); case 'B': return addMarks(content, [schema.marks.strong.create()]); case 'EM': return addMarks(content, [schema.marks.em.create()]); case 'TT': if (!isSchemaWithAdvancedTextFormattingMarks(schema)) { return null; } // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return addMarks(content, [schema.marks.code.create()]); case 'SUB': case 'SUP': if (!isSchemaWithSubSupMark(schema)) { return null; } var type = tag === 'SUB' ? 'sub' : 'sup'; return addMarks(content, [schema.marks.subsup.create({ type: type })]); case 'INS': return addMarks(content, [schema.marks.underline.create()]); case 'FONT': if (!isSchemaWithTextColor(schema)) { return null; } var color = normalizeHexColor(node.getAttribute('color'), '#333333'); return color ? addMarks(content, [schema.marks.textColor.create({ color: color })]) : content; // Nodes case 'A': if (node.className === 'user-hover' && isSchemaWithMentions(schema)) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return schema.nodes.mention.createChecked({ id: node.getAttribute('rel'), text: node.textContent }); } if (node.className.match('jira-issue-macro-key') || !content || !isSchemaWithLinks(schema)) { return null; } var href = node.getAttribute('href'); var title = node.getAttribute('title'); return href ? addMarks(content, [schema.marks.link.create({ href: href, title: title })]) : content; case 'SPAN': /** * JIRA ISSUE MACROS * ````````````````` * <span class="jira-issue-macro" data-jira-key="ED-1"> * <a href="https://product-fabric.atlassian.net/browse/ED-1" class="jira-issue-macro-key issue-link"> * <img class="icon" src="./epic.svg" /> * ED-1 * </a> * <span class="aui-lozenge aui-lozenge-subtle aui-lozenge-current jira-macro-single-issue-export-pdf"> * In Progress * </span> * </span> */ if (node.className.split(' ').indexOf('jira-issue-macro') > -1) { var jiraKey = node.getAttribute('data-jira-key'); var link = node.getElementsByTagName('a')[0]; if (jiraKey && link) { return addMarks(Fragment.from(schema.text(jiraKey)), [ // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion schema.marks.link.create({ href: link.getAttribute('href'), title: link.getAttribute('title') })]); } return null; } else if (node.className.match('jira-macro-single-issue-export-pdf')) { return null; } else if (node.className.match('code-')) { // Removing spans with syntax highlighting from JIRA return null; } else if (isMedia(node) && isSchemaWithMedia(schema)) { var dataNode = node.querySelector('[data-media-services-id]'); if (dataNode && dataNode instanceof HTMLElement) { var id = dataNode.getAttribute('data-media-services-id'); var _type = dataNode.getAttribute('data-media-services-type'); var collection = dataNode.getAttribute('data-media-services-collection') || ''; var attachmentName = dataNode.getAttribute('data-attachment-name'); var attachmentType = dataNode.getAttribute('data-attachment-type'); var fileName = dataNode.getAttribute('data-file-name'); var displayType = dataNode.getAttribute('data-display-type'); var width = parseInt(dataNode.getAttribute('data-width') || '', 10); var height = parseInt(dataNode.getAttribute('data-height') || '', 10); return schema.nodes.media.createChecked({ id: id, type: _type, collection: collection, width: width || null, height: height || null, __fileName: attachmentName || fileName, __displayType: attachmentType || displayType || 'thumbnail' }); } } break; case 'IMG': if (node.parentElement && node.parentElement.className.match('jira-issue-macro-key')) { return null; } else if (node.className === 'emoticon') { var emojiResult = mapImageToEmoji(node); if (emojiResult) { return schema.text(emojiResult); } } break; 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( // @see ED-4708 { level: level === 6 ? 5 : level }, schema.nodes.heading.validContent(content) ? content : // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any ensureInline(schema, content, supportedMarks)); case 'BR': return schema.nodes.hardBreak.createChecked(); case 'HR': return schema.nodes.rule.createChecked(); case 'P': if (node.firstElementChild && isMedia(node.firstElementChild)) { // Filter out whitespace text nodes var _schema$nodes = schema.nodes, mediaSingle = _schema$nodes.mediaSingle, mediaGroup = _schema$nodes.mediaGroup, paragraph = _schema$nodes.paragraph; var mediaArray = []; var contentArray = []; var fragmentArray = []; var hasNonMediaChildren = false; content.forEach(function (child) { if (child.type === schema.nodes.media) { mediaArray.push(child); return; // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp } else if (!(child.isText && /^\s*$/.test(child.text || ''))) { hasNonMediaChildren = true; } contentArray.push(child); }); if (hasNonMediaChildren && contentArray.length) { fragmentArray.push(paragraph.createChecked({}, contentArray)); } if (isSchemaWithMedia(schema) && mediaArray.length) { var mediaNodeType = isMediaSingle(node.firstElementChild) ? mediaSingle : mediaGroup; fragmentArray.push(mediaNodeType.createChecked({}, mediaArray)); } if (fragmentArray.length) { return Fragment.fromArray(fragmentArray); } return null; } return schema.nodes.paragraph.createChecked({}, content); } // lists if (isSchemaWithLists(schema)) { switch (tag) { case 'UL': // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return schema.nodes.bulletList.createChecked({}, content); case 'OL': // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return schema.nodes.orderedList.createChecked({}, content); case 'LI': // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var compatibleContent = schema.nodes.listItem.validContent(content) ? content : ensureBlocks(content, schema, schema.nodes.listItem); // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return schema.nodes.listItem.createChecked({}, compatibleContent); } } // code block if (isSchemaWithCodeBlock(schema)) { switch (tag) { case 'DIV': if (node.className === 'codeContent panelContent' || node.className.match('preformattedContent')) { return null; } else if (node.className === 'code panel' || node.className === 'preformatted panel') { var pre = node.querySelector('pre'); if (!pre) { return null; } var language = node.className === 'preformatted panel' ? 'plain' : pre.className.split('-')[1]; // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp var textContent = (pre.textContent || '').replace(/\r\n/g, '\n'); // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return schema.nodes.codeBlock.createChecked({ language: language }, textContent ? schema.text(textContent) : undefined); } break; case 'PRE': return null; } } if (isSchemaWithBlockQuotes(schema) && tag === 'BLOCKQUOTE') { var blockquoteContent = // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any content && content.content.length ? content : schema.nodes.paragraph.createChecked(); // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return schema.nodes.blockquote.createChecked({}, blockquoteContent); } // table if (isSchemaWithTables(schema)) { switch (tag) { case 'TABLE': return schema.nodes.table.createChecked({}, content); case 'TR': // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return schema.nodes.tableRow.createChecked({}, content); case 'TD': var tdContent = schema.nodes.tableCell.validContent(content) ? content : ensureBlocks(content, schema); return schema.nodes.tableCell.createChecked({}, tdContent); case 'TH': var thContent = schema.nodes.tableHeader.validContent(content) ? content : ensureBlocks(content, schema); return schema.nodes.tableHeader.createChecked({}, thContent); } } } return; } /* * Flattens DOM tree into single array */ export function bfsOrder(root) { var inqueue = [root]; var outqueue = []; var elem; // Ignored via go/ees005 // eslint-disable-next-line no-cond-assign while (elem = inqueue.shift()) { outqueue.push(elem); var childIndex = void 0; for (childIndex = 0; childIndex < elem.childNodes.length; childIndex++) { var child = elem.childNodes[childIndex]; switch (child.nodeType) { case Node.ELEMENT_NODE: case Node.TEXT_NODE: inqueue.push(child); break; default: // eslint-disable-next-line no-console console.error("Not pushing: ".concat(child.nodeType, " ").concat(child.nodeName)); } } } outqueue.shift(); return outqueue; } /** * Create a fragment by adding a set of marks to each node. */ function addMarks(fragment, marks) { var result = fragment; for (var i = 0; i < fragment.childCount; i++) { var child = result.child(i); var newChild = child; var _iterator = _createForOfIteratorHelper(marks), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var mark = _step.value; newChild = newChild.mark(mark.addToSet(newChild.marks)); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } result = result.replaceChild(i, newChild); } return result; } function getNodeName(node) { return node.nodeName.toUpperCase(); } function isMedia(node) { if (node && node instanceof HTMLElement) { if (node.parentNode && getNodeName(node.parentNode) === 'P') { if (getNodeName(node) === 'SPAN') { return !!node.querySelector('a > jira-attachment-thumbnail > img[data-attachment-type="thumbnail"], ' + 'a[data-attachment-type="file"]'); } } } return false; } function isMediaSingle(node) { if (isMedia(node)) { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting var dataNode = node.querySelector('[data-media-services-id]'); if (dataNode instanceof HTMLElement) { var width = parseInt(dataNode.getAttribute('data-width') || '', 10); var height = parseInt(dataNode.getAttribute('data-height') || '', 10); // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting if (node.parentNode.classList.contains('mediaSingle') && width && height) { return true; } } } return false; }