|
| 1 | +<html> |
| 2 | +<head> |
| 3 | +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
| 4 | +<title>Render Markdown</title> |
| 5 | +<script defer data-domain="tools.simonwillison.net" src="https://plausible.io/js/plausible.js"></script> |
| 6 | +<style> |
| 7 | +body { |
| 8 | + padding: 1em; |
| 9 | + margin: 0 |
| 10 | +} |
| 11 | +#preview { |
| 12 | + padding: 0.5em; |
| 13 | + border: 1px dotted black; |
| 14 | +} |
| 15 | + |
| 16 | +button { |
| 17 | + font-size: 1.4em; |
| 18 | +} |
| 19 | + |
| 20 | +/*! |
| 21 | + * GitHub Light v0.5.0 |
| 22 | + * Copyright (c) 2012 - 2017 GitHub, Inc. |
| 23 | + * Licensed under MIT (https://github.com/primer/github-syntax-theme-generator/blob/master/LICENSE) |
| 24 | + */ |
| 25 | + |
| 26 | + .pl-c /* comment, punctuation.definition.comment, string.comment */ { |
| 27 | + color: #6a737d; |
| 28 | +} |
| 29 | + |
| 30 | +.pl-c1 /* constant, entity.name.constant, variable.other.constant, variable.language, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header, meta.output */, |
| 31 | +.pl-s .pl-v /* string variable */ { |
| 32 | + color: #005cc5; |
| 33 | +} |
| 34 | + |
| 35 | +.pl-e /* entity */, |
| 36 | +.pl-en /* entity.name */ { |
| 37 | + color: #6f42c1; |
| 38 | +} |
| 39 | + |
| 40 | +.pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */, |
| 41 | +.pl-s .pl-s1 /* string source */ { |
| 42 | + color: #24292e; |
| 43 | +} |
| 44 | + |
| 45 | +.pl-ent /* entity.name.tag, markup.quote */ { |
| 46 | + color: #22863a; |
| 47 | +} |
| 48 | + |
| 49 | +.pl-k /* keyword, storage, storage.type */ { |
| 50 | + color: #d73a49; |
| 51 | +} |
| 52 | + |
| 53 | +.pl-s /* string */, |
| 54 | +.pl-pds /* punctuation.definition.string, source.regexp, string.regexp.character-class */, |
| 55 | +.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, |
| 56 | +.pl-sr /* string.regexp */, |
| 57 | +.pl-sr .pl-cce /* string.regexp constant.character.escape */, |
| 58 | +.pl-sr .pl-sre /* string.regexp source.ruby.embedded */, |
| 59 | +.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ { |
| 60 | + color: #032f62; |
| 61 | +} |
| 62 | + |
| 63 | +.pl-v /* variable */, |
| 64 | +.pl-smw /* sublimelinter.mark.warning */ { |
| 65 | + color: #e36209; |
| 66 | +} |
| 67 | + |
| 68 | +.pl-bu /* invalid.broken, invalid.deprecated, invalid.unimplemented, message.error, brackethighlighter.unmatched, sublimelinter.mark.error */ { |
| 69 | + color: #b31d28; |
| 70 | +} |
| 71 | + |
| 72 | +.pl-ii /* invalid.illegal */ { |
| 73 | + color: #fafbfc; |
| 74 | + background-color: #b31d28; |
| 75 | +} |
| 76 | + |
| 77 | +.pl-c2 /* carriage-return */ { |
| 78 | + color: #fafbfc; |
| 79 | + background-color: #d73a49; |
| 80 | +} |
| 81 | + |
| 82 | +.pl-c2::before /* carriage-return */ { |
| 83 | + content: "^M"; |
| 84 | +} |
| 85 | + |
| 86 | +.pl-sr .pl-cce /* string.regexp constant.character.escape */ { |
| 87 | + font-weight: bold; |
| 88 | + color: #22863a; |
| 89 | +} |
| 90 | + |
| 91 | +.pl-ml /* markup.list */ { |
| 92 | + color: #735c0f; |
| 93 | +} |
| 94 | + |
| 95 | +.pl-mh /* markup.heading */, |
| 96 | +.pl-mh .pl-en /* markup.heading entity.name */, |
| 97 | +.pl-ms /* meta.separator */ { |
| 98 | + font-weight: bold; |
| 99 | + color: #005cc5; |
| 100 | +} |
| 101 | + |
| 102 | +.pl-mi /* markup.italic */ { |
| 103 | + font-style: italic; |
| 104 | + color: #24292e; |
| 105 | +} |
| 106 | + |
| 107 | +.pl-mb /* markup.bold */ { |
| 108 | + font-weight: bold; |
| 109 | + color: #24292e; |
| 110 | +} |
| 111 | + |
| 112 | +.pl-md /* markup.deleted, meta.diff.header.from-file, punctuation.definition.deleted */ { |
| 113 | + color: #b31d28; |
| 114 | + background-color: #ffeef0; |
| 115 | +} |
| 116 | + |
| 117 | +.pl-mi1 /* markup.inserted, meta.diff.header.to-file, punctuation.definition.inserted */ { |
| 118 | + color: #22863a; |
| 119 | + background-color: #f0fff4; |
| 120 | +} |
| 121 | + |
| 122 | +.pl-mc /* markup.changed, punctuation.definition.changed */ { |
| 123 | + color: #e36209; |
| 124 | + background-color: #ffebda; |
| 125 | +} |
| 126 | + |
| 127 | +.pl-mi2 /* markup.ignored, markup.untracked */ { |
| 128 | + color: #f6f8fa; |
| 129 | + background-color: #005cc5; |
| 130 | +} |
| 131 | + |
| 132 | +.pl-mdr /* meta.diff.range */ { |
| 133 | + font-weight: bold; |
| 134 | + color: #6f42c1; |
| 135 | +} |
| 136 | + |
| 137 | +.pl-ba /* brackethighlighter.tag, brackethighlighter.curly, brackethighlighter.round, brackethighlighter.square, brackethighlighter.angle, brackethighlighter.quote */ { |
| 138 | + color: #586069; |
| 139 | +} |
| 140 | + |
| 141 | +.pl-sg /* sublimelinter.gutter-mark */ { |
| 142 | + color: #959da5; |
| 143 | +} |
| 144 | + |
| 145 | +.pl-corl /* constant.other.reference.link, string.other.link */ { |
| 146 | + text-decoration: underline; |
| 147 | + color: #032f62; |
| 148 | +} |
| 149 | +</style> |
| 150 | +</head> |
| 151 | +<body> |
| 152 | +<h1>Render Markdown</h1> |
| 153 | +<p>Using GitHub's API, see <a href="/til/til/markdown_github-markdown-api.md">Rendering Markdown with the GitHub Markdown API</a></p> |
| 154 | +<p><textarea id="input" style="width: 80%; height: 10em"></textarea></p> |
| 155 | +<p> |
| 156 | + <button>Render</button> |
| 157 | + <label><input type="checkbox" id="strip_hidden" checked> Strip hidden and cleanup HTML</label> |
| 158 | + <label><input type="checkbox" id="gfm_mode"> Use GitHub Flavored Markdown (GFM)</label> |
| 159 | +</p> |
| 160 | +<p><textarea id="output" style="width: 80%; height: 10em"></textarea></p> |
| 161 | +<div id="preview"></div> |
| 162 | +<script> |
| 163 | +const button = document.getElementsByTagName('button')[0]; |
| 164 | +const strip_hidden = document.getElementById('strip_hidden'); |
| 165 | +const input = document.getElementById('input'); |
| 166 | +const output = document.getElementById('output'); |
| 167 | +const preview = document.getElementById('preview'); |
| 168 | +button.addEventListener('click', async function() { |
| 169 | + const rendered = await render(input.value); |
| 170 | + output.value = rendered; |
| 171 | + preview.innerHTML = rendered; |
| 172 | + if (strip_hidden.checked) { |
| 173 | + Array.from( |
| 174 | + preview.querySelectorAll('[aria-hidden]') |
| 175 | + ).forEach(el => el.parentNode.removeChild(el)); |
| 176 | + Array.from( |
| 177 | + preview.querySelectorAll('[rel="nofollow"]') |
| 178 | + ).forEach(el => el.removeAttribute('rel')); |
| 179 | + // <div class="highlight highlight-source-python"> |
| 180 | + Array.from( |
| 181 | + preview.querySelectorAll('div.highlight-source-python') |
| 182 | + ).forEach(el => el.replaceWith(el.firstChild)); |
| 183 | + /* Remove <a href="https://camo.githubusercontent.com"... wrapper |
| 184 | + links around images, and replace src= with data-canonical-src= */ |
| 185 | + Array.from( |
| 186 | + preview.querySelectorAll( |
| 187 | + 'a[href^="https://camo.githubusercontent.com"]' |
| 188 | + ) |
| 189 | + ).forEach(el => el.replaceWith(el.firstChild)); |
| 190 | + Array.from( |
| 191 | + preview.querySelectorAll( |
| 192 | + 'img[data-canonical-src]' |
| 193 | + ) |
| 194 | + ).forEach( |
| 195 | + el => { |
| 196 | + el.setAttribute('src', el.getAttribute('data-canonical-src')); |
| 197 | + el.removeAttribute('data-canonical-src'); |
| 198 | + } |
| 199 | + ); |
| 200 | + // Remove <a class="heading-link"> links, replace with their content |
| 201 | + Array.from( |
| 202 | + preview.querySelectorAll('a.heading-link') |
| 203 | + ).forEach(el => el.replaceWith(el.firstChild)); |
| 204 | + // Output it to the textarea |
| 205 | + output.value = preview.innerHTML.replace( |
| 206 | + /<h([1-6])>\n/g, '<h$1>' |
| 207 | + ).replace( |
| 208 | + /<br>/g, '<br />' |
| 209 | + ).replace( |
| 210 | + /<img ([^>].+?)>/g, '<img $1 />' |
| 211 | + ); |
| 212 | + } |
| 213 | + // And prepend the menu as well |
| 214 | + const menu = '<ul>\n' + ( |
| 215 | + Array.from( |
| 216 | + document.querySelectorAll('h2[id],h3[id],h4[id],h5[id],h6[id]') |
| 217 | + ).map(el => ` <li><a href="#${el.id}">${el.innerText}</a></li>`).join('\n') |
| 218 | + ) + '\n</ul>'; |
| 219 | + output.value = menu + '\n\n' + output.value; |
| 220 | + preview.innerHTML = menu + '\n\n' + preview.innerHTML; |
| 221 | +}); |
| 222 | + |
| 223 | +async function render(markdown) { |
| 224 | + const mode = document.getElementById('gfm_mode').checked ? 'gfm' : 'markdown'; |
| 225 | + return (await fetch('https://api.github.com/markdown', { |
| 226 | + method: 'POST', |
| 227 | + headers: { |
| 228 | + 'Content-Type': 'application/json' |
| 229 | + }, |
| 230 | + body: JSON.stringify({'mode': mode, 'text': markdown}) |
| 231 | + })).text(); |
| 232 | +} |
| 233 | + |
| 234 | +/* Populate textarea from localStorage */ |
| 235 | +if (!input.value && localStorage.getItem('saved')) { |
| 236 | + input.value = localStorage.getItem('saved'); |
| 237 | + button.click(); |
| 238 | +} |
| 239 | +input.addEventListener('input', () => { |
| 240 | + localStorage.setItem('saved', input.value); |
| 241 | +}); |
| 242 | + |
| 243 | +</script> |
| 244 | + |
| 245 | +</body> |
| 246 | +</html> |
0 commit comments