// 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 \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 codeBlockLang = null\n" " var inList = false\n" " var listType = null\n" " var listClass = null\n" " var inBlockquote = false\n" " var inTable = false\n" " var tableRows = []\n" "\n" " for (i in 0...lines.count) {\n" " var line = lines[i]\n" "\n" " if (line.startsWith(\"```\")) {\n" " if (inCodeBlock) {\n" " var langAttr = codeBlockLang != null ? \" class=\\\"language-%(codeBlockLang)\\\"\" : \"\"\n" " result.add(\"
\" + (safeMode ? Html.quote(codeBlockContent.join(\"\\n\")) : codeBlockContent.join(\"\\n\")) + \"
\")\n" " codeBlockContent = []\n" " codeBlockLang = null\n" " inCodeBlock = false\n" " } else {\n" " closeTable_(result, inTable, tableRows, safeMode)\n" " inTable = false\n" " tableRows = []\n" " closeList_(result, inList, listType, listClass)\n" " inList = false\n" " listClass = null\n" " inCodeBlock = true\n" " var lang = line[3..-1].trim()\n" " if (lang.count > 0) codeBlockLang = lang\n" " }\n" " continue\n" " }\n" "\n" " if (inCodeBlock) {\n" " codeBlockContent.add(line)\n" " continue\n" " }\n" "\n" " if (isTableRow_(line)) {\n" " if (!inTable) {\n" " closeList_(result, inList, listType, listClass)\n" " inList = false\n" " listClass = null\n" " if (inBlockquote) {\n" " result.add(\"\")\n" " inBlockquote = false\n" " }\n" " inTable = true\n" " tableRows = []\n" " }\n" " tableRows.add(line)\n" " continue\n" " }\n" "\n" " if (inTable) {\n" " result.add(buildTable_(tableRows, safeMode))\n" " inTable = false\n" " tableRows = []\n" " }\n" "\n" " if (line.count == 0) {\n" " closeList_(result, inList, listType, listClass)\n" " inList = false\n" " listClass = null\n" " if (inBlockquote) {\n" " result.add(\"\")\n" " inBlockquote = false\n" " }\n" " continue\n" " }\n" "\n" " if (line.startsWith(\"> \")) {\n" " closeList_(result, inList, listType, listClass)\n" " inList = false\n" " listClass = null\n" " if (!inBlockquote) {\n" " result.add(\"
\")\n" " inBlockquote = true\n" " }\n" " result.add(\"

\" + processInline_(line[2..-1], safeMode) + \"

\")\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, listClass)\n" " inList = false\n" " listClass = null\n" " result.add(\"
\")\n" " continue\n" " }\n" " }\n" "\n" " var heading = parseHeading_(line)\n" " if (heading) {\n" " closeList_(result, inList, listType, listClass)\n" " inList = false\n" " listClass = null\n" " result.add(\"\" + processInline_(heading[\"text\"], safeMode) + \"\")\n" " continue\n" " }\n" "\n" " var listItem = parseListItem_(line, safeMode)\n" " if (listItem) {\n" " var newType = listItem[\"type\"]\n" " var newClass = listItem[\"class\"]\n" " if (!inList || listType != newType || listClass != newClass) {\n" " closeList_(result, inList, listType, listClass)\n" " var classAttr = newClass != null ? \" class=\\\"%(newClass)\\\"\" : \"\"\n" " result.add(newType == \"ul\" ? \"\" : \"\")\n" " inList = true\n" " listType = newType\n" " listClass = newClass\n" " }\n" " result.add(listItem[\"html\"])\n" " continue\n" " }\n" "\n" " closeList_(result, inList, listType, listClass)\n" " inList = false\n" " listClass = null\n" " result.add(\"

\" + processInline_(line, safeMode) + \"

\")\n" " }\n" "\n" " closeTable_(result, inTable, tableRows, safeMode)\n" " closeList_(result, inList, listType, listClass)\n" " if (inBlockquote) {\n" " result.add(\"
\")\n" " }\n" " if (inCodeBlock) {\n" " var langAttr = codeBlockLang != null ? \" class=\\\"language-%(codeBlockLang)\\\"\" : \"\"\n" " result.add(\"
\" + (safeMode ? Html.quote(codeBlockContent.join(\"\\n\")) : codeBlockContent.join(\"\\n\")) + \"
\")\n" " }\n" "\n" " return result.join(\"\\n\")\n" " }\n" "\n" " static closeList_(result, inList, listType, listClass) {\n" " if (inList) {\n" " result.add(listType == \"ul\" ? \"\" : \"\")\n" " }\n" " }\n" "\n" " static closeTable_(result, inTable, tableRows, safeMode) {\n" " if (inTable && tableRows.count > 0) {\n" " result.add(buildTable_(tableRows, safeMode))\n" " }\n" " }\n" "\n" " static isTableRow_(line) {\n" " var trimmed = line.trim()\n" " return trimmed.contains(\"|\") && (trimmed.startsWith(\"|\") || trimmed.endsWith(\"|\"))\n" " }\n" "\n" " static isTableSeparator_(line) {\n" " var trimmed = line.trim()\n" " if (!trimmed.contains(\"|\")) return false\n" " if (!trimmed.contains(\"-\")) return false\n" " for (c in trimmed) {\n" " if (c != \"|\" && c != \"-\" && c != \":\" && c != \" \") return false\n" " }\n" " return true\n" " }\n" "\n" " static splitTableCells_(line) {\n" " var trimmed = line.trim()\n" " if (trimmed.startsWith(\"|\")) trimmed = trimmed[1..-1]\n" " if (trimmed.endsWith(\"|\")) trimmed = trimmed[0..-2]\n" " return trimmed.split(\"|\")\n" " }\n" "\n" " static parseTableAlignments_(line) {\n" " var cells = splitTableCells_(line)\n" " var alignments = []\n" " for (cell in cells) {\n" " var t = cell.trim()\n" " var left = t.count > 0 && t[0] == \":\"\n" " var right = t.count > 0 && t[-1] == \":\"\n" " if (left && right) {\n" " alignments.add(\"center\")\n" " } else if (right) {\n" " alignments.add(\"right\")\n" " } else {\n" " alignments.add(\"left\")\n" " }\n" " }\n" " return alignments\n" " }\n" "\n" " static buildTable_(rows, safeMode) {\n" " if (rows.count < 2) return \"\"\n" "\n" " var separatorIdx = -1\n" " for (i in 1...rows.count) {\n" " if (isTableSeparator_(rows[i])) {\n" " separatorIdx = i\n" " break\n" " }\n" " }\n" "\n" " if (separatorIdx < 0) return \"\"\n" "\n" " var alignments = parseTableAlignments_(rows[separatorIdx])\n" " var html = [\"\", \"\", \"\"]\n" "\n" " for (r in 0...separatorIdx) {\n" " var headerCells = splitTableCells_(rows[r])\n" " for (i in 0...headerCells.count) {\n" " var align = i < alignments.count ? alignments[i] : \"left\"\n" " var style = align != \"left\" ? \" style=\\\"text-align:%(align)\\\"\" : \"\"\n" " html.add(\"\" + processInline_(headerCells[i].trim(), safeMode) + \"\")\n" " }\n" " }\n" "\n" " html.add(\"\")\n" " html.add(\"\")\n" " html.add(\"\")\n" "\n" " for (r in (separatorIdx + 1)...rows.count) {\n" " var cells = splitTableCells_(rows[r])\n" " html.add(\"\")\n" " for (i in 0...cells.count) {\n" " var align = i < alignments.count ? alignments[i] : \"left\"\n" " var style = align != \"left\" ? \" style=\\\"text-align:%(align)\\\"\" : \"\"\n" " html.add(\"\" + processInline_(cells[i].trim(), safeMode) + \"\")\n" " }\n" " html.add(\"\")\n" " }\n" "\n" " html.add(\"\")\n" " html.add(\"
\")\n" " return html.join(\"\\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" " return parseListItem_(line, false)\n" " }\n" "\n" " static parseListItem_(line, safeMode) {\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" " var text = trimmed[2..-1]\n" " var taskInfo = parseTaskCheckbox_(text)\n" " if (taskInfo) {\n" " var checkbox = taskInfo[\"checked\"] ?\n" " \"\" :\n" " \"\"\n" " return {\n" " \"type\": \"ul\",\n" " \"text\": taskInfo[\"text\"],\n" " \"class\": \"task-list\",\n" " \"html\": \"
  • \" + checkbox + \" \" + processInline_(taskInfo[\"text\"], safeMode) + \"
  • \"\n" " }\n" " }\n" " return {\n" " \"type\": \"ul\",\n" " \"text\": text,\n" " \"class\": null,\n" " \"html\": \"
  • \" + processInline_(text, safeMode) + \"
  • \"\n" " }\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" " var text = trimmed[i + 2..-1]\n" " return {\n" " \"type\": \"ol\",\n" " \"text\": text,\n" " \"class\": null,\n" " \"html\": \"
  • \" + processInline_(text, safeMode) + \"
  • \"\n" " }\n" " }\n" "\n" " return null\n" " }\n" "\n" " static parseTaskCheckbox_(text) {\n" " if (text.count >= 3) {\n" " if (text.startsWith(\"[ ] \")) {\n" " return {\"checked\": false, \"text\": text[4..-1]}\n" " }\n" " if (text.startsWith(\"[x] \") || text.startsWith(\"[X] \")) {\n" " return {\"checked\": true, \"text\": text[4..-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" " text = processAutolinks_(text)\n" " return text\n" " }\n" "\n" " static processAutolinks_(text) {\n" " var result = text\n" " result = processUrlAutolinks_(result, \"https://\")\n" " result = processUrlAutolinks_(result, \"http://\")\n" " result = processEmailAutolinks_(result)\n" " return result\n" " }\n" "\n" " static processUrlAutolinks_(text, protocol) {\n" " var result = text\n" " var searchPos = 0\n" " while (true) {\n" " if (result.count < protocol.count) break\n" " if (searchPos > result.count - protocol.count) break\n" "\n" " var idx = findProtocol_(result, protocol, searchPos)\n" " if (idx < 0) break\n" "\n" " if (idx > 0 && result[idx - 1] == \"\\\"\") {\n" " searchPos = idx + 1\n" " continue\n" " }\n" " if (idx >= 6) {\n" " var prefix = result[idx - 6...idx]\n" " if (prefix == \"href=\\\"\" || prefix == \"href='\") {\n" " searchPos = idx + 1\n" " continue\n" " }\n" " }\n" "\n" " var urlEnd = idx + protocol.count\n" " while (urlEnd < result.count) {\n" " var c = result[urlEnd]\n" " if (c == \" \" || c == \"\\t\" || c == \"\\n\" || c == \"<\" || c == \">\" || c == \"\\\"\" || c == \")\") break\n" " urlEnd = urlEnd + 1\n" " }\n" "\n" " var url = result[idx...urlEnd]\n" " var before = result[0...idx]\n" " var after = result[urlEnd..-1]\n" " var replacement = \"%(url)\"\n" " result = before + replacement + after\n" " searchPos = idx + replacement.count\n" " }\n" " return result\n" " }\n" "\n" " static findProtocol_(text, protocol, start) {\n" " var maxPos = text.count - protocol.count\n" " var i = start\n" " while (i <= maxPos) {\n" " var match = true\n" " var j = 0\n" " while (j < protocol.count) {\n" " if (text[i + j] != protocol[j]) {\n" " match = false\n" " break\n" " }\n" " j = j + 1\n" " }\n" " if (match) return i\n" " i = i + 1\n" " }\n" " return -1\n" " }\n" "\n" " static processEmailAutolinks_(text) {\n" " var result = text\n" " var searchPos = 0\n" " while (true) {\n" " var atIdx = -1\n" " for (i in searchPos...result.count) {\n" " if (result[i] == \"@\") {\n" " atIdx = i\n" " break\n" " }\n" " }\n" " if (atIdx < 0) break\n" "\n" " if (atIdx > 0 && result[atIdx - 1] == \"\\\"\") {\n" " searchPos = atIdx + 1\n" " continue\n" " }\n" "\n" " var localStart = atIdx\n" " while (localStart > 0) {\n" " var c = result[localStart - 1]\n" " if (!isEmailChar_(c)) break\n" " localStart = localStart - 1\n" " }\n" "\n" " var domainEnd = atIdx + 1\n" " var hasDot = false\n" " while (domainEnd < result.count) {\n" " var c = result[domainEnd]\n" " if (c == \".\") hasDot = true\n" " if (!isEmailChar_(c) && c != \".\") break\n" " domainEnd = domainEnd + 1\n" " }\n" "\n" " if (localStart < atIdx && atIdx + 1 < domainEnd && hasDot) {\n" " var email = result[localStart...domainEnd]\n" " var before = result[0...localStart]\n" " var after = result[domainEnd..-1]\n" " var replacement = \"%(email)\"\n" " result = before + replacement + after\n" " searchPos = localStart + replacement.count\n" " } else {\n" " searchPos = atIdx + 1\n" " }\n" " }\n" " return result\n" " }\n" "\n" " static isEmailChar_(c) {\n" " var cp = c.codePoints.toList[0]\n" " if (cp >= 97 && cp <= 122) return true\n" " if (cp >= 65 && cp <= 90) return true\n" " if (cp >= 48 && cp <= 57) return true\n" " if (c == \".\" || c == \"-\" || c == \"_\" || c == \"+\") return true\n" " return false\n" " }\n" "\n" " static processCode_(text) {\n" " var parts = []\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 codeChars = []\n" " for (j in (i + 1)...end) {\n" " codeChars.add(chars[j])\n" " }\n" " parts.add(\"\")\n" " parts.add(codeChars.join(\"\"))\n" " parts.add(\"\")\n" " i = end + 1\n" " } else {\n" " parts.add(chars[i])\n" " i = i + 1\n" " }\n" " } else {\n" " parts.add(chars[i])\n" " i = i + 1\n" " }\n" " }\n" " return parts.join(\"\")\n" " }\n" "\n" " static processBold_(text) {\n" " var result = processMarker_(text, \"**\", \"\", \"\")\n" " result = processMarker_(result, \"__\", \"\", \"\")\n" " return result\n" " }\n" "\n" " static processMarker_(text, marker, openTag, closeTag) {\n" " var parts = []\n" " var pos = 0\n" " var markerLen = marker.count\n" " while (pos < text.count) {\n" " var start = text.indexOf(marker, pos)\n" " if (start < 0) {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " var rest = text[start + markerLen..-1]\n" " var end = rest.indexOf(marker)\n" " if (end < 0) {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " parts.add(text[pos...start])\n" " parts.add(openTag)\n" " parts.add(rest[0...end])\n" " parts.add(closeTag)\n" " pos = start + markerLen + end + markerLen\n" " }\n" " return parts.join(\"\")\n" " }\n" "\n" " static processItalic_(text) {\n" " var parts = []\n" " var pos = 0\n" " while (pos < text.count) {\n" " var start = text.indexOf(\"*\", pos)\n" " if (start < 0) {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " if (start > 0 && text[start - 1] == \"<\") {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " var rest = text[start + 1..-1]\n" " var end = rest.indexOf(\"*\")\n" " if (end < 0 || end == 0) {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " parts.add(text[pos...start])\n" " parts.add(\"\")\n" " parts.add(rest[0...end])\n" " parts.add(\"\")\n" " pos = start + 1 + end + 1\n" " }\n" " return parts.join(\"\")\n" " }\n" "\n" " static processStrikethrough_(text) {\n" " return processMarker_(text, \"~~\", \"\", \"\")\n" " }\n" "\n" " static processLinks_(text) {\n" " var parts = []\n" " var pos = 0\n" " while (pos < text.count) {\n" " var linkEnd = text.indexOf(\"](\", pos)\n" " if (linkEnd < 0) {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " var textStart = linkEnd\n" " while (textStart > pos && text[textStart - 1] != \"[\") {\n" " textStart = textStart - 1\n" " }\n" " if (textStart > pos && text[textStart - 1] == \"[\") {\n" " var urlStart = linkEnd + 2\n" " var urlEnd = urlStart\n" " while (urlEnd < text.count && text[urlEnd] != \")\") {\n" " urlEnd = urlEnd + 1\n" " }\n" " if (urlEnd < text.count) {\n" " parts.add(text[pos...textStart - 1])\n" " var linkText = text[textStart...linkEnd]\n" " var url = text[urlStart...urlEnd]\n" " parts.add(\"\")\n" " parts.add(linkText)\n" " parts.add(\"\")\n" " pos = urlEnd + 1\n" " } else {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " } else {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " }\n" " return parts.join(\"\")\n" " }\n" "\n" " static processImages_(text) {\n" " var parts = []\n" " var pos = 0\n" " while (pos < text.count) {\n" " var start = text.indexOf(\"![\", pos)\n" " if (start < 0) {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " var altEnd = start + 2\n" " while (altEnd < text.count && text[altEnd] != \"]\") {\n" " altEnd = altEnd + 1\n" " }\n" " if (altEnd < text.count && altEnd + 1 < text.count && text[altEnd + 1] == \"(\") {\n" " var urlStart = altEnd + 2\n" " var urlEnd = urlStart\n" " while (urlEnd < text.count && text[urlEnd] != \")\") {\n" " urlEnd = urlEnd + 1\n" " }\n" " if (urlEnd < text.count) {\n" " parts.add(text[pos...start])\n" " var alt = text[start + 2...altEnd]\n" " var url = text[urlStart...urlEnd]\n" " parts.add(\"\\\"\")\n"\")\n" " pos = urlEnd + 1\n" " } else {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " } else {\n" " parts.add(text[pos..-1])\n" " break\n" " }\n" " }\n" " return parts.join(\"\")\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 = \"\"\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 = processTablesFromHtml_(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 processTablesFromHtml_(html) {\n" " var result = html\n" "\n" " while (true) {\n" " var start = findTagStart_(result, \"table\")\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, \"table\")\n" " if (closeStart < 0) break\n" "\n" " var tableContent = result[openEnd + 1...closeStart]\n" " var mdTable = convertTableToMarkdown_(tableContent)\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\" + mdTable + \"\\n\" + after\n" " }\n" "\n" " return result\n" " }\n" "\n" " static convertTableToMarkdown_(tableHtml) {\n" " var headers = []\n" " var rows = []\n" " var alignments = []\n" "\n" " var theadStart = findTagStart_(tableHtml, \"thead\")\n" " if (theadStart >= 0) {\n" " var theadOpenEnd = theadStart\n" " while (theadOpenEnd < tableHtml.count && tableHtml[theadOpenEnd] != \">\") {\n" " theadOpenEnd = theadOpenEnd + 1\n" " }\n" " var theadCloseStart = findCloseTag_(tableHtml, theadOpenEnd + 1, \"thead\")\n" " if (theadCloseStart >= 0) {\n" " var theadContent = tableHtml[theadOpenEnd + 1...theadCloseStart]\n" " headers = extractTableCells_(theadContent, \"th\")\n" " alignments = extractAlignments_(theadContent, \"th\")\n" " if (headers.count == 0) {\n" " headers = extractTableCells_(theadContent, \"td\")\n" " alignments = extractAlignments_(theadContent, \"td\")\n" " }\n" " }\n" " }\n" "\n" " var tbodyStart = findTagStart_(tableHtml, \"tbody\")\n" " if (tbodyStart >= 0) {\n" " var tbodyOpenEnd = tbodyStart\n" " while (tbodyOpenEnd < tableHtml.count && tableHtml[tbodyOpenEnd] != \">\") {\n" " tbodyOpenEnd = tbodyOpenEnd + 1\n" " }\n" " var tbodyCloseStart = findCloseTag_(tableHtml, tbodyOpenEnd + 1, \"tbody\")\n" " if (tbodyCloseStart >= 0) {\n" " var tbodyContent = tableHtml[tbodyOpenEnd + 1...tbodyCloseStart]\n" " rows = extractTableRows_(tbodyContent)\n" " }\n" " }\n" "\n" " if (headers.count == 0 && rows.count > 0) {\n" " headers = rows[0]\n" " rows = rows.count > 1 ? rows[1..-1] : []\n" " }\n" "\n" " if (headers.count == 0) return \"\"\n" "\n" " var mdLines = []\n" " mdLines.add(\"| \" + headers.join(\" | \") + \" |\")\n" "\n" " var sep = []\n" " for (i in 0...headers.count) {\n" " var align = i < alignments.count ? alignments[i] : \"left\"\n" " if (align == \"center\") {\n" " sep.add(\":---:\")\n" " } else if (align == \"right\") {\n" " sep.add(\"---:\")\n" " } else {\n" " sep.add(\"---\")\n" " }\n" " }\n" " mdLines.add(\"| \" + sep.join(\" | \") + \" |\")\n" "\n" " for (row in rows) {\n" " mdLines.add(\"| \" + row.join(\" | \") + \" |\")\n" " }\n" "\n" " return mdLines.join(\"\\n\")\n" " }\n" "\n" " static extractTableCells_(html, cellTag) {\n" " var cells = []\n" " var i = 0\n" " while (i < html.count) {\n" " var cellStart = findTagStartFrom_(html, i, cellTag)\n" " if (cellStart < 0) break\n" "\n" " var openEnd = cellStart\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, cellTag)\n" " if (closeStart >= 0) {\n" " var content = html[openEnd + 1...closeStart]\n" " cells.add(stripAllTags_(content).trim())\n" " var closeEnd = closeStart\n" " while (closeEnd < html.count && html[closeEnd] != \">\") {\n" " closeEnd = closeEnd + 1\n" " }\n" " i = closeEnd + 1\n" " } else {\n" " i = openEnd + 1\n" " }\n" " }\n" " return cells\n" " }\n" "\n" " static extractAlignments_(html, cellTag) {\n" " var alignments = []\n" " var i = 0\n" " while (i < html.count) {\n" " var cellStart = findTagStartFrom_(html, i, cellTag)\n" " if (cellStart < 0) break\n" "\n" " var openEnd = cellStart\n" " while (openEnd < html.count && html[openEnd] != \">\") {\n" " openEnd = openEnd + 1\n" " }\n" " if (openEnd >= html.count) break\n" "\n" " var tagContent = html[cellStart...openEnd + 1]\n" " var align = \"left\"\n" " if (tagContent.contains(\"text-align:center\") || tagContent.contains(\"text-align: center\")) {\n" " align = \"center\"\n" " } else if (tagContent.contains(\"text-align:right\") || tagContent.contains(\"text-align: right\")) {\n" " align = \"right\"\n" " }\n" " alignments.add(align)\n" "\n" " var closeStart = findCloseTag_(html, openEnd + 1, cellTag)\n" " if (closeStart >= 0) {\n" " var closeEnd = closeStart\n" " while (closeEnd < html.count && html[closeEnd] != \">\") {\n" " closeEnd = closeEnd + 1\n" " }\n" " i = closeEnd + 1\n" " } else {\n" " i = openEnd + 1\n" " }\n" " }\n" " return alignments\n" " }\n" "\n" " static extractTableRows_(html) {\n" " var rows = []\n" " var i = 0\n" " while (i < html.count) {\n" " var trStart = findTagStartFrom_(html, i, \"tr\")\n" " if (trStart < 0) break\n" "\n" " var openEnd = trStart\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, \"tr\")\n" " if (closeStart >= 0) {\n" " var rowContent = html[openEnd + 1...closeStart]\n" " var cells = extractTableCells_(rowContent, \"td\")\n" " if (cells.count == 0) cells = extractTableCells_(rowContent, \"th\")\n" " if (cells.count > 0) rows.add(cells)\n" " var closeEnd = closeStart\n" " while (closeEnd < html.count && html[closeEnd] != \">\") {\n" " closeEnd = closeEnd + 1\n" " }\n" " i = closeEnd + 1\n" " } else {\n" " i = openEnd + 1\n" " }\n" " }\n" " return rows\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 parts = []\n" " var i = 0\n" " while (i < html.count) {\n" " if (matchTagName_(html, i, \"hr\")) {\n" " var end = i\n" " while (end < html.count && html[end] != \">\") {\n" " end = end + 1\n" " }\n" " parts.add(\"\\n---\\n\")\n" " i = end + 1\n" " } else {\n" " parts.add(html[i])\n" " i = i + 1\n" " }\n" " }\n" " return parts.join(\"\")\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 parts = []\n" " var i = 0\n" " while (i < html.count) {\n" " if (matchTagName_(html, i, \"br\")) {\n" " var end = i\n" " while (end < html.count && html[end] != \">\") {\n" " end = end + 1\n" " }\n" " parts.add(\"\\n\")\n" " i = end + 1\n" " } else {\n" " parts.add(html[i])\n" " i = i + 1\n" " }\n" " }\n" " return parts.join(\"\")\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 parts = []\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" " parts.add(c)\n" " }\n" " }\n" " return parts.join(\"\")\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";