mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-29 17:42:38 +00:00
Merge branch 'master' of https://github.com/naturalcrit/homebrewery into experimental-development
This commit is contained in:
@@ -35,6 +35,7 @@ const printCurrentBrew = ()=>{
|
||||
};
|
||||
|
||||
const fetchThemeBundle = async (obj, renderer, theme)=>{
|
||||
if(!renderer || !theme) return;
|
||||
const res = await request
|
||||
.get(`/api/theme/${renderer}/${theme}`)
|
||||
.catch((err)=>{
|
||||
|
||||
@@ -3,7 +3,7 @@ const _ = require('lodash');
|
||||
const Marked = require('marked');
|
||||
const MarkedExtendedTables = require('marked-extended-tables');
|
||||
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite');
|
||||
const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id');
|
||||
const { gfmHeadingId: MarkedGFMHeadingId, resetHeadings: MarkedGFMResetHeadingIDs } = require('marked-gfm-heading-id');
|
||||
const { markedEmoji: MarkedEmojis } = require('marked-emoji');
|
||||
|
||||
//Icon fonts included so they can appear in emoji autosuggest dropdown
|
||||
@@ -86,7 +86,7 @@ renderer.link = function (href, title, text) {
|
||||
if(href[0] == '#') {
|
||||
self = true;
|
||||
}
|
||||
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
href = cleanUrl(href);
|
||||
|
||||
if(href === null) {
|
||||
return text;
|
||||
@@ -102,6 +102,20 @@ renderer.link = function (href, title, text) {
|
||||
return out;
|
||||
};
|
||||
|
||||
// Expose `src` attribute as `--HB_src` to make the URL accessible via CSS
|
||||
renderer.image = function (href, title, text) {
|
||||
href = cleanUrl(href);
|
||||
if (href === null)
|
||||
return text;
|
||||
|
||||
let out = `<img src="${href}" alt="${text}" style="--HB_src:url(${href});"`;
|
||||
if (title)
|
||||
out += ` title="${title}"`;
|
||||
|
||||
out += '>';
|
||||
return out;
|
||||
}
|
||||
|
||||
// Disable default reflink behavior, as it steps on our variables extension
|
||||
tokenizer.def = function () {
|
||||
return undefined;
|
||||
@@ -356,6 +370,27 @@ const superSubScripts = {
|
||||
}
|
||||
};
|
||||
|
||||
const forcedParagraphBreaks = {
|
||||
name : 'hardBreaks',
|
||||
level : 'block',
|
||||
start(src) { return src.match(/\n:+$/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const regex = /^(:+)(?:\n|$)/ym;
|
||||
const match = regex.exec(src);
|
||||
if(match?.length) {
|
||||
return {
|
||||
type : 'hardBreaks', // Should match "name" above
|
||||
raw : match[0], // Text to consume from the source
|
||||
length : match[1].length,
|
||||
text : ''
|
||||
};
|
||||
}
|
||||
},
|
||||
renderer(token) {
|
||||
return `<div class='blank'></div>`.repeat(token.length).concat('\n');
|
||||
}
|
||||
};
|
||||
|
||||
const definitionListsSingleLine = {
|
||||
name : 'definitionListsSingleLine',
|
||||
level : 'block',
|
||||
@@ -400,9 +435,9 @@ const definitionListsSingleLine = {
|
||||
const definitionListsMultiLine = {
|
||||
name : 'definitionListsMultiLine',
|
||||
level : 'block',
|
||||
start(src) { return src.match(/\n[^\n]*\n::/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
start(src) { return src.match(/\n[^\n]*\n::[^:\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::))|\n::(.(?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
|
||||
const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::[^:\n]))|\n::([^:\n](?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
|
||||
let match;
|
||||
let endIndex = 0;
|
||||
const definitions = [];
|
||||
@@ -706,34 +741,27 @@ const MarkedEmojiOptions = {
|
||||
renderer : (token)=>`<i class="${token.emoji}"></i>`
|
||||
};
|
||||
|
||||
const tableTerminators = [
|
||||
`:+\\n`, // hardBreak
|
||||
` *{[^\n]+}`, // blockInjector
|
||||
` *{{[^{\n]*\n.*?\n}}` // mustacheDiv
|
||||
]
|
||||
|
||||
Marked.use(MarkedVariables());
|
||||
Marked.use({ extensions: [definitionListsMultiLine, definitionListsSingleLine, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] });
|
||||
Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts,
|
||||
mustacheSpans, mustacheDivs, mustacheInjectInline] });
|
||||
Marked.use(mustacheInjectBlock);
|
||||
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
||||
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
|
||||
Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
|
||||
|
||||
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;
|
||||
};
|
||||
function cleanUrl(href) {
|
||||
try {
|
||||
href = encodeURI(href).replace(/%25/g, '%');
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return href;
|
||||
}
|
||||
|
||||
const escapeTest = /[&<>"']/;
|
||||
const escapeReplace = /[&<>"']/g;
|
||||
@@ -828,13 +856,15 @@ let globalPageNumber = 0;
|
||||
|
||||
module.exports = {
|
||||
marked : Marked,
|
||||
render : (rawBrewText, pageNumber=1)=>{
|
||||
globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
|
||||
varsQueue = []; //Could move into MarkedVariables()
|
||||
globalPageNumber = pageNumber;
|
||||
render : (rawBrewText, pageNumber=0)=>{
|
||||
globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
|
||||
varsQueue = []; //Could move into MarkedVariables()
|
||||
globalPageNumber = pageNumber;
|
||||
if(pageNumber==0) {
|
||||
MarkedGFMResetHeadingIDs();
|
||||
}
|
||||
|
||||
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
||||
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
||||
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`);
|
||||
const opts = Marked.defaults;
|
||||
|
||||
rawBrewText = opts.hooks.preprocess(rawBrewText);
|
||||
|
||||
Reference in New Issue
Block a user