0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 18:32:41 +00:00
Files
homebrewery/shared/naturalcrit/markdownLegacy.js
2023-06-17 20:25:15 +12:00

174 lines
4.4 KiB
JavaScript

const _ = require('lodash');
const Markdown = require('markedLegacy');
const renderer = new Markdown.Renderer();
//Processes the markdown within an HTML block if it's just a class-wrapper
renderer.html = function (html) {
if(_.startsWith(_.trim(html), '<div') && _.endsWith(_.trim(html), '</div>')){
const openTag = html.substring(0, html.indexOf('>')+1);
html = html.substring(html.indexOf('>')+1);
html = html.substring(0, html.lastIndexOf('</div>'));
return `${openTag} ${Markdown(html)} </div>`;
}
// if(_.startsWith(_.trim(html), '<style') && _.endsWith(_.trim(html), '</style>')){
// const openTag = html.substring(0, html.indexOf('>')+1);
// html = html.substring(html.indexOf('>')+1);
// html = html.substring(0, html.lastIndexOf('</style>'));
// html = html.replaceAll(/\s(\.[^{]*)/gm, '.legacy $1');
// return `${openTag} ${html} </style>`;
// }
return html;
};
renderer.link = function (href, title, text) {
let self = false;
if(href[0] == '#') {
self = true;
}
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
if(href === null) {
return text;
}
let out = `<a href="${escape(href)}"`;
if(title) {
out += ` title="${title}"`;
}
if(self) {
out += ' target="_self"';
}
out += `>${text}</a>`;
return out;
};
const nonWordAndColonTest = /[^\w:]/g;
const cleanUrl = function (sanitize, base, href) {
if(sanitize) {
let prot;
try {
prot = decodeURIComponent(unescape(href))
.replace(nonWordAndColonTest, '')
.toLowerCase();
} catch (e) {
return null;
}
if(prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
return null;
}
}
try {
href = encodeURI(href).replace(/%25/g, '%');
} catch (e) {
return null;
}
return href;
};
const escapeTest = /[&<>"']/;
const escapeReplace = /[&<>"']/g;
const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
const escapeReplacements = {
'&' : '&amp;',
'<' : '&lt;',
'>' : '&gt;',
'"' : '&quot;',
'\'' : '&#39;'
};
const getEscapeReplacement = (ch)=>escapeReplacements[ch];
const escape = function (html, encode) {
if(encode) {
if(escapeTest.test(html)) {
return html.replace(escapeReplace, getEscapeReplacement);
}
} else {
if(escapeTestNoEncode.test(html)) {
return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
}
}
return html;
};
const tagTypes = ['div', 'span', 'a'];
const tagRegex = new RegExp(`(${
_.map(tagTypes, (type)=>{
return `\\<${type}\\b|\\</${type}>`;
}).join('|')})`, 'g');
// Special "void" tags that can be self-closed but don't need to be.
const voidTags = new Set([
'area', 'base', 'br', 'col', 'command', 'hr', 'img',
'input', 'keygen', 'link', 'meta', 'param', 'source'
]);
module.exports = {
marked : Markdown,
render : (rawBrewText)=>{
return Markdown(
rawBrewText,
{ renderer: renderer }
);
},
validate : (rawBrewText)=>{
const errors = [];
const leftovers = _.reduce(rawBrewText.split('\n'), (acc, line, _lineNumber)=>{
const lineNumber = _lineNumber + 1;
const matches = line.match(tagRegex);
if(!matches || !matches.length) return acc;
_.each(matches, (match)=>{
_.each(tagTypes, (type)=>{
if(match == `<${type}`){
acc.push({
type : type,
line : lineNumber
});
}
if(match === `</${type}>`){
// Closing tag: Check we expect it to be closed.
// The accumulator may contain a sequence of voidable opening tags,
// over which we skip before checking validity of the close.
while (acc.length && voidTags.has(_.last(acc).type) && _.last(acc).type != type) {
acc.pop();
}
// Now check that what remains in the accumulator is valid.
if(!acc.length){
errors.push({
line : lineNumber,
type : type,
text : 'Unmatched closing tag',
id : 'CLOSE'
});
} else if(_.last(acc).type == type){
acc.pop();
} else {
errors.push({
line : `${_.last(acc).line} to ${lineNumber}`,
type : type,
text : 'Type mismatch on closing tag',
id : 'MISMATCH'
});
acc.pop();
}
}
});
});
return acc;
}, []);
_.each(leftovers, (unmatched)=>{
errors.push({
line : unmatched.line,
type : unmatched.type,
text : 'Unmatched opening tag',
id : 'OPEN'
});
});
return errors;
},
};