-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
content-parser.ts
131 lines (122 loc) · 4.52 KB
/
content-parser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import Service from '@ember/service';
import { htmlSafe } from '@ember/template';
// import yabbcode from 'ya-bbcode';
import yabbcode from './bbcode/ya-bbcode';
import { emojis } from './bbcode/emoji';
export default class ContentParserService extends Service {
parser = this.registerCustomTags(new yabbcode());
/**
* Parses post content to HTML and returns the result.
* @param input The post content containing BBCode and other things.
* @returns The HTML output.
*/
parsePostContent(input: string) {
let output = input;
output = this.parseQuoteUsernames(output);
output = this.parser.parse(output);
output = this.parseEmojis(output);
output = this.cleanup(output);
return htmlSafe(output);
}
/**
* Registers custom tags for yabbcode parser. Default tags can be found here:
* https://github.com/nodecraft/ya-bbcode/blob/main/ya-bbcode.js
* The parser is documented at https://github.com/nodecraft/ya-bbcode.
* @param parser The yabbcode parser instance.
* @returns The modified yabbcode parser instance.
*/
private registerCustomTags(parser: yabbcode) {
parser.registerTag('quote', {
type: 'content',
replace: this.parseQuote,
});
parser.registerTag('s', {
type: 'replace',
open: '<s>',
close: '</s>',
});
parser.registerTag('url', {
type: 'content',
replace: this.parseUrl,
});
parser.registerTag('spoiler', {
type: 'replace',
open: '<label class="spoiler"><input class="spoiler-input" type="checkbox"/><p class="spoiler-header">👀 Spoiler anzeigen</p><span class="spoiler-content">',
close: '</span></label>',
});
parser.registerTag('video', {
type: 'content',
replace: this.parseVideo,
});
return parser;
}
private parseEmojis(input: string) {
let output = input;
for (const emoji of emojis) {
output = output.replaceAll(
emoji.pattern,
`<img class="post-emoji" src="post-emojis/${emoji.filename}" alt="${emoji.key}"/>`
);
}
return output;
}
private parseUrl(attr: string, content: string) {
if (!attr) attr = `"${content}"`;
return `<a href=${attr} target="_blank">${content}</a>`;
}
private parseVideo(attr: string, content: string) {
// YouTube links need to be embedded using their propietary player
if (content.match(/youtu.be/g) || content.match(/youtube.com/g)) {
const split = content.split('/');
const videoId = split[split.length - 1];
return `<iframe class="youtube-player" type="text/html"
src="https://www.youtube.com/embed/${videoId}?&origin=${window.location.host}"
frameborder="0"/>`;
} else {
return `<video src="${content}" controls/>`;
}
}
/**
* Quotes can contain usernames which again can contain square brackets, which
* will make yabbcode misinterpret the end of the opening tag. We will need
* to prepare those so that yabbcode will have a easier time parsing them.
*/
private parseQuoteUsernames(input: string) {
// eslint-disable-next-line no-useless-escape
const quoteAttributesRegex = RegExp(/(?:(\")(.*)(\"))/g);
const matches = input.match(quoteAttributesRegex);
if (!matches || matches.length === 0) return input;
let output = input;
for (const match of matches) {
const replacement = match.replace('[', '<SQBO>').replace(']', '<SQBC>');
console.log(replacement);
output = output.replace(match, replacement);
}
return output;
}
private parseQuote(attr: string, content: string) {
if (!attr) {
return `<span class="quote"><blockquote>${content}</blockquote></span>`;
}
const userNameMatches = attr.match(/(?<=")(.*)(?=")/);
let userName = '';
if (userNameMatches && userNameMatches.length > 0) {
userName = userNameMatches[0];
} else {
userName = 'Unknown';
}
const ids = attr.replace(/[^0-9,]/g, '').split(',');
if (ids.length >= 2) {
const threadId = ids[0];
const postId = ids[1];
const url = `${window.location.protocol}//${window.location.host}/thread?TID=${threadId}&PID=${postId}#reply_${postId}`;
return `<span class="quote"><a class="quote-header" href="${url}"><p>${userName}</p></a><blockquote>${content}</blockquote></span>`;
}
return `<span class="quote"><span class="quote-header"><p>${userName}</p></span><blockquote>${content}</blockquote></span>`;
}
cleanup(input: string) {
let output = input;
output = output.replaceAll('<SQBO>', '[').replaceAll('<SQBC>', ']');
return output;
}
}