import { Descendant, Text } from 'slate'; import { sanitizeText } from '../../utils/sanitize'; import { BlockType } from './Elements'; import { CustomElement, FormattedText } from './slate'; import { parseInlineMD } from '../../utils/markdown'; export type OutputOptions = { allowTextFormatting?: boolean; allowMarkdown?: boolean; }; const textToCustomHtml = (node: FormattedText, opts: OutputOptions): string => { let string = sanitizeText(node.text); if (opts.allowTextFormatting) { if (node.bold) string = `${string}`; if (node.italic) string = `${string}`; if (node.underline) string = `${string}`; if (node.strikeThrough) string = `${string}`; if (node.code) string = `${string}`; if (node.spoiler) string = `${string}`; } if (opts.allowMarkdown && string === sanitizeText(node.text)) { string = parseInlineMD(string); } return string; }; const elementToCustomHtml = (node: CustomElement, children: string): string => { switch (node.type) { case BlockType.Paragraph: return `${children}
`; case BlockType.Heading: return `${children}`; case BlockType.CodeLine: return `${children}\n`; case BlockType.CodeBlock: return `
${children}
`; case BlockType.QuoteLine: return `${children}
`; case BlockType.BlockQuote: return `
${children}
`; case BlockType.ListItem: return `
  • ${children}

  • `; case BlockType.OrderedList: return `
      ${children}
    `; case BlockType.UnorderedList: return ``; case BlockType.Mention: return `${node.name}`; case BlockType.Emoticon: return node.key.startsWith('mxc://') ? `${node.shortcode}` : node.key; case BlockType.Link: return `${node.children}`; default: return children; } }; export const toMatrixCustomHTML = ( node: Descendant | Descendant[], opts: OutputOptions ): string => { const parseNode = (n: Descendant) => { const isCodeLine = 'type' in n && n.type === BlockType.CodeLine; if (isCodeLine) return toMatrixCustomHTML(n, {}); return toMatrixCustomHTML(n, opts); }; if (Array.isArray(node)) return node.map(parseNode).join(''); if (Text.isText(node)) return textToCustomHtml(node, opts); const children = node.children.map(parseNode).join(''); return elementToCustomHtml(node, children); }; const elementToPlainText = (node: CustomElement, children: string): string => { switch (node.type) { case BlockType.Paragraph: return `${children}\n`; case BlockType.Heading: return `${children}\n`; case BlockType.CodeLine: return `${children}\n`; case BlockType.CodeBlock: return `${children}\n`; case BlockType.QuoteLine: return `| ${children}\n`; case BlockType.BlockQuote: return `${children}\n`; case BlockType.ListItem: return `- ${children}\n`; case BlockType.OrderedList: return `${children}\n`; case BlockType.UnorderedList: return `${children}\n`; case BlockType.Mention: return node.id; case BlockType.Emoticon: return node.key.startsWith('mxc://') ? `:${node.shortcode}:` : node.key; case BlockType.Link: return `[${node.children}](${node.href})`; default: return children; } }; export const toPlainText = (node: Descendant | Descendant[]): string => { if (Array.isArray(node)) return node.map((n) => toPlainText(n)).join(''); if (Text.isText(node)) return node.text; const children = node.children.map((n) => toPlainText(n)).join(''); return elementToPlainText(node, children); }; /** * Check if customHtml is equals to plainText * by replacing `
    ` with `/n` in customHtml * and sanitizing plainText before comparison * because text are sanitized in customHtml * @param customHtml string * @param plain string * @returns boolean */ export const customHtmlEqualsPlainText = (customHtml: string, plain: string): boolean => customHtml.replace(//g, '\n') === sanitizeText(plain); export const trimCustomHtml = (customHtml: string) => customHtml.replace(/$/g, '');