892 lines
27 KiB
PHP
Raw Normal View History

// Please do not edit this file. It has been generated automatically
// from `src/module/markdown.wren` using `util/wren_to_c_string.py`
static const char* markdownModuleSource =
"// retoor <retoor@molodetz.nl>\n"
"\n"
"import \"html\" for Html\n"
"\n"
"class Markdown {\n"
" static toHtml(text) { toHtml(text, {}) }\n"
"\n"
" static toHtml(text, options) {\n"
" var safeMode = options.containsKey(\"safeMode\") ? options[\"safeMode\"] : false\n"
" var lines = text.split(\"\\n\")\n"
" var result = []\n"
" var inCodeBlock = false\n"
" var codeBlockContent = []\n"
" var inList = false\n"
" var listType = null\n"
" var inBlockquote = false\n"
"\n"
" for (i in 0...lines.count) {\n"
" var line = lines[i]\n"
"\n"
" if (line.startsWith(\"```\")) {\n"
" if (inCodeBlock) {\n"
" result.add(\"<pre><code>\" + (safeMode ? Html.quote(codeBlockContent.join(\"\\n\")) : codeBlockContent.join(\"\\n\")) + \"</code></pre>\")\n"
" codeBlockContent = []\n"
" inCodeBlock = false\n"
" } else {\n"
" closeList_(result, inList, listType)\n"
" inList = false\n"
" inCodeBlock = true\n"
" }\n"
" continue\n"
" }\n"
"\n"
" if (inCodeBlock) {\n"
" codeBlockContent.add(line)\n"
" continue\n"
" }\n"
"\n"
" if (line.count == 0) {\n"
" closeList_(result, inList, listType)\n"
" inList = false\n"
" if (inBlockquote) {\n"
" result.add(\"</blockquote>\")\n"
" inBlockquote = false\n"
" }\n"
" continue\n"
" }\n"
"\n"
" if (line.startsWith(\"> \")) {\n"
" closeList_(result, inList, listType)\n"
" inList = false\n"
" if (!inBlockquote) {\n"
" result.add(\"<blockquote>\")\n"
" inBlockquote = true\n"
" }\n"
" result.add(\"<p>\" + processInline_(line[2..-1], safeMode) + \"</p>\")\n"
" continue\n"
" }\n"
"\n"
" if (line.startsWith(\"---\") || line.startsWith(\"***\") || line.startsWith(\"___\")) {\n"
" var isHr = true\n"
" for (c in line) {\n"
" if (c != \"-\" && c != \"*\" && c != \"_\" && c != \" \") {\n"
" isHr = false\n"
" break\n"
" }\n"
" }\n"
" if (isHr && line.count >= 3) {\n"
" closeList_(result, inList, listType)\n"
" inList = false\n"
" result.add(\"<hr>\")\n"
" continue\n"
" }\n"
" }\n"
"\n"
" var heading = parseHeading_(line)\n"
" if (heading) {\n"
" closeList_(result, inList, listType)\n"
" inList = false\n"
" result.add(\"<h%(heading[\"level\"])>\" + processInline_(heading[\"text\"], safeMode) + \"</h%(heading[\"level\"])>\")\n"
" continue\n"
" }\n"
"\n"
" var listItem = parseListItem_(line)\n"
" if (listItem) {\n"
" var newType = listItem[\"type\"]\n"
" if (!inList || listType != newType) {\n"
" closeList_(result, inList, listType)\n"
" result.add(newType == \"ul\" ? \"<ul>\" : \"<ol>\")\n"
" inList = true\n"
" listType = newType\n"
" }\n"
" result.add(\"<li>\" + processInline_(listItem[\"text\"], safeMode) + \"</li>\")\n"
" continue\n"
" }\n"
"\n"
" closeList_(result, inList, listType)\n"
" inList = false\n"
" result.add(\"<p>\" + processInline_(line, safeMode) + \"</p>\")\n"
" }\n"
"\n"
" closeList_(result, inList, listType)\n"
" if (inBlockquote) {\n"
" result.add(\"</blockquote>\")\n"
" }\n"
" if (inCodeBlock) {\n"
" result.add(\"<pre><code>\" + (safeMode ? Html.quote(codeBlockContent.join(\"\\n\")) : codeBlockContent.join(\"\\n\")) + \"</code></pre>\")\n"
" }\n"
"\n"
" return result.join(\"\\n\")\n"
" }\n"
"\n"
" static closeList_(result, inList, listType) {\n"
" if (inList) {\n"
" result.add(listType == \"ul\" ? \"</ul>\" : \"</ol>\")\n"
" }\n"
" }\n"
"\n"
" static parseHeading_(line) {\n"
" var level = 0\n"
" for (c in line) {\n"
" if (c == \"#\") {\n"
" level = level + 1\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" if (level > 0 && level <= 6 && line.count > level && line[level] == \" \") {\n"
" return {\"level\": level, \"text\": line[level + 1..-1]}\n"
" }\n"
" return null\n"
" }\n"
"\n"
" static parseListItem_(line) {\n"
" var trimmed = line\n"
" var indent = 0\n"
" while (trimmed.count > 0 && trimmed[0] == \" \") {\n"
" trimmed = trimmed[1..-1]\n"
" indent = indent + 1\n"
" }\n"
"\n"
" if (trimmed.count >= 2 && (trimmed[0] == \"-\" || trimmed[0] == \"*\" || trimmed[0] == \"+\") && trimmed[1] == \" \") {\n"
" return {\"type\": \"ul\", \"text\": trimmed[2..-1]}\n"
" }\n"
"\n"
" var i = 0\n"
" while (i < trimmed.count) {\n"
" var c = trimmed[i].codePoints.toList[0]\n"
" if (c < 48 || c > 57) break\n"
" i = i + 1\n"
" }\n"
" if (i > 0 && i < trimmed.count - 1 && trimmed[i] == \".\" && trimmed[i + 1] == \" \") {\n"
" return {\"type\": \"ol\", \"text\": trimmed[i + 2..-1]}\n"
" }\n"
"\n"
" return null\n"
" }\n"
"\n"
" static processInline_(text, safeMode) {\n"
" if (safeMode) text = Html.quote(text)\n"
" text = processCode_(text)\n"
" text = processBold_(text)\n"
" text = processItalic_(text)\n"
" text = processStrikethrough_(text)\n"
" text = processImages_(text)\n"
" text = processLinks_(text)\n"
" return text\n"
" }\n"
"\n"
" static processCode_(text) {\n"
" var result = \"\"\n"
" var i = 0\n"
" var chars = text.toList\n"
" while (i < chars.count) {\n"
" if (chars[i] == \"`\") {\n"
" var end = i + 1\n"
" while (end < chars.count && chars[end] != \"`\") {\n"
" end = end + 1\n"
" }\n"
" if (end < chars.count) {\n"
" var code = \"\"\n"
" for (j in (i + 1)...end) {\n"
" code = code + chars[j]\n"
" }\n"
" result = result + \"<code>\" + code + \"</code>\"\n"
" i = end + 1\n"
" } else {\n"
" result = result + chars[i]\n"
" i = i + 1\n"
" }\n"
" } else {\n"
" result = result + chars[i]\n"
" i = i + 1\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static processBold_(text) {\n"
" var result = text\n"
" while (result.contains(\"**\")) {\n"
" var start = result.indexOf(\"**\")\n"
" var rest = result[start + 2..-1]\n"
" var end = rest.indexOf(\"**\")\n"
" if (end >= 0) {\n"
" var before = result[0...start]\n"
" var content = rest[0...end]\n"
" var after = rest[end + 2..-1]\n"
" result = before + \"<strong>\" + content + \"</strong>\" + after\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" while (result.contains(\"__\")) {\n"
" var start = result.indexOf(\"__\")\n"
" var rest = result[start + 2..-1]\n"
" var end = rest.indexOf(\"__\")\n"
" if (end >= 0) {\n"
" var before = result[0...start]\n"
" var content = rest[0...end]\n"
" var after = rest[end + 2..-1]\n"
" result = before + \"<strong>\" + content + \"</strong>\" + after\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static processItalic_(text) {\n"
" var result = text\n"
" while (result.contains(\"*\")) {\n"
" var start = result.indexOf(\"*\")\n"
" if (start > 0 && result[start - 1] == \"<\") {\n"
" break\n"
" }\n"
" var rest = result[start + 1..-1]\n"
" var end = rest.indexOf(\"*\")\n"
" if (end >= 0 && end > 0) {\n"
" var before = result[0...start]\n"
" var content = rest[0...end]\n"
" var after = rest[end + 1..-1]\n"
" result = before + \"<em>\" + content + \"</em>\" + after\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static processStrikethrough_(text) {\n"
" var result = text\n"
" while (result.contains(\"~~\")) {\n"
" var start = result.indexOf(\"~~\")\n"
" var rest = result[start + 2..-1]\n"
" var end = rest.indexOf(\"~~\")\n"
" if (end >= 0) {\n"
" var before = result[0...start]\n"
" var content = rest[0...end]\n"
" var after = rest[end + 2..-1]\n"
" result = before + \"<del>\" + content + \"</del>\" + after\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static processLinks_(text) {\n"
" var result = text\n"
" while (result.contains(\"](\")) {\n"
" var linkEnd = result.indexOf(\"](\")\n"
" var textStart = linkEnd\n"
" while (textStart > 0 && result[textStart - 1] != \"[\") {\n"
" textStart = textStart - 1\n"
" }\n"
" if (textStart > 0 && result[textStart - 1] == \"[\") {\n"
" var urlStart = linkEnd + 2\n"
" var urlEnd = urlStart\n"
" while (urlEnd < result.count && result[urlEnd] != \")\") {\n"
" urlEnd = urlEnd + 1\n"
" }\n"
" if (urlEnd < result.count) {\n"
" var before = result[0...textStart - 1]\n"
" var linkText = result[textStart...linkEnd]\n"
" var url = result[urlStart...urlEnd]\n"
" var after = result[urlEnd + 1..-1]\n"
" result = before + \"<a href=\\\"\" + url + \"\\\">\" + linkText + \"</a>\" + after\n"
" } else {\n"
" break\n"
" }\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static processImages_(text) {\n"
" var result = text\n"
" while (result.contains(\"![\")) {\n"
" var start = result.indexOf(\"![\")\n"
" var altEnd = start + 2\n"
" while (altEnd < result.count && result[altEnd] != \"]\") {\n"
" altEnd = altEnd + 1\n"
" }\n"
" if (altEnd < result.count && altEnd + 1 < result.count && result[altEnd + 1] == \"(\") {\n"
" var urlStart = altEnd + 2\n"
" var urlEnd = urlStart\n"
" while (urlEnd < result.count && result[urlEnd] != \")\") {\n"
" urlEnd = urlEnd + 1\n"
" }\n"
" if (urlEnd < result.count) {\n"
" var before = result[0...start]\n"
" var alt = result[start + 2...altEnd]\n"
" var url = result[urlStart...urlEnd]\n"
" var after = result[urlEnd + 1..-1]\n"
" result = before + \"<img src=\\\"\" + url + \"\\\" alt=\\\"\" + alt + \"\\\">\" + after\n"
" } else {\n"
" break\n"
" }\n"
" } else {\n"
" break\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static fromHtml(html) { fromHtml(html, {}) }\n"
"\n"
" static fromHtml(html, options) {\n"
" var stripUnknown = options.containsKey(\"stripUnknown\") ? options[\"stripUnknown\"] : true\n"
" var text = html\n"
"\n"
" text = removeHiddenTags_(text)\n"
" text = processBlockElements_(text)\n"
" text = processInlineFromHtml_(text)\n"
" text = Html.unquote(text)\n"
" text = cleanupWhitespace_(text)\n"
"\n"
" return text.trim()\n"
" }\n"
"\n"
" static removeHiddenTags_(html) {\n"
" var result = html\n"
" var tags = [\"script\", \"style\", \"head\", \"meta\", \"link\", \"noscript\"]\n"
" for (tag in tags) {\n"
" result = removeTagWithContent_(result, tag)\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static removeTagWithContent_(html, tag) {\n"
" var result = html\n"
" var openTag = \"<\" + tag\n"
" var closeTag = \"</\" + tag + \">\"\n"
" while (true) {\n"
" var startLower = result.indexOf(openTag)\n"
" var startUpper = result.indexOf(\"<\" + tag[0].codePoints.toList[0].toString)\n"
" var start = -1\n"
"\n"
" var i = 0\n"
" while (i < result.count - openTag.count) {\n"
" if (matchTagName_(result, i, tag)) {\n"
" start = i\n"
" break\n"
" }\n"
" i = i + 1\n"
" }\n"
"\n"
" if (start < 0) break\n"
"\n"
" var tagEnd = start\n"
" while (tagEnd < result.count && result[tagEnd] != \">\") {\n"
" tagEnd = tagEnd + 1\n"
" }\n"
" if (tagEnd >= result.count) break\n"
"\n"
" if (result[tagEnd - 1] == \"/\") {\n"
" result = result[0...start] + result[tagEnd + 1..-1]\n"
" continue\n"
" }\n"
"\n"
" var closeStart = findCloseTag_(result, tagEnd + 1, tag)\n"
" if (closeStart < 0) {\n"
" result = result[0...start] + result[tagEnd + 1..-1]\n"
" } else {\n"
" var closeEnd = closeStart\n"
" while (closeEnd < result.count && result[closeEnd] != \">\") {\n"
" closeEnd = closeEnd + 1\n"
" }\n"
" result = result[0...start] + result[closeEnd + 1..-1]\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static matchTagName_(html, pos, tag) {\n"
" if (pos >= html.count || html[pos] != \"<\") return false\n"
" var rest = html[pos + 1..-1].trim()\n"
" var tagLower = tag\n"
" for (i in 0...tag.count) {\n"
" if (i >= rest.count) return false\n"
" var c1 = rest[i].codePoints.toList[0]\n"
" var c2 = tag[i].codePoints.toList[0]\n"
" if (c1 >= 65 && c1 <= 90) c1 = c1 + 32\n"
" if (c2 >= 65 && c2 <= 90) c2 = c2 + 32\n"
" if (c1 != c2) return false\n"
" }\n"
" if (tag.count < rest.count) {\n"
" var next = rest[tag.count]\n"
" if (next != \" \" && next != \">\" && next != \"/\" && next != \"\\t\" && next != \"\\n\") return false\n"
" }\n"
" return true\n"
" }\n"
"\n"
" static findCloseTag_(html, start, tag) {\n"
" var i = start\n"
" while (i < html.count - tag.count - 2) {\n"
" if (html[i] == \"<\" && html[i + 1] == \"/\") {\n"
" var match = true\n"
" for (j in 0...tag.count) {\n"
" var c1 = html[i + 2 + j].codePoints.toList[0]\n"
" var c2 = tag[j].codePoints.toList[0]\n"
" if (c1 >= 65 && c1 <= 90) c1 = c1 + 32\n"
" if (c2 >= 65 && c2 <= 90) c2 = c2 + 32\n"
" if (c1 != c2) {\n"
" match = false\n"
" break\n"
" }\n"
" }\n"
" if (match) return i\n"
" }\n"
" i = i + 1\n"
" }\n"
" return -1\n"
" }\n"
"\n"
" static processBlockElements_(html) {\n"
" var result = html\n"
"\n"
" result = processHeadingsFromHtml_(result)\n"
" result = processCodeBlocksFromHtml_(result)\n"
" result = processBlockquotesFromHtml_(result)\n"
" result = processListsFromHtml_(result)\n"
" result = processHrFromHtml_(result)\n"
" result = processParagraphsFromHtml_(result)\n"
" result = processBrFromHtml_(result)\n"
" result = stripContainerTags_(result)\n"
"\n"
" return result\n"
" }\n"
"\n"
" static processHeadingsFromHtml_(html) {\n"
" var result = html\n"
" for (level in 1..6) {\n"
" var prefix = \"\"\n"
" for (i in 0...level) prefix = prefix + \"#\"\n"
" result = replaceTag_(result, \"h\" + level.toString, prefix + \" \", \"\\n\\n\")\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static processCodeBlocksFromHtml_(html) {\n"
" var result = html\n"
"\n"
" while (true) {\n"
" var preStart = findTagStart_(result, \"pre\")\n"
" if (preStart < 0) break\n"
"\n"
" var preOpenEnd = preStart\n"
" while (preOpenEnd < result.count && result[preOpenEnd] != \">\") {\n"
" preOpenEnd = preOpenEnd + 1\n"
" }\n"
" if (preOpenEnd >= result.count) break\n"
"\n"
" var preCloseStart = findCloseTag_(result, preOpenEnd + 1, \"pre\")\n"
" if (preCloseStart < 0) break\n"
"\n"
" var content = result[preOpenEnd + 1...preCloseStart]\n"
"\n"
" var codeStart = findTagStart_(content, \"code\")\n"
" if (codeStart >= 0) {\n"
" var codeOpenEnd = codeStart\n"
" while (codeOpenEnd < content.count && content[codeOpenEnd] != \">\") {\n"
" codeOpenEnd = codeOpenEnd + 1\n"
" }\n"
" var codeCloseStart = findCloseTag_(content, codeOpenEnd + 1, \"code\")\n"
" if (codeCloseStart >= 0) {\n"
" content = content[codeOpenEnd + 1...codeCloseStart]\n"
" }\n"
" }\n"
"\n"
" var preCloseEnd = preCloseStart\n"
" while (preCloseEnd < result.count && result[preCloseEnd] != \">\") {\n"
" preCloseEnd = preCloseEnd + 1\n"
" }\n"
"\n"
" var before = result[0...preStart]\n"
" var after = result[preCloseEnd + 1..-1]\n"
" result = before + \"\\n```\\n\" + content.trim() + \"\\n```\\n\" + after\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" static processBlockquotesFromHtml_(html) {\n"
" var result = html\n"
"\n"
" while (true) {\n"
" var start = findTagStart_(result, \"blockquote\")\n"
" if (start < 0) break\n"
"\n"
" var openEnd = start\n"
" while (openEnd < result.count && result[openEnd] != \">\") {\n"
" openEnd = openEnd + 1\n"
" }\n"
" if (openEnd >= result.count) break\n"
"\n"
" var closeStart = findCloseTag_(result, openEnd + 1, \"blockquote\")\n"
" if (closeStart < 0) break\n"
"\n"
" var content = result[openEnd + 1...closeStart].trim()\n"
" var lines = content.split(\"\\n\")\n"
" var quoted = []\n"
" for (line in lines) {\n"
" var trimmed = line.trim()\n"
" if (trimmed.count > 0) {\n"
" quoted.add(\"> \" + trimmed)\n"
" }\n"
" }\n"
"\n"
" var closeEnd = closeStart\n"
" while (closeEnd < result.count && result[closeEnd] != \">\") {\n"
" closeEnd = closeEnd + 1\n"
" }\n"
"\n"
" var before = result[0...start]\n"
" var after = result[closeEnd + 1..-1]\n"
" result = before + \"\\n\" + quoted.join(\"\\n\") + \"\\n\" + after\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" static processListsFromHtml_(html) {\n"
" var result = html\n"
" result = processListType_(result, \"ul\", \"-\")\n"
" result = processListType_(result, \"ol\", \"1.\")\n"
" return result\n"
" }\n"
"\n"
" static processListType_(html, tag, marker) {\n"
" var result = html\n"
"\n"
" while (true) {\n"
" var start = findTagStart_(result, tag)\n"
" if (start < 0) break\n"
"\n"
" var openEnd = start\n"
" while (openEnd < result.count && result[openEnd] != \">\") {\n"
" openEnd = openEnd + 1\n"
" }\n"
" if (openEnd >= result.count) break\n"
"\n"
" var closeStart = findCloseTag_(result, openEnd + 1, tag)\n"
" if (closeStart < 0) break\n"
"\n"
" var content = result[openEnd + 1...closeStart]\n"
" var items = extractListItems_(content)\n"
" var mdItems = []\n"
" var num = 1\n"
" for (item in items) {\n"
" if (tag == \"ol\") {\n"
" mdItems.add(num.toString + \". \" + item.trim())\n"
" num = num + 1\n"
" } else {\n"
" mdItems.add(marker + \" \" + item.trim())\n"
" }\n"
" }\n"
"\n"
" var closeEnd = closeStart\n"
" while (closeEnd < result.count && result[closeEnd] != \">\") {\n"
" closeEnd = closeEnd + 1\n"
" }\n"
"\n"
" var before = result[0...start]\n"
" var after = result[closeEnd + 1..-1]\n"
" result = before + \"\\n\" + mdItems.join(\"\\n\") + \"\\n\" + after\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" static extractListItems_(html) {\n"
" var items = []\n"
" var i = 0\n"
" while (i < html.count) {\n"
" var liStart = findTagStartFrom_(html, i, \"li\")\n"
" if (liStart < 0) break\n"
"\n"
" var openEnd = liStart\n"
" while (openEnd < html.count && html[openEnd] != \">\") {\n"
" openEnd = openEnd + 1\n"
" }\n"
" if (openEnd >= html.count) break\n"
"\n"
" var closeStart = findCloseTag_(html, openEnd + 1, \"li\")\n"
" var content = \"\"\n"
" if (closeStart >= 0) {\n"
" content = html[openEnd + 1...closeStart]\n"
" var closeEnd = closeStart\n"
" while (closeEnd < html.count && html[closeEnd] != \">\") {\n"
" closeEnd = closeEnd + 1\n"
" }\n"
" i = closeEnd + 1\n"
" } else {\n"
" var nextLi = findTagStartFrom_(html, openEnd + 1, \"li\")\n"
" if (nextLi >= 0) {\n"
" content = html[openEnd + 1...nextLi]\n"
" i = nextLi\n"
" } else {\n"
" content = html[openEnd + 1..-1]\n"
" i = html.count\n"
" }\n"
" }\n"
" items.add(stripAllTags_(content).trim())\n"
" }\n"
" return items\n"
" }\n"
"\n"
" static findTagStartFrom_(html, start, tag) {\n"
" var i = start\n"
" while (i < html.count) {\n"
" if (matchTagName_(html, i, tag)) return i\n"
" i = i + 1\n"
" }\n"
" return -1\n"
" }\n"
"\n"
" static processHrFromHtml_(html) {\n"
" var result = html\n"
" var i = 0\n"
" while (i < result.count) {\n"
" if (matchTagName_(result, i, \"hr\")) {\n"
" var end = i\n"
" while (end < result.count && result[end] != \">\") {\n"
" end = end + 1\n"
" }\n"
" result = result[0...i] + \"\\n---\\n\" + result[end + 1..-1]\n"
" }\n"
" i = i + 1\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static processParagraphsFromHtml_(html) {\n"
" var result = html\n"
" result = replaceTag_(result, \"p\", \"\", \"\\n\\n\")\n"
" return result\n"
" }\n"
"\n"
" static processBrFromHtml_(html) {\n"
" var result = html\n"
" var i = 0\n"
" while (i < result.count) {\n"
" if (matchTagName_(result, i, \"br\")) {\n"
" var end = i\n"
" while (end < result.count && result[end] != \">\") {\n"
" end = end + 1\n"
" }\n"
" result = result[0...i] + \"\\n\" + result[end + 1..-1]\n"
" }\n"
" i = i + 1\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static stripContainerTags_(html) {\n"
" var result = html\n"
" var tags = [\"div\", \"span\", \"section\", \"article\", \"main\", \"header\", \"footer\", \"nav\", \"html\", \"body\"]\n"
" for (tag in tags) {\n"
" result = replaceTag_(result, tag, \"\", \"\")\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static processInlineFromHtml_(html) {\n"
" var result = html\n"
"\n"
" result = processStrongFromHtml_(result)\n"
" result = processEmFromHtml_(result)\n"
" result = processCodeFromHtml_(result)\n"
" result = processDelFromHtml_(result)\n"
" result = processLinksFromHtml_(result)\n"
" result = processImagesFromHtml_(result)\n"
"\n"
" return result\n"
" }\n"
"\n"
" static processStrongFromHtml_(html) {\n"
" var result = html\n"
" result = replaceTag_(result, \"strong\", \"**\", \"**\")\n"
" result = replaceTag_(result, \"b\", \"**\", \"**\")\n"
" return result\n"
" }\n"
"\n"
" static processEmFromHtml_(html) {\n"
" var result = html\n"
" result = replaceTag_(result, \"em\", \"*\", \"*\")\n"
" result = replaceTag_(result, \"i\", \"*\", \"*\")\n"
" return result\n"
" }\n"
"\n"
" static processCodeFromHtml_(html) {\n"
" return replaceTag_(html, \"code\", \"`\", \"`\")\n"
" }\n"
"\n"
" static processDelFromHtml_(html) {\n"
" var result = html\n"
" result = replaceTag_(result, \"del\", \"~~\", \"~~\")\n"
" result = replaceTag_(result, \"s\", \"~~\", \"~~\")\n"
" result = replaceTag_(result, \"strike\", \"~~\", \"~~\")\n"
" return result\n"
" }\n"
"\n"
" static processLinksFromHtml_(html) {\n"
" var result = html\n"
"\n"
" while (true) {\n"
" var start = findTagStart_(result, \"a\")\n"
" if (start < 0) break\n"
"\n"
" var openEnd = start\n"
" while (openEnd < result.count && result[openEnd] != \">\") {\n"
" openEnd = openEnd + 1\n"
" }\n"
" if (openEnd >= result.count) break\n"
"\n"
" var tagContent = result[start...openEnd + 1]\n"
" var href = extractAttr_(tagContent, \"href\")\n"
"\n"
" var closeStart = findCloseTag_(result, openEnd + 1, \"a\")\n"
" if (closeStart < 0) break\n"
"\n"
" var linkText = result[openEnd + 1...closeStart]\n"
"\n"
" var closeEnd = closeStart\n"
" while (closeEnd < result.count && result[closeEnd] != \">\") {\n"
" closeEnd = closeEnd + 1\n"
" }\n"
"\n"
" var before = result[0...start]\n"
" var after = result[closeEnd + 1..-1]\n"
"\n"
" if (href != null && href.count > 0) {\n"
" result = before + \"[\" + stripAllTags_(linkText) + \"](\" + href + \")\" + after\n"
" } else {\n"
" result = before + stripAllTags_(linkText) + after\n"
" }\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" static processImagesFromHtml_(html) {\n"
" var result = html\n"
" var i = 0\n"
"\n"
" while (i < result.count) {\n"
" if (matchTagName_(result, i, \"img\")) {\n"
" var end = i\n"
" while (end < result.count && result[end] != \">\") {\n"
" end = end + 1\n"
" }\n"
"\n"
" var tagContent = result[i...end + 1]\n"
" var src = extractAttr_(tagContent, \"src\")\n"
" var alt = extractAttr_(tagContent, \"alt\")\n"
"\n"
" if (src == null) src = \"\"\n"
" if (alt == null) alt = \"\"\n"
"\n"
" var mdImg = \"![\" + alt + \"](\" + src + \")\"\n"
" result = result[0...i] + mdImg + result[end + 1..-1]\n"
" i = i + mdImg.count\n"
" } else {\n"
" i = i + 1\n"
" }\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" static extractAttr_(tag, name) {\n"
" var search = name + \"=\\\"\"\n"
" var start = tag.indexOf(search)\n"
" if (start < 0) {\n"
" search = name + \"='\"\n"
" start = tag.indexOf(search)\n"
" }\n"
" if (start < 0) return null\n"
"\n"
" var valueStart = start + search.count\n"
" var quote = search[-1]\n"
" var valueEnd = valueStart\n"
" while (valueEnd < tag.count && tag[valueEnd] != quote) {\n"
" valueEnd = valueEnd + 1\n"
" }\n"
"\n"
" return tag[valueStart...valueEnd]\n"
" }\n"
"\n"
" static replaceTag_(html, tag, prefix, suffix) {\n"
" var result = html\n"
"\n"
" while (true) {\n"
" var start = findTagStart_(result, tag)\n"
" if (start < 0) break\n"
"\n"
" var openEnd = start\n"
" while (openEnd < result.count && result[openEnd] != \">\") {\n"
" openEnd = openEnd + 1\n"
" }\n"
" if (openEnd >= result.count) break\n"
"\n"
" if (result[openEnd - 1] == \"/\") {\n"
" result = result[0...start] + result[openEnd + 1..-1]\n"
" continue\n"
" }\n"
"\n"
" var closeStart = findCloseTag_(result, openEnd + 1, tag)\n"
" if (closeStart < 0) {\n"
" result = result[0...start] + result[openEnd + 1..-1]\n"
" continue\n"
" }\n"
"\n"
" var content = result[openEnd + 1...closeStart]\n"
"\n"
" var closeEnd = closeStart\n"
" while (closeEnd < result.count && result[closeEnd] != \">\") {\n"
" closeEnd = closeEnd + 1\n"
" }\n"
"\n"
" var before = result[0...start]\n"
" var after = result[closeEnd + 1..-1]\n"
" result = before + prefix + content + suffix + after\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" static findTagStart_(html, tag) {\n"
" return findTagStartFrom_(html, 0, tag)\n"
" }\n"
"\n"
" static stripAllTags_(html) {\n"
" var result = \"\"\n"
" var inTag = false\n"
" for (c in html) {\n"
" if (c == \"<\") {\n"
" inTag = true\n"
" } else if (c == \">\") {\n"
" inTag = false\n"
" } else if (!inTag) {\n"
" result = result + c\n"
" }\n"
" }\n"
" return result\n"
" }\n"
"\n"
" static cleanupWhitespace_(text) {\n"
" var result = text\n"
"\n"
" while (result.contains(\"\\n\\n\\n\")) {\n"
" result = result.replace(\"\\n\\n\\n\", \"\\n\\n\")\n"
" }\n"
"\n"
" var lines = result.split(\"\\n\")\n"
" var cleaned = []\n"
" for (line in lines) {\n"
" cleaned.add(line.trim())\n"
" }\n"
"\n"
" return cleaned.join(\"\\n\")\n"
" }\n"
"}\n";