Mini-Markdown Alternate Version
const tagRules = {
b: '\\*\\*',
i: '_',
strike: '~~',
code: '`'
};
tagRules.keys = Object.keys(tagRules);
const toTag = (str, delim, tag) => str.replace(
new RegExp(
`${delim}(.+)${delim}`, 'gm'),`<${tag}>$1</${tag}>`)
const makeTags = str =>
tagRules.keys.reduce((accum, val, i) =>
toTag(accum, tagRules[val], val), str)
const markConfig = {
emptyLine: {
check: val => val.trim() === '',
process: () => '<br>'
},
header: {
calc: val => val.split`#`.length - 1,
check: (val, result) => result > 0,
process: (val, result) => `<h${result} style="margin-bottom:0">
${val.replace(/#/g, '')}</h${result}>`
},
hr: {
check: val => val.trim() === '***',
process: () => '<hr>'
},
li: {
check: val => val.startsWith('- '),
process: (val, result, lastMethod) => {
const open = lastMethod != 'li' ? '<ul>' : '';
return `${open}<li>${val}</li>`;
}
}
};
markConfig.keys = Object.keys(markConfig);
markConfig.tail = {
process: (val, result, lastMethod) => {
const close = lastMethod == 'li' ? '</ul>' : '';
return val + close;
}
};
const mark = str => {
const lines = str.split`\n`;
let lastMethod;
const newStr = lines.map((line, i) => {
const method = markConfig.keys.find(key => {
const { calc = () => {}, check } = markConfig[key];
return check(line, calc(line))
}) || 'tail'
const { calc = () => {} } = markConfig[method];
const newLine = markConfig[method].process(line, calc(line), lastMethod);
lastMethod = method;
return newLine;
})
return newStr.join`\n`;
}
// try it out
const md = document.body.appendChild(document.createElement('div'));
md.style.fontFamily = 'sans-serif';
md.innerHTML = `# Mini Markdown Subset
This is a subset of markdown stuff
## It includes
- headers
- **bold styles**
- _italic styles, <br> multiline fine..._
- \`code style\`
Other than ~~strikethrough~~ that is pretty much it... oh and **hr** tags
***
***
_here is some italic text with some bold text **nested** <br>within it etc..._
`;
md.innerHTML = makeTags(mark(md.innerHTML));
This is another version of a post from awhile back. I was curious about readability. This has no for
loops and no if
statements. Personally I find it less readable than the other version…
Here is the simpler original version for comparison… this one also has nested list support.
const isLi = val => val.match(/(^\s+)?- /)
function mark(str) {
const lines = str.split`\n`;
let wasLi = false
let lastDepth = 0;
let depth;
for (let i = 0; i < lines.length; i++) {
let curr = lines[i];
const line = curr.trim();
const hdr = line.split`#`.length - 1;
if (line === '') {
lines[i] = '<br>';
} else if (hdr > 0) {
lines[i] = `<h${hdr} style="margin-bottom:0">
${curr.replace(/#/g, '')}</h${hdr}>`;
} else if (line === '***') {
lines[i] = '<hr>';
} else if (isLi(curr)) {
depth = curr.split('-')[0].length + 1;
lines[i] = '';
if (depth < lastDepth) {
const diff = (lastDepth - depth) / 2
lines[i] += '</ul>'.repeat(diff);
lastDepth = depth
}
lines[i] += `${depth > lastDepth ? '<ul>' : ''}
<li>${curr.replace(/-\s+/, '')}</li>`;
lastDepth = depth;
wasLi = true;
} else if (wasLi) {
lines[i - 1] = '</ul>\n'.repeat(lastDepth)
wasLi = false
}
}
return lines.join`\n`
.replace(/\*\*(.+)\*\*/gm, '<b>$1</b>')
.replace(/_(.+)_/gm, '<i>$1</i>')
.replace(/~~(.+)~~/gm, '<strike>$1</strike>')
.replace(/`(.+)`/gm, '<code>$1</code>');
}
// try it out
const md = document.body.appendChild(document.createElement('div'));
md.style.fontFamily = 'sans-serif';
md.innerHTML = `
#Snippet Zone
This snippet renders a subset of markdown.
- **bold** and _italic text_
- lists
- this particulate
- snippet can render
- nested
- lists
- _**~~strikethrough text~~**_
In a real project you will probably want to use
something more complete like the great <a href="https://github.com/markedjs/marked" target="blank" rel="noopener">marked.js</a> library.
`;
md.innerHTML = mark(md.innerHTML);