UNPKG

@atlaskit/adf-schema

Version:

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

148 lines (143 loc) 4.68 kB
import { Fragment } from '@atlaskit/editor-prosemirror/model'; import { codeBlock as codeBlockFactory } from '../../next-schema/generated/nodeTypes'; /** * @name codeBlock_with_no_marks_node */ /** * @name codeBlock_node */ var getLanguageFromEditorStyle = function getLanguageFromEditorStyle(dom) { return dom.getAttribute('data-language') || undefined; }; // example of BB style: // <div class="codehilite language-javascript"><pre><span>hello world</span><span>\n</span></pre></div> var getLanguageFromBitbucketStyle = function getLanguageFromBitbucketStyle(dom) { if (dom && dom.classList.contains('codehilite')) { // code block html from Bitbucket always contains an extra new line return extractLanguageFromClass(dom.className); } return; }; // If there is a child code element, check that for data-language var getLanguageFromCode = function getLanguageFromCode(dom) { var firstChild = dom.firstElementChild; if (firstChild && firstChild.nodeName === 'CODE') { return firstChild.getAttribute('data-language') || undefined; } }; var extractLanguageFromClass = function extractLanguageFromClass(className) { var languageRegex = /(?:^|\s)language-([^\s]+)/; var result = languageRegex.exec(className); if (result && result[1]) { return result[1]; } return; }; var removeLastNewLine = function removeLastNewLine(dom) { var parent = dom && dom.parentElement; if (parent && parent.classList.contains('codehilite')) { dom.textContent = dom.textContent.replace(/\n$/, ''); } return dom; }; function parseCodeFromHtml(node) { var code = ''; node.childNodes.forEach(function (child) { if (child.nodeType === Node.TEXT_NODE) { // append text code += child.nodeValue; } else if (child.nodeType === Node.ELEMENT_NODE && child instanceof Element) { var tagName = child.tagName.toLowerCase(); if (tagName === 'div' || tagName === 'p') { // add a newline before its content, unless it's the first child to avoid leading newlines if (child.previousElementSibling !== null) { code += '\n'; } } if (tagName === 'br') { code += '\n'; } else { code += parseCodeFromHtml(child); } } }); return code; } export var codeBlock = codeBlockFactory({ parseDOM: [{ tag: 'pre', preserveWhitespace: 'full', getAttrs: function getAttrs(domNode) { var dom = domNode; var language = getLanguageFromBitbucketStyle(dom.parentElement) || getLanguageFromEditorStyle(dom.parentElement) || getLanguageFromCode(dom) || dom.getAttribute('data-language'); dom = removeLastNewLine(dom); return { language: language }; } }, // Handle VSCode, Android Studio paste // Checking `white-space: pre-wrap` is too aggressive @see ED-2627 { tag: 'div[style]', preserveWhitespace: 'full', getAttrs: function getAttrs(domNode) { var dom = domNode; if (dom.style.whiteSpace === 'pre' || dom.style.fontFamily && dom.style.fontFamily.toLowerCase().indexOf('monospace') > -1) { return {}; } return false; }, getContent: function getContent(domNode, schema) { var code = parseCodeFromHtml(domNode); return code ? Fragment.from(schema.text(code)) : Fragment.empty; } }, // Handle GitHub/Gist paste { tag: 'table[style]', preserveWhitespace: 'full', getAttrs: function getAttrs(dom) { if (dom.querySelector('td[class*="blob-code"]')) { return {}; } return false; } }, { tag: 'div.code-block', preserveWhitespace: 'full', getAttrs: function getAttrs(domNode) { var dom = domNode; // TODO: ED-5604 Fix it inside `react-syntax-highlighter` // Remove line numbers var lineNumber = dom.querySelectorAll('.react-syntax-highlighter-line-number'); if (lineNumber.length > 0) { // It's possible to copy without the line numbers too hence this // `react-syntax-highlighter-line-number` check, so that we don't remove real code lineNumber.forEach(function (line) { return line.remove(); }); } return {}; } }], toDOM: function toDOM(node) { return ['pre', ['code', { 'data-language': node.attrs.language }, 0]]; } }); export var toJSON = function toJSON(node) { return { attrs: Object.keys(node.attrs).reduce(function (memo, key) { if (key === 'uniqueId') { return memo; } if (key === 'language' && node.attrs.language === null) { return memo; } memo[key] = node.attrs[key]; return memo; }, {}) }; };