0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-02 02:02:43 +00:00

Merge branch 'master' into marked-definition-lists

This commit is contained in:
David Bolack
2025-04-08 21:43:14 -05:00
81 changed files with 2698 additions and 2770 deletions

View File

@@ -9,6 +9,8 @@ import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHead
import { markedEmoji as MarkedEmojis } from 'marked-emoji';
import MarkedDefinitionLists from 'marked-definition-lists';
import MarkedSubSuperText from 'marked-subsuper-text';
import { romanize } from 'romans';
import writtenNumber from 'written-number';
//Icon fonts included so they can appear in emoji autosuggest dropdown
import diceFont from '../../themes/fonts/iconFonts/diceFont.js';
@@ -60,9 +62,57 @@ mathParser.functions.signed = function (a) {
if(a >= 0) return `+${a}`;
return `${a}`;
};
// Add Roman numeral functions
mathParser.functions.toRomans = function (a) {
return romanize(a);
};
mathParser.functions.toRomansUpper = function (a) {
return romanize(a).toUpperCase();
};
mathParser.functions.toRomansLower = function (a) {
return romanize(a).toLowerCase();
};
// Add character functions
mathParser.functions.toChar = function (a) {
if(a <= 0) return a;
const genChars = function (i) {
return (i > 26 ? genChars(Math.floor((i - 1) / 26)) : '') + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[(i - 1) % 26];
};
return genChars(a);
};
mathParser.functions.toCharUpper = function (a) {
return mathParser.functions.toChar(a).toUpperCase();
};
mathParser.functions.toCharLower = function (a) {
return mathParser.functions.toChar(a).toLowerCase();
};
// Add word functions
mathParser.functions.toWords = function (a) {
return writtenNumber(a);
};
mathParser.functions.toWordsUpper = function (a) {
return mathParser.functions.toWords(a).toUpperCase();
};
mathParser.functions.toWordsLower = function (a) {
return mathParser.functions.toWords(a).toLowerCase();
};
mathParser.functions.toWordsCaps = function (a) {
const words = mathParser.functions.toWords(a).split(' ');
return words.map((word)=>{
return word.replace(/(?:^|\b|\s)(\w)/g, function(w, index) {
return index === 0 ? w.toLowerCase() : w.toUpperCase();
});
}).join(' ');
};
// Normalize variable names; trim edge spaces and shorten blocks of whitespace to 1 space
const normalizeVarNames = (label)=>{
return label.trim().replace(/\s+/g, ' ');
};
//Processes the markdown within an HTML block if it's just a class-wrapper
renderer.html = function (html) {
renderer.html = function (token) {
let html = token.text;
if(_.startsWith(_.trim(html), '<div') && _.endsWith(_.trim(html), '</div>')){
const openTag = html.substring(0, html.indexOf('>')+1);
html = html.substring(html.indexOf('>')+1);
@@ -73,18 +123,21 @@ renderer.html = function (html) {
};
// Don't wrap {{ Spans alone on a line, or {{ Divs in <p> tags
renderer.paragraph = function(text){
renderer.paragraph = function(token){
let match;
const text = this.parser.parseInline(token.tokens);
if(text.startsWith('<div') || text.startsWith('</div'))
return `${text}`;
else if(match = text.match(/(^|^.*?\n)<span class="inline-block(.*?<\/span>)$/)) {
else if(match = text.match(/(^|^.*?\n)<span class="inline-block(.*?<\/span>)$/))
return `${match[1].trim() ? `<p>${match[1]}</p>` : ''}<span class="inline-block${match[2]}`;
} else
else
return `<p>${text}</p>\n`;
};
//Fix local links in the Preview iFrame to link inside the frame
renderer.link = function (href, title, text) {
renderer.link = function (token) {
let { href, title, tokens } = token;
const text = this.parser.parseInline(tokens);
let self = false;
if(href[0] == '#') {
self = true;
@@ -96,7 +149,7 @@ renderer.link = function (href, title, text) {
}
let out = `<a href="${escape(href)}"`;
if(title) {
out += ` title="${title}"`;
out += ` title="${escape(title)}"`;
}
if(self) {
out += ' target="_self"';
@@ -106,8 +159,8 @@ renderer.link = function (href, title, text) {
};
// Expose `src` attribute as `--HB_src` to make the URL accessible via CSS
renderer.image = function (href, title, text) {
href = cleanUrl(href);
renderer.image = function (token) {
const { href, title, text } = token;
if(href === null)
return text;
@@ -388,7 +441,7 @@ const forcedParagraphBreaks = {
}
},
renderer(token) {
return `<br>\n`.repeat(token.length);
return `<div class='blank'></div>\n`.repeat(token.length);
}
};
@@ -419,7 +472,7 @@ const replaceVar = function(input, hoist=false, allowUnresolved=false) {
const match = regex.exec(input);
const prefix = match[1];
const label = match[2];
const label = normalizeVarNames(match[2]); // Ensure the label name is normalized as it should be in the var stack.
//v=====--------------------< HANDLE MATH >-------------------=====v//
const mathRegex = /[a-z]+\(|[+\-*/^(),]/g;
@@ -574,8 +627,8 @@ function MarkedVariables() {
});
}
if(match[3]) { // Block Definition
const label = match[4] ? match[4].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
const content = match[5] ? match[5].trim().replace(/[ \t]+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
const label = match[4] ? normalizeVarNames(match[4]) : null;
const content = match[5] ? match[5].trim().replace(/[ \t]+/g, ' ') : null; // Normalize text content (except newlines for block-level content)
varsQueue.push(
{ type : 'varDefBlock',
@@ -584,7 +637,7 @@ function MarkedVariables() {
});
}
if(match[6]) { // Block Call
const label = match[7] ? match[7].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
const label = match[7] ? normalizeVarNames(match[7]) : null;
varsQueue.push(
{ type : 'varCallBlock',
@@ -593,7 +646,7 @@ function MarkedVariables() {
});
}
if(match[8]) { // Inline Definition
const label = match[10] ? match[10].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
const label = match[10] ? normalizeVarNames(match[10]) : null;
let content = match[11] || null;
// In case of nested (), find the correct matching end )
@@ -625,7 +678,7 @@ function MarkedVariables() {
});
}
if(match[12]) { // Inline Call
const label = match[13] ? match[13].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
const label = match[13] ? normalizeVarNames(match[13]) : null;
varsQueue.push(
{ type : 'varCallInline',
@@ -687,7 +740,7 @@ Marked.use({ extensions : [justifiedParagraphs, forcedParagraphBreaks,
Marked.use(mustacheInjectBlock);
Marked.use(MarkedSubSuperText());
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
Marked.use(MarkedExtendedTables({interruptPatterns : tableTerminators}), MarkedGFMHeadingId({ globalSlugs: true }),
Marked.use(MarkedExtendedTables({ interruptPatterns: tableTerminators }), MarkedGFMHeadingId({ globalSlugs: true }),
MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
function cleanUrl(href) {
@@ -752,12 +805,12 @@ const processStyleTags = (string)=>{
obj[key.trim()] = value.trim();
return obj;
}, {}) || null;
const styles = tags?.length ? tags.reduce((styleObj, style) => {
const index = style.indexOf(':');
const [key, value] = [style.substring(0, index), style.substring(index + 1)];
styleObj[key.trim()] = value.replace(/"?([^"]*)"?/g, '$1').trim();
return styleObj;
}, {}) : null;
const styles = tags?.length ? tags.reduce((styleObj, style)=>{
const index = style.indexOf(':');
const [key, value] = [style.substring(0, index), style.substring(index + 1)];
styleObj[key.trim()] = value.replace(/"?([^"]*)"?/g, '$1').trim();
return styleObj;
}, {}) : null;
return {
id : id,
@@ -773,8 +826,8 @@ const extractHTMLStyleTags = (htmlString)=>{
const id = firstElementOnly.match(/id="([^"]*)"/)?.[1] || null;
const classes = firstElementOnly.match(/class="([^"]*)"/)?.[1] || null;
const styles = firstElementOnly.match(/style="([^"]*)"/)?.[1]
?.split(';').reduce((styleObj, style) => {
if (style.trim() === '') return styleObj;
?.split(';').reduce((styleObj, style)=>{
if(style.trim() === '') return styleObj;
const index = style.indexOf(':');
const [key, value] = [style.substring(0, index), style.substring(index + 1)];
styleObj[key.trim()] = value.trim();
@@ -784,7 +837,7 @@ const extractHTMLStyleTags = (htmlString)=>{
?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
.reduce((obj, attr)=>{
const index = attr.indexOf('=');
let [key, value] = [attr.substring(0, index), attr.substring(index + 1)];
const [key, value] = [attr.substring(0, index), attr.substring(index + 1)];
obj[key.trim()] = value.replace(/"/g, '');
return obj;
}, {}) || null;
@@ -797,7 +850,7 @@ const extractHTMLStyleTags = (htmlString)=>{
};
};
const mergeHTMLTags = (originalTags, newTags) => {
const mergeHTMLTags = (originalTags, newTags)=>{
return {
id : newTags.id || originalTags.id || null,
classes : [originalTags.classes, newTags.classes].join(' ').trim() || null,
@@ -813,7 +866,13 @@ let globalPageNumber = 0;
const Markdown = {
marked : Marked,
render : (rawBrewText, pageNumber=0)=>{
globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
const lastPageNumber = pageNumber > 0 ? globalVarsList[pageNumber - 1].HB_pageNumber.content : 0;
globalVarsList[pageNumber] = { //Reset global links for current page, to ensure values are parsed in order
'HB_pageNumber' : { //Add document variables for this page
content : !isNaN(Number(lastPageNumber)) ? Number(lastPageNumber) + 1 : lastPageNumber,
resolved : true
}
};
varsQueue = []; //Could move into MarkedVariables()
globalPageNumber = pageNumber;
if(pageNumber==0) {