fixes, add web warnings
This commit is contained in:
2
index.ts
2
index.ts
@@ -79,7 +79,7 @@ test_assert_equal(parse_md_to_html("> ```\n> alert('e')\n> ```"), "<blockquote>\
|
||||
|
||||
test_assert_equal(parse_md_to_html("> a\n\n> b"), "<blockquote>\n<p>a</p>\n</blockquote>\n<blockquote>\n<p>b</p>\n</blockquote>", "block quote test 3");
|
||||
|
||||
test_assert_equal(parse_md_to_html("> - burger\n> -winter melons\n> abcdefg\n> - fries\n- p**i**zza\nabacus"), "<blockquote>\n<ul>\n<li>burger</li>\n</ul>\n<p>-winter melons</p>\n<p>abcdefg</p>\n<ul>\n<li>fries</li>\n</ul>\n</blockquote>\n<ul>\n<li>p<b>i</b>zza</li>\n</ul>\n<p>abacus</p>", "unordered lists test");
|
||||
test_assert_equal(parse_md_to_html("> - burger\n> -winter melons\n> abcdefg\n> - fries\n- p**i**zza\na"), "<blockquote>\n<ul>\n<li>burger</li>\n</ul>\n<p>-winter melons</p>\n<p>abcdefg</p>\n<ul>\n<li>fries</li>\n</ul>\n</blockquote>\n<ul>\n<li>p<b>i</b>zza</li>\n</ul>\n<p>a</p>", "unordered lists test");
|
||||
|
||||
test_assert_equal(parse_md_to_html("1. a\n2. b\n3. c\n5. should *fail*\n> 1. d\n> 2. e\n3. should fail too"), "<ol>\n<li>a</li>\n<li>b</li>\n<li>c</li>\n</ol>\n<p>5. should <i>fail</i></p>\n<blockquote>\n<ol>\n<li>d</li>\n<li>e</li>\n</ol>\n</blockquote>\n<p>3. should fail too</p>", "ordered lists test 1")
|
||||
|
||||
|
||||
76
makoto.ts
76
makoto.ts
@@ -225,6 +225,11 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
}
|
||||
}
|
||||
}
|
||||
warnings.push({
|
||||
type: "italic-not-closed",
|
||||
message: "Italic not closed, may be missing closing '*'? Backslash the '*' if this is intentional",
|
||||
line_number,
|
||||
});
|
||||
} else if (asterisk_num === 2) {
|
||||
//remove the last <b> and replace it with a **
|
||||
let split: string[] = html_line.split("<b>");
|
||||
@@ -239,6 +244,11 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
}
|
||||
}
|
||||
}
|
||||
warnings.push({
|
||||
type: "bold-not-closed",
|
||||
message: "Bold not closed, may be missing closing '**'? Backslash the '**' if this is intentional",
|
||||
line_number,
|
||||
});
|
||||
}
|
||||
asterisk_num = 0;
|
||||
asterisk_out_num = 0;
|
||||
@@ -260,6 +270,11 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
}
|
||||
}
|
||||
in_superscript = false;
|
||||
warnings.push({
|
||||
type: "superscript-not-closed",
|
||||
message: "Superscript not closed, may be missing closing '^'? Backslash the '^' if this is intentional",
|
||||
line_number,
|
||||
});
|
||||
}
|
||||
//ending table row
|
||||
if (in_table) {
|
||||
@@ -286,7 +301,23 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
}
|
||||
html_line = "";
|
||||
horizontal_num = 0;
|
||||
line_number++;
|
||||
if (char === "\n") {
|
||||
line_number++;
|
||||
}
|
||||
//check to see if unordered list is ending
|
||||
if (in_unordered_list && char === "\n" && ((chars.slice(i+1, i+3) !== "- " && !blockquote_list) || (chars.slice(i+1, i+5) !== "> - " && blockquote_list))) {
|
||||
html += "</ul>\n";
|
||||
in_unordered_list = false;
|
||||
blockquote_list = false;
|
||||
}
|
||||
//check to see if ordered list is ending
|
||||
let ol_num_length: number = String(ordered_list_num+1).length;
|
||||
if (in_ordered_list && char === "\n" && ((chars.slice(i+1, i+ol_num_length+3) !== `${ordered_list_num+1}. ` && !blockquote_list) || (chars.slice(i+1, i+ol_num_length+5) !== `> ${ordered_list_num+1}. ` && blockquote_list))) {
|
||||
html += "</ol>\n";
|
||||
ordered_list_num = 0;
|
||||
in_ordered_list = false;
|
||||
blockquote_list = false;
|
||||
}
|
||||
if (horizontal_rule || was_image || was_link) {
|
||||
if (i !== chars.length - 1 && html[html.length-1] !== "\n") {
|
||||
//only add new line if there isn't already one, and isn't last character
|
||||
@@ -434,10 +465,6 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
blockquote_list = true;
|
||||
}
|
||||
continue;
|
||||
} else if (in_unordered_list && ((((chars[i-1] === "\n" && char !== "-") || (chars[i-2] === "\n" && char !== " ")) && !blockquote_list) || (((chars[i-3] === "\n" && char !== "-") || (chars[i-4] === "\n" && char !== " ")) && blockquote_list))) {
|
||||
html += "</ul>\n";
|
||||
in_unordered_list = false;
|
||||
blockquote_list = false;
|
||||
}
|
||||
//handle ordered lists
|
||||
let ol_num_length: number = String(ordered_list_num+1).length;
|
||||
@@ -452,11 +479,6 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
blockquote_list = true;
|
||||
}
|
||||
continue;
|
||||
} else if (in_ordered_list && ((chars[i-ol_num_length-2] === "\n" && chars.slice(i-ol_num_length-1, i+1) !== `${ordered_list_num+1}. ` && !in_blockquote) || (in_blockquote && chars[i-ol_num_length-4] === "\n" && chars.slice(i-ol_num_length-3, i+1) !== `> ${ordered_list_num+1}. `))) {
|
||||
html += "</ol>\n";
|
||||
ordered_list_num = 0;
|
||||
in_ordered_list = false;
|
||||
blockquote_list = false;
|
||||
}
|
||||
//handle code
|
||||
if (char === "`" && !in_code) {
|
||||
@@ -534,7 +556,7 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
//ensure headings are continuous and have after it ("#a##" or "##abc" are not a valid headings), and are at the beginning of the line
|
||||
//ensure headings are possible in block quotes
|
||||
if (chars.slice(i-heading_level-1, i) === "\n"+"#".repeat(heading_level) || (is_first_line && chars.slice(0, i) === "#".repeat(heading_level)) || (chars.slice(i-heading_level-3, i) === "\n> "+"#".repeat(heading_level) && in_blockquote) || (is_first_line && chars.slice(0, i) === "> "+"#".repeat(heading_level) && in_blockquote)) {
|
||||
if (char === "#" && !in_heading && heading_level <= 6) {
|
||||
if (char === "#" && !in_heading && heading_level < 6) {
|
||||
heading_level++;
|
||||
continue;
|
||||
} else if (heading_level > 0 && char === " " && !in_heading) {
|
||||
@@ -542,6 +564,12 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
html_line = `<h${heading_level} id="header-${header_num}">`;
|
||||
header_num++;
|
||||
continue;
|
||||
} else if (char === "#" && heading_level === 6) {
|
||||
warnings.push({
|
||||
type: "too-much-header",
|
||||
message: "Header cannot be more than 6 levels",
|
||||
line_number,
|
||||
})
|
||||
} else if (heading_level > 0) {
|
||||
//not a heading
|
||||
html_line = "<p>"+"#".repeat(heading_level);
|
||||
@@ -569,11 +597,13 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
} else if (horizontal_num > 0) {
|
||||
//no longer a horizontal line
|
||||
html_line = "<p>"+"-".repeat(horizontal_num);
|
||||
warnings.push({
|
||||
type: "horizontal-rule-broken",
|
||||
message: "Horizontal rule broken",
|
||||
line_number,
|
||||
});
|
||||
if (horizontal_num > 2) {
|
||||
warnings.push({
|
||||
type: "horizontal-rule-broken",
|
||||
message: "Horizontal rule broken",
|
||||
line_number,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
//handle images
|
||||
@@ -591,6 +621,13 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
if (chars[i+1] === ")" && i+1 === chars.length-1) {
|
||||
image_src += char;
|
||||
}
|
||||
if (image_alt === "") {
|
||||
warnings.push({
|
||||
type: "missing-image-alt",
|
||||
message: "Image is missing alt text, this is bad for accessibility",
|
||||
line_number,
|
||||
});
|
||||
}
|
||||
html_line += `<img src="${image_src}" alt="${image_alt}">`;
|
||||
was_image = true;
|
||||
image_alt = undefined;
|
||||
@@ -625,6 +662,13 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult {
|
||||
if (chars[before_link] === "\n" || before_link === -1) {
|
||||
html_line = "<p>";
|
||||
}
|
||||
if (link_content === "") {
|
||||
warnings.push({
|
||||
type: "empty-link",
|
||||
message: "Link missing text",
|
||||
line_number,
|
||||
});
|
||||
}
|
||||
html_line += `<a href="${link_href}">${link_content}</a>`;
|
||||
was_link = true;
|
||||
link_content = undefined;
|
||||
|
||||
@@ -7,7 +7,7 @@ blockquote {
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 4px 0px;
|
||||
margin: 7px 0px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@@ -18,6 +18,8 @@ h1, h2, h3, h4, h5, h6 {
|
||||
.code-block {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
background-color: #d8d8d8;
|
||||
padding: 2px 4px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
img {
|
||||
|
||||
@@ -26,15 +26,14 @@ html, body {
|
||||
#editor {
|
||||
box-sizing: border-box;
|
||||
width: 95%;
|
||||
width: calc(100% - 15px);
|
||||
width: calc(100% - 58px);
|
||||
height: 95%;
|
||||
margin: 0;
|
||||
word-break: break-all;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.9em;
|
||||
padding: 3px;
|
||||
padding-left: 35px;
|
||||
margin-left: 15px;
|
||||
padding-left: 58px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ html, body {
|
||||
|
||||
#rendered-text {
|
||||
padding: 5px;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
@@ -56,3 +55,16 @@ html, body {
|
||||
#rendered-text.dark .code-block {
|
||||
color: #2d2d2d;
|
||||
}
|
||||
|
||||
.line-warning {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
left: -58px;
|
||||
margin-top: -1px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.line-warning::after {
|
||||
content: "⚠️";
|
||||
}
|
||||
|
||||
73
web.ts
73
web.ts
@@ -8,27 +8,49 @@ let dark_theme_toggle: HTMLInputElement = document.getElementById("dark-theme-to
|
||||
let unedited: boolean = true;
|
||||
|
||||
function render_warnings(warnings: Warning[]) {
|
||||
console.log(warnings)
|
||||
document.querySelectorAll(".line-warning").forEach((item: Element) => item.remove());
|
||||
warnings.forEach((warning: Warning) => {
|
||||
if (warning.line_number) {
|
||||
//
|
||||
let line: Element = editor.children[warning.line_number-1];
|
||||
let warning_span: HTMLElement = document.createElement("SPAN");
|
||||
warning_span.classList.add("line-warning");
|
||||
warning_span.title = warning.message;
|
||||
line.insertBefore(warning_span, line.childNodes[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const refresh_html = () => {
|
||||
let parsed: ParseResult = parse_md_to_html_with_warnings(editor.innerText);
|
||||
//go through the lines and get the editor text
|
||||
let editor_text: string = Array.from(editor.children).map((item) => item.textContent).reduce((added, current) => added+"\n"+current);
|
||||
let parsed: ParseResult = parse_md_to_html_with_warnings(editor_text);
|
||||
render_warnings(parsed.warnings);
|
||||
preview.innerHTML = parsed.html;
|
||||
};
|
||||
|
||||
editor.addEventListener("keyup", refresh_html);
|
||||
editor.addEventListener("keyup", () => {
|
||||
if (editor.children.length === 0) {
|
||||
//the first <li> was deleted
|
||||
let li: HTMLElement = document.createElement("LI");
|
||||
unedited = true;
|
||||
editor.appendChild(li);
|
||||
//reset cursor position
|
||||
let range: Range = document.createRange();
|
||||
range.setStart(li, 0);
|
||||
range.collapse(true);
|
||||
let selection: Selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
refresh_html();
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e: MouseEvent) => {
|
||||
if (e.target === editor && unedited) {
|
||||
(editor.children[0] as HTMLElement).innerText = "";
|
||||
unedited = false;
|
||||
refresh_html();
|
||||
} else if (e.target !== editor && editor.innerText.trim() === "") {
|
||||
} else if (e.target !== editor && editor.innerText.trim() === "" && editor.children.length === 1) {
|
||||
(editor.children[0] as HTMLElement).innerText = "markdown goes here...";
|
||||
unedited = true;
|
||||
}
|
||||
@@ -44,9 +66,42 @@ const theme_change = () => {
|
||||
|
||||
dark_theme_toggle.addEventListener("change", theme_change);
|
||||
|
||||
refresh_html();
|
||||
theme_change();
|
||||
|
||||
if (editor.innerText !== "markdown goes here...") {
|
||||
//applies if the browser remembers what the user typed in
|
||||
if (editor.innerText.trim() !== "markdown goes here...") {
|
||||
unedited = false;
|
||||
}
|
||||
|
||||
//give a quick intro to markdown
|
||||
let params: URLSearchParams = new URLSearchParams(window.location.search);
|
||||
if (params.get("help") === "true") {
|
||||
(editor.children[0] as HTMLElement).innerText = "# Makoto Markdown Parser";
|
||||
let extra_lines: string[] = [
|
||||
"This markdown parser is powered by spaghetti. You can have **bold text** or *italic text*, and even ^superscripts!^",
|
||||
"Of course, you can have [links](https://en.wikipedia.org), and use backslashes to \*escape\*. Here's a list:",
|
||||
"- uno",
|
||||
"- dos",
|
||||
"- tres",
|
||||
"> ## Wow! A blockquote!",
|
||||
"> Reasons why blockquotes are cool:",
|
||||
"> 1. They are blocks",
|
||||
"> 2. They are also quotes",
|
||||
"Now here's some `code`!!!!",
|
||||
"```rust",
|
||||
"fn main() {",
|
||||
" println!('HOLA MUNDO');",
|
||||
"}",
|
||||
"```",
|
||||
"---",
|
||||
""
|
||||
];
|
||||
for (let i=0; i < extra_lines.length; i++) {
|
||||
let additional_line: HTMLElement = document.createElement("LI");
|
||||
//needs to be .innerHTML so the html space entity can be parsed
|
||||
additional_line.innerHTML = extra_lines[i];
|
||||
editor.appendChild(additional_line);
|
||||
}
|
||||
//
|
||||
}
|
||||
|
||||
refresh_html();
|
||||
theme_change();
|
||||
|
||||
Reference in New Issue
Block a user