src/umarkdown.js
/*
* micro-markdown.js
* markdown in under 5kb
*
* Copyright 2014, Simon Waldherr - http://simon.waldherr.eu/
* Released under the MIT Licence
* http://simon.waldherr.eu/license/mit/
*
* Github: https://github.com/simonwaldherr/micromarkdown.js/
* Version: 0.3.0
*/
/*jshint strict: true */
var micromarkdown = {
regexobject: {
headline: /^(\#{1,6})([^\#\n]+)$/m,
pre: /\s\`\`\`(\w+)\n?[^`]+\`\`\`/g,
code: /\s\`\`\`\n?([^`]+)\`\`\`/g,
hr: /^(?:([\*\-_] ?)+)\1\1$/gm,
lists: /^((\s*((\*|\-)|\d(\.|\))) [^\n]+)\n)+/gm,
bolditalic: /(?:([\*_~]{1,3}))([^\*_~\n]+[^\*_~\s])\1/g,
links: /!?\[([^\]<>]+)\]\(([^ \)<>]+)( "[^\(\)\"]+")?\)/g,
reflinks: /\[([^\]]+)\]\[([^\]]+)\]/g,
smlinks: /\@([a-z0-9]{3,})\@(t|gh|fb|gp|adn)/gi,
mail: /<(([a-z0-9_\-\.])+\@([a-z0-9_\-\.])+\.([a-z]{2,7}))>/gmi,
tables: /\n(([^|\n]+ *\| *)+([^|\n]+\n))((:?\-+:?\|)+(:?\-+:?)*\n)((([^|\n]+ *\| *)+([^|\n]+)\n)+)/g,
include: /[\[<]include (\S+) from (https?:\/\/[a-z0-9\.\-]+\.[a-z]{2,9}[a-z0-9\.\-\?\&\/]+)[\]>]/gi,
url: /<([a-zA-Z0-9@:%_\+.~#?&\/\/=]{2,256}\.[a-z]{2,4}\b(\/[\-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)?)>/g
},
codeHandler: function (stra, str) {
var pre = '', preClass;
if ((preClass = this.regexobject.pre.exec(stra)) !== null) {
pre = preClass[1];
}
return str.replace(stra[0], '<pre><code class="' + pre + '">\n' + micromarkdown.htmlEncode(stra[1]).replace(pre + '\n', '').replace(/\n/gm, '<br/>').replace(/\ /gm, ' ') + '</code></pre>\n');
},
headlineHandler: function (stra, str) {
var count = stra[1].length;
return str.replace(stra[0], '<h' + count + '>' + stra[2] + '</h' + count + '>' + '\n');
},
linksHandler: function (stra, str, strict) {
if (stra[0].substr(0, 1) === '!') {
str = str.replace(stra[0], '<img src="' + stra[2] + '" alt="' + stra[1] + '" title="' + stra[1] + '" />\n');
} else {
str = str.replace(stra[0], '<a ' + micromarkdown.mmdCSSclass(stra[2], strict) + 'href="' + stra[2] + '">' + stra[1] + '</a>\n');
}
return str;
},
mailHandler: function (stra, str) {
return str.replace(stra[0], '<a href="mailto:' + stra[1] + '">' + stra[1] + '</a>');
},
hrHandler: function (stra, str) {
return str.replace(stra[0], '\n<hr/>\n');
},
urlHandler: function (stra, str, strict) {
var replaceString = stra[1];
if (replaceString.indexOf('://') === -1) {
replaceString = 'http://' + replaceString;
}
return str.replace(stra[0], '<a ' + micromarkdown.mmdCSSclass(replaceString, strict) + 'href="' + replaceString + '">' + replaceString.replace(/(https:\/\/|http:\/\/|mailto:|ftp:\/\/)/gmi, '') + '</a>');
},
refLinksHandler: function (str, stra, helper, strict) {
return str.replace(stra[0], '<a ' + micromarkdown.mmdCSSclass(helper[1], strict) + 'href="' + helper[1] + '">' + stra[1] + '</a>');
},
smlinksHandler: function (stra, str, strict) {
var replaceStr = "";
switch (stra[2]) {
case 't':
replaceStr = 'https://twitter.com/' + stra[1];
break;
case 'gh':
replaceStr = 'https://github.com/' + stra[1];
break;
case 'fb':
replaceStr = 'https://www.facebook.com/' + stra[1];
break;
case 'gp':
replaceStr = 'https://plus.google.com/+' + stra[1];
break;
}
return str.replace(stra[0], '<a ' + micromarkdown.mmdCSSclass(replaceStr, strict) + 'href="' + replaceStr + '">' + stra[1] + '</a>');
},
boldItalicHandler: function (stra, str) {
var replaceStr = [];
if (stra[1] === '~~') {
str = str.replace(stra[0], '<del>' + stra[2] + '</del>');
} else {
switch (stra[1].length) {
case 1:
replaceStr = ['<i>', '</i>'];
break;
case 2:
replaceStr = ['<b>', '</b>'];
break;
case 3:
replaceStr = ['<i><b>', '</b></i>'];
break;
}
str = str.replace(stra[0], replaceStr[0] + stra[2] + replaceStr[1]);
}
return str;
},
tableHandlerHelper: function (helper, calign, strict) {
var i;
for (i = 0; i < helper.length; i++) {
if (calign.length <= i) {
calign.push(0);
} else if ((calign[i].trimRight().slice(-1) === ':') && (strict !== true)) {
if (calign[i][0] === ':') {
calign[i] = 3;
} else {
calign[i] = 2;
}
} else if (strict !== true) {
if (calign[i][0] === ':') {
calign[i] = 1;
} else {
calign[i] = 0;
}
} else {
calign[i] = 0;
}
}
},
tableHandler: function (helper1, i, calign, replaceStr, cel) {
var j, helper2;
helper2 = helper1[i].split('|');
if (helper2[0].length !== 0) {
while (calign.length < helper2.length) {
calign.push(0);
}
replaceStr += '<tr>';
for (j = 0; j < helper2.length; j++) {
replaceStr += cel[calign[j]] + helper2[j].trim() + '</td>';
}
replaceStr += '</tr>' + '\n';
}
return {helper2: helper2, replaceStr: replaceStr};
},
tablesHandler: function (stra, str, strict) {
var replaceStr, cel, helper, calign, helper1, helper2, i;
replaceStr = '<table><tr>';
helper = stra[1].split('|');
calign = stra[4].split('|');
this.tableHandlerHelper(helper, calign, strict);
cel = ['<th>', '<th align="left">', '<th align="right">', '<th align="center">'];
for (i = 0; i < helper.length; i++) {
replaceStr += cel[calign[i]] + helper[i].trim() + '</th>';
}
replaceStr += '</tr>';
cel = ['<td>', '<td align="left">', '<td align="right">', '<td align="center">'];
helper1 = stra[7].split('\n');
for (i = 0; i < helper1.length; i++) {
var result = this.tableHandler(helper1, i, calign, replaceStr, cel);
helper2 = result.helper2;
replaceStr = result.replaceStr;
}
replaceStr += '</table>';
return str.replace(stra[0], replaceStr);
},
listHandlerStart: function (stra, replaceStr) {
if ((stra[0].trim().substr(0, 1) === '*') || (stra[0].trim().substr(0, 1) === '-')) {
replaceStr = '<ul>';
} else {
replaceStr = '<ol>';
}
return replaceStr;
},
listsHandlerEnd: function (stra, replaceStr) {
if ((stra[0].trim().substr(0, 1) === '*') || (stra[0].trim().substr(0, 1) === '-')) {
replaceStr += '</ul>';
} else {
replaceStr += '</ol>';
}
return replaceStr;
},
listsHandlerSub: function (line, replaceStr, helper1) {
if ((line[0].trim().substr(0, 1) === '*') || (line[0].trim().substr(0, 1) === '-')) {
replaceStr += '<ul>';
helper1.push('</ul>');
} else {
replaceStr += '<ol>';
helper1.push('</ol>');
}
return replaceStr;
},
listHandler: function (line, nstatus, status, replaceStr, helper1, casca) {
var indent = false;
if ((line[2] === undefined) || (line[2].length === 0)) {
nstatus = 0;
} else {
if (indent === false) {
indent = line[2].replace(/\t/, ' ').length;
}
nstatus = Math.round(line[2].replace(/\t/, ' ').length / indent);
}
while (status > nstatus) {
replaceStr += helper1.pop();
status--;
casca--;
}
while (status < nstatus) {
replaceStr = this.listsHandlerSub(line, replaceStr, helper1);
status++;
casca++;
}
replaceStr += '<li>' + line[6] + '</li>' + '\n';
return {nstatus: nstatus, status: status, replaceStr: replaceStr, casca: casca};
},
listsHandler: function (stra, str) {
var helper, helper1 = [], status = 0, line, nstatus, replaceStr, i, casca = 0;
replaceStr = this.listHandlerStart(stra, replaceStr);
helper = stra[0].split('\n');
for (i = 0; i < helper.length; i++) {
if ((line = /^((\s*)((\*|\-)|\d(\.|\))) ([^\n]+))/.exec(helper[i])) !== null) {
var result = this.listHandler(line, nstatus, status, replaceStr, helper1, casca);
nstatus = result.nstatus;
status = result.status;
replaceStr = result.replaceStr;
casca = result.casca;
}
}
while (casca > 0) {
replaceStr += '</ul>';
casca--;
}
replaceStr = this.listsHandlerEnd(stra, replaceStr);
return str.replace(stra[0], replaceStr + '\n');
},
listStrict: function (strict, regexobject) {
if (strict !== true) {
regexobject.lists = /^((\s*(\*|\d\.) [^\n]+)\n)+/gm;
}
return regexobject.lists;
},
removeRefLinks: function (trashgc, str) {
var i;
for (i = 0; i < trashgc.length; i++) {
str = str.replace(trashgc[i], '');
}
return str;
},
parse: function (str, strict) {
var helper, helper1, stra, trashgc = [], i, that = this, regexObject = micromarkdown.regexobject;
regexObject.lists = this.listStrict(strict, regexObject);
str = '\n' + str + '\n';
['code', 'headline', 'lists', 'tables', 'links', 'mail', 'url', 'smlinks', 'hr'].forEach(function (type) {
while ((stra = regexObject[type].exec(str)) !== null) {
str = that[(type + 'Handler')].apply(that, [stra, str, strict]);
}
});
for (i = 0; i < 3; i++) {
while ((stra = regexObject.bolditalic.exec(str)) !== null) {
str = this.boldItalicHandler(stra, str);
}
}
while ((stra = regexObject.reflinks.exec(str)) !== null) {
helper1 = new RegExp('\\[' + stra[2] + '\\]: ?([^ \n]+)', "gi");
if ((helper = helper1.exec(str)) !== null) {
str = this.refLinksHandler(str, stra, helper, strict);
trashgc.push(helper[0]);
}
}
str = this.removeRefLinks(trashgc, str);
return str.replace(/ {2,}[\n]{1,}/gmi, '<br/><br/>');
},
htmlEncode: function (str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
},
mmdCSSclass: function (str, strict) {
var urlTemp;
if ((str.indexOf('/') !== -1) && (strict !== true)) {
urlTemp = str.split('/');
if (urlTemp[1].length === 0) {
urlTemp = urlTemp[2].split('.');
} else {
urlTemp = urlTemp[0].split('.');
}
return 'class="mmd_' + urlTemp[urlTemp.length - 2].replace(/[^\w\d]/g, '') + urlTemp[urlTemp.length - 1] + '" ';
}
return '';
}
};
EchoesWorks.md = micromarkdown;