Skip to content

Instantly share code, notes, and snippets.

@plugnburn
Last active February 7, 2023 17:12
Show Gist options
  • Save plugnburn/f0d12e38b6416a77c098 to your computer and use it in GitHub Desktop.
Save plugnburn/f0d12e38b6416a77c098 to your computer and use it in GitHub Desktop.
Landmark - a minimalistic and extensible Markdown compiler in JavaScript

Landmark: the simplest Markdown engine for the browser

Landmark is a small but extensible JavaScript library that allows to render Markdown documents into HTML. It's the primary engine for Sitemark and some other projects.

Usage

Landmark features only 2 methods:

  • Landmark.render(text) - takes the Markdown source and returns ready HTML code (you'll only need this most of the time)
  • Landmark.addRule(regexp, replacement) - adds a new processing rule that allows you to specify custom Markdown tags: regexp must be an escaped regular expression string in JavaScript syntax, and replacement can be either a string, or a function (see string replacement documentation for the reference)

Markdown syntax

Markdown syntax used in Landmark resembles both classical and GitHub-flavored Markdown versions, however it does not support some advanced features such as tables or reference-style links. Supported tags are:

  • Headings, single-line form: # Heading level 1, ## Heading level 2 and so on
  • Headings of levels 1 and 2, two-line form:
    Heading level 1
    ===============
    
    Heading level 2
    ---------------
    
  • Inline formatting: **bold** or __bold__, *italic* or _italic_, ~~strike~~, :"quote": and even non-standard ___underline___ (triple underscore) tag!
  • Preformatted inline blocks:`inline code`
  • Preformatted multiline blocks:
    ```
    first line of preformatted text
    second line of preformatted text
    etc...
    ```
  • Block quotes:
    > first line of quote
    > second line of quote
    > etc...
  • Lists:
    - unordered item 1
    - unordered item 2
    
    * another unordered item 1
    * another unordered item 2
    * another unordered item 3
    
    1. ordered item 1
    2. ordered item 2
    3. ordered item 3
    4. ordered item 4
  • Links: [link text](http://example.com)
  • Images: ![alt text](http://example.com/flower.jpg)
  • Horizontal rules: **************** (5 and more asterisks on a new line)
  • Paragraphs: just like in standard Markdown, any non-special text is enclosed into paragraphs by default, just surround it with newlines.

Of course, you can use plain old HTML in Markdown documents, and it will pass as is, unless it's enclosed into inline or multiline preformatted blocks, in which case it will be escaped appropriately.

(function(w, libName){
var esc=function(s){
s = s.replace(/\&/g, '&')
var escChars = '\'#<>`*-~_=:"![]()nt',c,l=escChars.length,i
for(i=0;i<l;i++) s=s.replace(RegExp('\\'+escChars[i], 'g'), function(m){return'&#'+m.charCodeAt(0)+';'})
return s
}, rules = [
{p:/\r\n/g, r:'\n'},
{p:/\n\s*```\n([^]*?)\n\s*```\s*\n/g, r:function(m,grp){return'<pre>'+esc(grp)+'</pre>'}},
{p:/`(.*?)`/g, r:function(m,grp){return'<code>'+esc(grp)+'</code>'}},
{p:/\n\s*(#+)(.*?)/g, r:function(m,hset,hval){m=hset.length;return'<h'+m+'>'+hval.trim()+'</h'+m+'>'}},
{p:/\n\s*(.*?)\n={3,}\n/g, r:'\n<h1>$1</h1>\n'},
{p:/\n\s*(.*?)\n-{3,}\n/g, r:'\n<h2>$1</h2>\n'},
{p:/___(.*?)___/g, r:'<u>$1</u>'},
{p:/(\*\*|__)(.*?)\1/g, r:'<strong>$2</strong>'},
{p:/(\*|_)(.*?)\1/g, r:'<em>$2</em>'},
{p:/~~(.*?)~~/g, r:'<del>$1</del>'},
{p:/:"(.*?)":/g, r:'<q>$1</q>'},
{p:/\!\[([^\[]+?)\]\s*\(([^\)]+?)\)/g, r:'<img src="$2" alt="$1">'},
{p:/\[([^\[]+?)\]\s*\(([^\)]+?)\)/g, r:'<a href="$2">$1</a>'},
{p:/\n\s*(\*|\-)\s*([^\n]*)/g, r:'\n<ul><li>$2</li></ul>'},
{p:/\n\s*\d+\.\s*([^\n]*)/g, r:'\n<ol><li>$1</li></ol>'},
{p:/\n\s*(\>|&gt;)\s*([^\n]*)/g, r:'\n<blockquote>$2</blockquote>'},
{p:/<\/(ul|ol|blockquote)>\s*<\1>/g, r: ' '},
{p:/\n\s*\*{5,}\s*\n/g, r:'\n<hr>'},
{p:/\n{3,}/g, r:'\n\n'},
{p:/\n([^\n]+)\n/g, r:function(m, grp){grp=grp.trim();return /^\<\/?(ul|ol|bl|h\d|p).*/.test(grp.slice(0,9)) ? grp : ('<p>'+grp+'</p>')}},
{p:/>\s+</g, r:'><'}
], l = rules.length, i
w[libName] = {
addRule:function(ruleString, replacement) {rules.push({p:RegExp(ruleString, 'g'),r:replacement})},
render:function(text) {
if(text = text || '') {
text = '\n' + text.trim() + '\n'
for(var i=0;i<l;i++) text = text.replace(rules[i].p, rules[i].r)
}
return text
}
}
})(self, 'Landmark')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment