149 lines
4.1 KiB
JavaScript
149 lines
4.1 KiB
JavaScript
|
|
/**
|
||
|
|
* @fileoverview Markdown Rendering Wrapper for Rantii
|
||
|
|
* @author retoor <retoor@molodetz.nl>
|
||
|
|
* @description Markdown parsing and rendering with syntax highlighting
|
||
|
|
* @keywords markdown, parsing, syntax, highlight, render
|
||
|
|
*/
|
||
|
|
|
||
|
|
class MarkdownRenderer {
|
||
|
|
constructor() {
|
||
|
|
this.marked = null;
|
||
|
|
this.hljs = null;
|
||
|
|
this.initialized = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
async init() {
|
||
|
|
if (this.initialized) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
if (window.marked) {
|
||
|
|
this.marked = window.marked;
|
||
|
|
}
|
||
|
|
if (window.hljs) {
|
||
|
|
this.hljs = window.hljs;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.marked && this.hljs) {
|
||
|
|
this.marked.setOptions({
|
||
|
|
highlight: (code, lang) => {
|
||
|
|
if (lang && this.hljs.getLanguage(lang)) {
|
||
|
|
try {
|
||
|
|
return this.hljs.highlight(code, { language: lang }).value;
|
||
|
|
} catch (e) {
|
||
|
|
return code;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return this.hljs.highlightAuto(code).value;
|
||
|
|
},
|
||
|
|
breaks: true,
|
||
|
|
gfm: true
|
||
|
|
});
|
||
|
|
} else if (this.marked) {
|
||
|
|
this.marked.setOptions({
|
||
|
|
breaks: true,
|
||
|
|
gfm: true
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
this.initialized = true;
|
||
|
|
return true;
|
||
|
|
} catch (e) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
render(text) {
|
||
|
|
if (!text) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.marked) {
|
||
|
|
try {
|
||
|
|
return this.marked.parse(text);
|
||
|
|
} catch (e) {
|
||
|
|
return this.escapeHtml(text);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return this.simpleRender(text);
|
||
|
|
}
|
||
|
|
|
||
|
|
simpleRender(text) {
|
||
|
|
let result = this.escapeHtml(text);
|
||
|
|
|
||
|
|
result = result.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
|
||
|
|
const highlighted = this.highlightCode(code.trim(), lang);
|
||
|
|
return `<pre><code class="language-${lang || 'plaintext'}">${highlighted}</code></pre>`;
|
||
|
|
});
|
||
|
|
|
||
|
|
result = result.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||
|
|
|
||
|
|
result = result.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||
|
|
result = result.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
||
|
|
|
||
|
|
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
||
|
|
|
||
|
|
result = result.replace(/\n/g, '<br>');
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
highlightCode(code, lang) {
|
||
|
|
if (this.hljs && lang && this.hljs.getLanguage(lang)) {
|
||
|
|
try {
|
||
|
|
return this.hljs.highlight(code, { language: lang }).value;
|
||
|
|
} catch (e) {
|
||
|
|
return this.escapeHtml(code);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (this.hljs) {
|
||
|
|
try {
|
||
|
|
return this.hljs.highlightAuto(code).value;
|
||
|
|
} catch (e) {
|
||
|
|
return this.escapeHtml(code);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return this.escapeHtml(code);
|
||
|
|
}
|
||
|
|
|
||
|
|
escapeHtml(text) {
|
||
|
|
const div = document.createElement('div');
|
||
|
|
div.textContent = text;
|
||
|
|
return div.innerHTML;
|
||
|
|
}
|
||
|
|
|
||
|
|
renderInline(text) {
|
||
|
|
if (!text) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
let result = this.escapeHtml(text);
|
||
|
|
result = result.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||
|
|
result = result.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||
|
|
result = result.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
stripMarkdown(text) {
|
||
|
|
if (!text) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
let result = text;
|
||
|
|
result = result.replace(/```[\s\S]*?```/g, '');
|
||
|
|
result = result.replace(/`([^`]+)`/g, '$1');
|
||
|
|
result = result.replace(/\*\*([^*]+)\*\*/g, '$1');
|
||
|
|
result = result.replace(/\*([^*]+)\*/g, '$1');
|
||
|
|
result = result.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const markdownRenderer = new MarkdownRenderer();
|
||
|
|
|
||
|
|
export { MarkdownRenderer, markdownRenderer };
|