summaryrefslogtreecommitdiff
path: root/packages/backend/src/core/MfmService.ts
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-07-21 20:36:07 +0900
committerGitHub <noreply@github.com>2023-07-21 20:36:07 +0900
commite64a81aa1d2801516e8eac8dc69aac540489f20b (patch)
tree56accbc0f5f71db864e1e975920135fb0a957291 /packages/backend/src/core/MfmService.ts
parentMerge pull request #10990 from misskey-dev/develop (diff)
parentNew Crowdin updates (#11336) (diff)
downloadmisskey-e64a81aa1d2801516e8eac8dc69aac540489f20b.tar.gz
misskey-e64a81aa1d2801516e8eac8dc69aac540489f20b.tar.bz2
misskey-e64a81aa1d2801516e8eac8dc69aac540489f20b.zip
Merge pull request #11301 from misskey-dev/develop
Release: 13.14.0
Diffstat (limited to 'packages/backend/src/core/MfmService.ts')
-rw-r--r--packages/backend/src/core/MfmService.ts108
1 files changed, 54 insertions, 54 deletions
diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index dffee16e08..38aaa84524 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -27,29 +27,29 @@ export class MfmService {
public fromHtml(html: string, hashtagNames?: string[]): string {
// some AP servers like Pixelfed use br tags as well as newlines
html = html.replace(/<br\s?\/?>\r?\n/gi, '\n');
-
+
const dom = parse5.parseFragment(html);
-
+
let text = '';
-
+
for (const n of dom.childNodes) {
analyze(n);
}
-
+
return text.trim();
-
+
function getText(node: TreeAdapter.Node): string {
if (treeAdapter.isTextNode(node)) return node.value;
if (!treeAdapter.isElementNode(node)) return '';
if (node.nodeName === 'br') return '\n';
-
+
if (node.childNodes) {
return node.childNodes.map(n => getText(n)).join('');
}
-
+
return '';
}
-
+
function appendChildren(childNodes: TreeAdapter.ChildNode[]): void {
if (childNodes) {
for (const n of childNodes) {
@@ -57,35 +57,35 @@ export class MfmService {
}
}
}
-
+
function analyze(node: TreeAdapter.Node) {
if (treeAdapter.isTextNode(node)) {
text += node.value;
return;
}
-
+
// Skip comment or document type node
if (!treeAdapter.isElementNode(node)) return;
-
+
switch (node.nodeName) {
case 'br': {
text += '\n';
break;
}
-
+
case 'a':
{
const txt = getText(node);
const rel = node.attrs.find(x => x.name === 'rel');
const href = node.attrs.find(x => x.name === 'href');
-
+
// ハッシュタグ
if (hashtagNames && href && hashtagNames.map(x => x.toLowerCase()).includes(txt.toLowerCase())) {
text += txt;
// メンション
} else if (txt.startsWith('@') && !(rel && rel.value.startsWith('me '))) {
const part = txt.split('@');
-
+
if (part.length === 2 && href) {
//#region ホスト名部分が省略されているので復元する
const acct = `${txt}@${(new URL(href.value)).hostname}`;
@@ -116,12 +116,12 @@ export class MfmService {
return `[${txt}](${href.value})`;
}
};
-
+
text += generateLink();
}
break;
}
-
+
case 'h1':
{
text += '【';
@@ -129,7 +129,7 @@ export class MfmService {
text += '】\n';
break;
}
-
+
case 'b':
case 'strong':
{
@@ -138,7 +138,7 @@ export class MfmService {
text += '**';
break;
}
-
+
case 'small':
{
text += '<small>';
@@ -146,7 +146,7 @@ export class MfmService {
text += '</small>';
break;
}
-
+
case 's':
case 'del':
{
@@ -155,7 +155,7 @@ export class MfmService {
text += '~~';
break;
}
-
+
case 'i':
case 'em':
{
@@ -164,7 +164,7 @@ export class MfmService {
text += '</i>';
break;
}
-
+
// block code (<pre><code>)
case 'pre': {
if (node.childNodes.length === 1 && node.childNodes[0].nodeName === 'code') {
@@ -176,7 +176,7 @@ export class MfmService {
}
break;
}
-
+
// inline code (<code>)
case 'code': {
text += '`';
@@ -184,7 +184,7 @@ export class MfmService {
text += '`';
break;
}
-
+
case 'blockquote': {
const t = getText(node);
if (t) {
@@ -193,7 +193,7 @@ export class MfmService {
}
break;
}
-
+
case 'p':
case 'h2':
case 'h3':
@@ -205,7 +205,7 @@ export class MfmService {
appendChildren(node.childNodes);
break;
}
-
+
// other block elements
case 'div':
case 'header':
@@ -219,7 +219,7 @@ export class MfmService {
appendChildren(node.childNodes);
break;
}
-
+
default: // includes inline elements
{
appendChildren(node.childNodes);
@@ -234,48 +234,48 @@ export class MfmService {
if (nodes == null) {
return null;
}
-
+
const { window } = new Window();
-
+
const doc = window.document;
-
+
function appendChildren(children: mfm.MfmNode[], targetElement: any): void {
if (children) {
for (const child of children.map(x => (handlers as any)[x.type](x))) targetElement.appendChild(child);
}
}
-
+
const handlers: { [K in mfm.MfmNode['type']]: (node: mfm.NodeType<K>) => any } = {
bold: (node) => {
const el = doc.createElement('b');
appendChildren(node.children, el);
return el;
},
-
+
small: (node) => {
const el = doc.createElement('small');
appendChildren(node.children, el);
return el;
},
-
+
strike: (node) => {
const el = doc.createElement('del');
appendChildren(node.children, el);
return el;
},
-
+
italic: (node) => {
const el = doc.createElement('i');
appendChildren(node.children, el);
return el;
},
-
+
fn: (node) => {
const el = doc.createElement('i');
appendChildren(node.children, el);
return el;
},
-
+
blockCode: (node) => {
const pre = doc.createElement('pre');
const inner = doc.createElement('code');
@@ -283,21 +283,21 @@ export class MfmService {
pre.appendChild(inner);
return pre;
},
-
+
center: (node) => {
const el = doc.createElement('div');
appendChildren(node.children, el);
return el;
},
-
+
emojiCode: (node) => {
return doc.createTextNode(`\u200B:${node.props.name}:\u200B`);
},
-
+
unicodeEmoji: (node) => {
return doc.createTextNode(node.props.emoji);
},
-
+
hashtag: (node) => {
const a = doc.createElement('a');
a.setAttribute('href', `${this.config.url}/tags/${node.props.hashtag}`);
@@ -305,32 +305,32 @@ export class MfmService {
a.setAttribute('rel', 'tag');
return a;
},
-
+
inlineCode: (node) => {
const el = doc.createElement('code');
el.textContent = node.props.code;
return el;
},
-
+
mathInline: (node) => {
const el = doc.createElement('code');
el.textContent = node.props.formula;
return el;
},
-
+
mathBlock: (node) => {
const el = doc.createElement('code');
el.textContent = node.props.formula;
return el;
},
-
+
link: (node) => {
const a = doc.createElement('a');
a.setAttribute('href', node.props.url);
appendChildren(node.children, a);
return a;
},
-
+
mention: (node) => {
const a = doc.createElement('a');
const { username, host, acct } = node.props;
@@ -340,47 +340,47 @@ export class MfmService {
a.textContent = acct;
return a;
},
-
+
quote: (node) => {
const el = doc.createElement('blockquote');
appendChildren(node.children, el);
return el;
},
-
+
text: (node) => {
const el = doc.createElement('span');
const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x));
-
+
for (const x of intersperse<FIXME | 'br'>('br', nodes)) {
el.appendChild(x === 'br' ? doc.createElement('br') : x);
}
-
+
return el;
},
-
+
url: (node) => {
const a = doc.createElement('a');
a.setAttribute('href', node.props.url);
a.textContent = node.props.url;
return a;
},
-
+
search: (node) => {
const a = doc.createElement('a');
a.setAttribute('href', `https://www.google.com/search?q=${node.props.query}`);
a.textContent = node.props.content;
return a;
},
-
+
plain: (node) => {
const el = doc.createElement('span');
appendChildren(node.children, el);
return el;
},
};
-
+
appendChildren(nodes, doc.body);
-
+
return `<p>${doc.body.innerHTML}</p>`;
- }
+ }
}