Files
cinny/src/app/components/editor/output.ts
T

133 lines
4.3 KiB
TypeScript
Raw Normal View History

2023-06-12 21:15:23 +10:00
import { Descendant, Text } from 'slate';
2023-10-14 16:08:43 +11:00
2023-06-12 21:15:23 +10:00
import { sanitizeText } from '../../utils/sanitize';
import { BlockType } from './Elements';
2023-10-14 16:08:43 +11:00
import { CustomElement } from './slate';
2023-10-09 22:26:54 +11:00
import { parseInlineMD } from '../../utils/markdown';
2023-06-12 21:15:23 +10:00
2023-10-09 22:26:54 +11:00
export type OutputOptions = {
allowTextFormatting?: boolean;
allowMarkdown?: boolean;
};
2023-10-14 16:08:43 +11:00
const textToCustomHtml = (node: Text, opts: OutputOptions): string => {
2023-06-12 21:15:23 +10:00
let string = sanitizeText(node.text);
2023-10-09 22:26:54 +11:00
if (opts.allowTextFormatting) {
if (node.bold) string = `<strong>${string}</strong>`;
if (node.italic) string = `<i>${string}</i>`;
if (node.underline) string = `<u>${string}</u>`;
2023-10-14 16:08:43 +11:00
if (node.strikeThrough) string = `<del>${string}</del>`;
2023-10-09 22:26:54 +11:00
if (node.code) string = `<code>${string}</code>`;
if (node.spoiler) string = `<span data-mx-spoiler>${string}</span>`;
}
if (opts.allowMarkdown && string === sanitizeText(node.text)) {
string = parseInlineMD(string);
}
2023-06-12 21:15:23 +10:00
return string;
};
const elementToCustomHtml = (node: CustomElement, children: string): string => {
switch (node.type) {
case BlockType.Paragraph:
2023-07-23 18:12:09 +10:00
return `${children}<br/>`;
2023-06-12 21:15:23 +10:00
case BlockType.Heading:
return `<h${node.level}>${children}</h${node.level}>`;
case BlockType.CodeLine:
return `${children}\n`;
case BlockType.CodeBlock:
return `<pre><code>${children}</code></pre>`;
case BlockType.QuoteLine:
2023-07-23 18:12:09 +10:00
return `${children}<br/>`;
2023-06-12 21:15:23 +10:00
case BlockType.BlockQuote:
return `<blockquote>${children}</blockquote>`;
case BlockType.ListItem:
return `<li><p>${children}</p></li>`;
case BlockType.OrderedList:
return `<ol>${children}</ol>`;
case BlockType.UnorderedList:
return `<ul>${children}</ul>`;
2023-10-14 16:08:43 +11:00
2023-06-12 21:15:23 +10:00
case BlockType.Mention:
return `<a href="https://matrix.to/#/${node.id}">${node.name}</a>`;
case BlockType.Emoticon:
return node.key.startsWith('mxc://')
? `<img data-mx-emoticon src="${node.key}" alt="${node.shortcode}" title="${node.shortcode}" height="32">`
: node.key;
case BlockType.Link:
return `<a href="${node.href}">${node.children}</a>`;
default:
return children;
}
};
2023-10-09 22:26:54 +11:00
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);
2023-06-12 21:15:23 +10:00
2023-10-09 22:26:54 +11:00
const children = node.children.map(parseNode).join('');
2023-06-12 21:15:23 +10:00
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('');
2023-06-14 03:47:18 +10:00
if (Text.isText(node)) return node.text;
2023-06-12 21:15:23 +10:00
const children = node.children.map((n) => toPlainText(n)).join('');
return elementToPlainText(node, children);
};
2023-07-23 18:12:09 +10:00
/**
* Check if customHtml is equals to plainText
* by replacing `<br/>` 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(/<br\/>/g, '\n') === sanitizeText(plain);
export const trimCustomHtml = (customHtml: string) => customHtml.replace(/<br\/>$/g, '');