Skip to content

Instantly share code, notes, and snippets.

@scagood
Last active November 7, 2018 19:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scagood/8ef1225fea15ac14e5c1b41a22e7dcee to your computer and use it in GitHub Desktop.
Save scagood/8ef1225fea15ac14e5c1b41a22e7dcee to your computer and use it in GitHub Desktop.
Simple markdown to html in javscript

Supported markdown:

Headers

# Header 1 => <h1>Header 1</h1>
## Header 2 => <h2>Header 2</h2>
### Header 3 => <h3>Header 3</h3>
#### Header 4 => <h4>Header 4</h4>
##### Header 5 => <h5>Header 5</h5>
###### Header 6 => <h6>Header 6</h6>

# Header 1 ..Sub Header.. => <h1>Header 1 <small>Sub Header</small></h1>
## Header 2 ..Sub Header.. => <h2>Header 2 <small>Sub Header</small></h2>
### Header 3 ..Sub Header.. => <h3>Header 3 <small>Sub Header</small></h3>
#### Header 4 ..Sub Header.. => <h4>Header 4 <small>Sub Header</small></h4>
##### Header 5 ..Sub Header.. => <h5>Header 5 <small>Sub Header</small></h5>
###### Header 6 ..Sub Header.. => <h6>Header 6 <small>Sub Header</small></h6>

Text decoration

  • **This is Bold** => This is Bold
  • __This is Underlined__ => <u>This is Underlined</u> If only git allowed underlining 😞
  • ~~This is Italic~~ => This is Italic
  • --This is Striked-- => This is Striked

Bullet points

* foo
* bar
  * bar
  * foo

Becomes

<ul>
  <li>foo</li>
  <li>bar</li>
  <ul>
    <li>bar</li>
    <li>foo</li>
  </ul>
</ul>

Numeric points

1. foo
2. bar
  1. bar
  2. foo

Becomes

<ol>
  <li>foo</li>
  <li>bar</li>
  <ol>
    <li>bar</li>
    <li>foo</li>
  </ol>
</ol>

Mixed points

1. foo
2. bar
  * bar
  * foo

Becomes

<ol>
  <li>foo</li>
  <li>bar</li>
  <ul>
    <li>bar</li>
    <li>foo</li>
  </ul>
</ol>

Images

![](image.png)                       => <img src="image.png" alt=""></img>
![Alt text](image.png)               => <img src="image.png" alt="Alt text"></img>
![Alt text](image.png "Image title") => <img title="Image title" src="image.png" alt="Alt text"></img>

Links

[Link Text](http://google.com/)              => <a href="http://google.com/">Link Text</a>
[Link Text](http://google.com/ "Link title") => <a title="Link title" href="http://google.com/">Link Text</a>

Horizontal Rule

--- => <hr />
*** => <hr />
___ => <hr />

The horizontal rule markdown can be any length above 3 characters, the characters cant be mixed alla _---__--_***

Tables

Pretty tables are converted
| Tables        | Are           | Cool  |
| ------------- |:-------------:| -----:|
| col 3 is      | right-aligned | $1600 |
| col 2 is      | centered      |   $12 |
| zebra stripes | are neat      |    $1 |

As are dirty tables (the external pipes '|' are optional)
Markdown | Less | Pretty
--- | --- | ---
*Still* | renders | **nicely**
1 | 2 | 3

Tables can also be 'headerless'
| col 3 is      | right-aligned | $1600 |
| col 2 is      | centered      |   $12 |
| zebra stripes | are neat      |    $1 |

Becomes

<p>Pretty tables are converted</p>
<table>
  <thead>
    <tr>
      <th>Tables</th>
      <th>Are</th>
      <th>Cool</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td align="left">col 3 is</td>
      <td align="center">right-aligned</td>
      <td align="right">$1600</td>
    </tr>
    <tr>
      <td align="left">col 2 is</td>
      <td align="center">centered</td>
      <td align="right">$12</td>
    </tr>
    <tr>
      <td align="left">zebra stripes</td>
      <td align="center">are neat</td>
      <td align="right">$1</td>
    </tr>
  </tbody>
</table>
<p>As are dirty tables (the external pipes '|' are optional)</p>
<table>
  <thead>
    <tr>
      <th>Markdown</th>
      <th>Less</th>
      <th>Pretty</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td align="left">*Still*</td>
      <td align="left">renders</td>
      <td align="left"><b>nicely</b></td>
    </tr>
    <tr>
      <td align="left">1</td>
      <td align="left">2</td>
      <td align="left">3</td>
    </tr>
  </tbody>
</table>
<p>Tables can also be 'headerless'</p>
<table>
  <tbody>
    <tr>
      <td align="left">col 3 is</td>
      <td align="left">right-aligned</td>
      <td align="left">$1600</td>
    </tr>
    <tr>
      <td align="left">col 2 is</td>
      <td align="left">centered</td>
      <td align="left">$12</td>
    </tr>
    <tr>
      <td align="left">zebra stripes</td>
      <td align="left">are neat</td>
      <td align="left">$1</td>
    </tr>
  </tbody>
</table>

Block Quotes

> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.

> This is a seperate block quote. You can __put__ **Markdown** into a blockquote. Oh, and block quotes 'should' auto wrap

Becomes

<blockquote>
  <p>Blockquotes are very handy in email to emulate reply text.</p>
  <p>This line is part of the same quote.</p>
</blockquote>
<blockquote>
  <p>This is a seperate block quote. You can <u>put</u> <b>Markdown</b> into a blockquote. Oh, and block quotes 'should' auto wrap</p>
</blockquote>

Code Blocks

Pretty tables are converted
| Tables        | Are           | Cool  |
| ------------- |:-------------:| -----:|
| col 3 is      | right-aligned | $1600 |
| col 2 is      | centered      |   $12 |
| zebra stripes | are neat      |    $1 |

ˋˋˋmarkdown
As are dirty tables (the external pipes '|' are optional)
Markdown | Less | Pretty
--- | --- | ---
*Still* | renders | **nicely**
1 | 2 | 3
ˋˋˋ

Tables can also be 'headerless'
| col 3 is      | right-aligned | $1600 |
| col 2 is      | centered      |   $12 |
| zebra stripes | are neat      |    $1 |

Becomes

<p>Pretty tables are converted</p>
<table><thead><tr><th>Tables</th><th>Are</th><th>Cool</th></tr></thead><tbody><tr><td align="left">col 3 is</td><td align="center">right-aligned</td><td align="right">$1600</td></tr><tr><td align="left">col 2 is</td><td align="center">centered</td><td align="right">$12</td></tr><tr><td align="left">zebra stripes</td><td align="center">are neat</td><td align="right">$1</td></tr></tbody></table>

<code language="markdown">
As are dirty tables (the external pipes '|' are optional)
Markdown | Less | Pretty
--- | --- | ---
*Still* | renders | **nicely**
1 | 2 | 3
</code>

<p>Tables can also be 'headerless'</p>
<table><tbody><tr><td align="left">col 3 is</td><td align="left">right-aligned</td><td align="left">$1600</td></tr><tr><td align="left">col 2 is</td><td align="left">centered</td><td align="left">$12</td></tr><tr><td align="left">zebra stripes</td><td align="left">are neat</td><td align="left">$1</td></tr></tbody></table>
function toMarkup(string, options = {}) {
options = Object.assign({
codeBlock: true,
blockQuote: true,
tables: true,
decoration: true,
rules: true,
lists: true,
images: true,
links: true,
}, options);
function bullets(string) {
var bull = "(?:(?:[-+*])|(?:[0-9]+\\.))";
var listReg = new RegExp("( *)" + bull + " (?:.*)(?:\n? *\\1" + bull + " (?:.*))*", "gm");
while (string.match(new RegExp("^(?: *)" + bull + " (.*)$", "gm"))) {
string = string.replace(listReg, function (original, spaces, start) {
var len = spaces.length - spaces.length%2;
var liReg = new RegExp("^(?: {"+(len == 0 ? 0 : len -1)+","+(len+1)+"})[-+*] (.*)$", "gm");
var nuReg = new RegExp("^(?: {"+(len == 0 ? 0 : len -1)+","+(len+1)+"})[0-9]+\\. (.*)$", "gm");
var first = original.match(/^ *(\*|[0-9]+\.)/g);
var lTag = "ul";
original = original.replace(liReg, "<li>$1</li>");
original = original.replace(nuReg, "<li>$1</li>");
if (first[0].match(/ *[0-9]+\./))
lTag = "ol";
return "<"+lTag+">\n"+original+"\n</"+lTag+">";
});
}
return string;
}
function tables(raw) {
Object.entries(raw.split(/\r?\n|\r/g).map(l => (l.match(/\|/g)||[]).length))
.filter(e => e[1]).map(([l,c]) => [parseInt(l, 10), c])
.reduce((s, c) => ((
s.length && c[1] === s[s.length - 1][0][1] &&
(c[0] - 1) === s[s.length - 1][s[s.length - 1].length - 1][0]
) ? s[s.length - 1].push(c) : s.push([c]), s), [])
.filter(e => e.length - 1)
.map(e => {
let t = raw.split(/\r?\n|\r/g).slice(e[0][0], e[e.length - 1][0] + 1).map(l => l.replace(/ *\| */g, '|'));
if (t.every(l => l.startsWith('|'))) t = t.map(l => l.slice(1))
if (t.every(l => l.endsWith('|'))) t = t.map(l => l.slice(0, -1))
const styleIndex = t.findIndex(l => /^[|:-]+$/.test(l));
if (styleIndex > 1) return; // Invalid table.
const header = styleIndex == 1 ? t.shift().split('|') : null;
const alignment = ~styleIndex
? t.shift().split('|').map(l => l[0] === ':' && l.slice(-1) === ':' ? 'center' : l.slice(-1) === ':' ? 'right' : 'left')
: new Array(t[0].split('|').length).fill('left');
const body = t.map(l => l.split('|'));
return {header, body, alignment, start: e[0][0], end: e[e.length - 1][0] + 1}
})
.filter(e => e).reverse()
.forEach(table => {
string = '<table>';
if (table.header) string += '<thead><tr><th>' + table.header.join('</th><th>') + '</th></tr></thead>';
string += '<tbody>';
string += table.body.map(r => '<tr>' + r.map((c, i) => '<td align="' + table.alignment[i] + '">' + c + '</td>').join('') + '</tr>').join('')
string += '</tbody></table>';
temp = raw.split(/\r?\n|\r/g);
temp.splice(table.start, table.end - table.start, string);
raw = temp.join('\r\n');
})
return raw;
}
const codeBlock = /(^```[a-z]*$\n[\s\S]*?\n^```$)/gm;
return string
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.split(codeBlock)
.map(block => {
// Code blocks
if (options.codeBlock && codeBlock.test(block)) {
return block.replace(/^```([a-z]*)$\n([\s\S]*?)\n^```$/gm, '<code language="$1">\n$2\n</code>');
}
// Block Quotes
if (options.blockQuote)
block = block.replace(
/((?:^&gt;.*(?:\r?\n|\n)?)+)/gm,
match => '<blockquote>\n' + match.replace(/^&gt; */gm, '').trim() + '\n</blockquote>'
);
// Headers
block = block.replace(/^######\s*(.+?)\s*(?:\.\.(.+)\.\.)?$/gm, (_,h,s) => "<h6>" + h + (s ? " <small>" + s + "</small>" : "") + "</h6>");
block = block.replace(/^#####\s*(.+?)\s*(?:\.\.(.+)\.\.)?$/gm, (_,h,s) => "<h5>" + h + (s ? " <small>" + s + "</small>" : "") + "</h5>");
block = block.replace(/^####\s*(.+?)\s*(?:\.\.(.+)\.\.)?$/gm, (_,h,s) => "<h4>" + h + (s ? " <small>" + s + "</small>" : "") + "</h4>");
block = block.replace(/^###\s*(.+?)\s*(?:\.\.(.+)\.\.)?$/gm, (_,h,s) => "<h3>" + h + (s ? " <small>" + s + "</small>" : "") + "</h3>");
block = block.replace(/^##\s*(.+?)\s*(?:\.\.(.+)\.\.)?$/gm, (_,h,s) => "<h2>" + h + (s ? " <small>" + s + "</small>" : "") + "</h2>");
block = block.replace(/^#\s*(.+?)\s*(?:\.\.(.+)\.\.)?$/gm, (_,h,s) => "<h1>" + h + (s ? " <small>" + s + "</small>" : "") + "</h1>");
// Tables
if (options.tables)
block = tables(block);
// Text decoration
if (options.decoration) {
block = block.replace(/\*\*(.*?)\*\*/g, (m, b) => /\w/.test(b) ? "<b>" + b + "</b>" : m);
block = block.replace(/__(.*?)__/g, (m, b) => /\w/.test(b) ? "<u>" + b + "</u>" : m);
block = block.replace(/~~(.*?)~~/g, (m, b) => /\w/.test(b) ? "<i>" + b + "</i>" : m);
block = block.replace(/--(.*?)--/g, (m, b) => /\w/.test(b) ? "<s>" + b + "</s>" : m);
}
// Horizontal Rule
if (options.rules) {
block = block.replace(/^(?:-{3,}|\*{3,}|_{3,})$/gm, '<hr />');
}
// Bullet points
if (options.lists) {
block = bullets (block);
}
// Images
if (options.images) {
block = block.replace(/!\[(?!\\\])([^\]\[]+)\]\((?:(?!\\\))([^()]+)) \"(?!\\\")([^"]+)\"?\)/g, "<img title=\"$3\" src=\"$2\" alt=\"$1\"></img>");
block = block.replace(/!\[(?!\\\])([^\]\[]+)\]\((?:(?!\\\))([^()"']+))\)/g, "<img src=\"$2\" alt=\"$1\"></img>");
}
// Links
if (options.links) {
block = block.replace(/\[(?!\\\])([^\]\[]+)\]\((?:(?!\\\))([^()]+)) \"(?!\\\")([^"]+)\"?\)/g, "<a title=\"$3\" href=\"$2\">$1</a>");
block = block.replace(/\[(?!\\\])([^\]\[]+)\]\((?:(?!\\\))([^()"']+))\)/g, "<a href=\"$2\">$1</a>");
}
// New lines
block = block.replace(
/(?:^[ \t]*(?:(?!<).*|<[buis].*)(?:\r?\n|\r\n)?)+/gm,
m => '<p>' + m.trim().replace(/(?:\r?\n|\r\n)/g, "<br />") + '</p>'
);
return block
})
.join('');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment