website/public/md2html.js

229 lines
4.5 KiB
JavaScript

const md2tokens = (markdown, newline) => {
// Simple lexer and parser
let i = 0;
let current_string = "";
newline = newline == null ? true : newline;
let newline_count = newline ? 1 : 0;
let tokens = [];
let match = (c) => {
if (c == markdown[i]) {
i++;
return true;
}
return false;
}
let advance = () => {
i++;
if (i < markdown.length)
return true
return false
}
let capture_until = (c) => {
let capture = "";
while (markdown[i] != c && markdown.length > i) {
capture += markdown[i];
i++;
}
return capture;
}
let finish = (type) => {
if (current_string.trim().length == 0) {
current_string = "";
return;
}
tokens.push({ type: type == null ? "span" : type, content: current_string })
current_string = "";
}
let def = () => {
current_string += markdown[i];
newline = false;
newline_count = 0;
}
while (i < markdown.length) {
switch (markdown[i]) {
case '\\':
i++;
current_string += markdown[i];
break;
case '*':
finish();
advance();
let depth_em = 1;
let type = "em";
if (match("*")) {
type = "bold";
depth_em = 2;
}
let found_match = () => {
for (let j = 0; j < depth_em; j++) {
if (markdown[i + j] != "*")
return false
}
return true
}
while (!found_match()) {
current_string += markdown[i];
if (!advance())
break;
}
advance();
finish(type);
break;
case "#":
if (!newline) {
def();
break;
}
finish();
let depth_he = 0;
do {
depth_he++;
} while (match("#"));
while (markdown[i] != "\n" && markdown.length > i) {
current_string += markdown[i];
i++
}
tokens.push({ type: "header", level: depth_he, content: current_string });
current_string = "";
break;
case '!':
if (markdown[i + 1] != "[") {
def();
break;
}
finish();
advance();
advance();
let alt_text = capture_until("]");
advance();
if (!match("(")) {
current_string = `![${alt_text}]`;
def();
break;
}
let url = capture_until("\"");
advance();
let title = capture_until("\"");
advance();
if (!match(")")) {
current_string = `![${alt_text}](${url} "${title}"`;
def();
break;
}
tokens.push({type:"image", alt: alt_text, url: url, title: title})
break;
case '-':
if (!newline) {
def();
break;
}
let items = [];
advance();
do {
let text = capture_until("\n");
advance();
items.push(md2tokens(text, false));
} while (match("-"));
tokens.push({type:"ul", items: items});
break;
case '\n':
if (newline_count > 2)
break;
newline = true;
newline_count++;
finish();
tokens.push({ type: "newline" })
break;
case '`':
finish();
advance();
current_string = capture_until("`");
finish("inlinecode");
break;
default:
def()
}
i++;
}
finish();
console.log(tokens);
return tokens;
};
const tokens2html = (tokens) => {
let output = "";
for (let token of tokens) {
switch (token.type) {
case "span":
output += `<span>${token.content}</span>`;
break
case "bold":
output += `<b>${token.content}</b>`
break;
case "em":
output += `<i>${token.content}</i>`
break;
case "header":
output += `<h${token.level}>${token.content}</h${token.level}>`
break;
case "newline":
output += "<br>"
break;
case "image":
output += `<p style="text-align:center"><img src="${token.url}" alt="${token.alt}"></p>`;
break
case "ul":
console.log(token);
output += "<ul>";
for (let item of token.items) {
output += `<li>${tokens2html(item)}</li>`
}
output += "</ul>";
break;
case "inlinecode":
output += `<code>${token.content}</code>`;
break;
}
}
return output;
}