${cleanStyle} ` }} />;
};
diff --git a/package-lock.json b/package-lock.json
index d9c862149..cce4d5577 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "homebrewery",
- "version": "3.12.0",
+ "version": "3.13.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "homebrewery",
- "version": "3.12.0",
+ "version": "3.13.1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -14,7 +14,7 @@
"@babel/plugin-transform-runtime": "^7.24.7",
"@babel/preset-env": "^7.24.7",
"@babel/preset-react": "^7.24.7",
- "@googleapis/drive": "^8.10.0",
+ "@googleapis/drive": "^8.11.0",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
"codemirror": "^5.65.6",
@@ -32,19 +32,19 @@
"less": "^3.13.1",
"lodash": "^4.17.21",
"marked": "11.2.0",
- "marked-emoji": "^1.4.0",
+ "marked-emoji": "^1.4.1",
"marked-extended-tables": "^1.0.8",
- "marked-gfm-heading-id": "^3.1.3",
+ "marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
- "mongoose": "^8.4.1",
+ "mongoose": "^8.4.5",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-frame-component": "^4.1.3",
- "react-router-dom": "6.23.1",
+ "react-router-dom": "6.24.1",
"sanitize-filename": "1.6.3",
"superagent": "^9.0.2",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
@@ -52,7 +52,7 @@
"devDependencies": {
"eslint": "^8.57.0",
"eslint-plugin-jest": "^28.6.0",
- "eslint-plugin-react": "^7.34.2",
+ "eslint-plugin-react": "^7.34.3",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
@@ -1993,9 +1993,9 @@
}
},
"node_modules/@googleapis/drive": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.10.0.tgz",
- "integrity": "sha512-loumtaDmAn2JvU4KuFMhhtaYG1Hxw0RVS4vl+rOWMU7NAU151XYfIWFDJfFFZjvYZxH4tbsmHEnF+DKH1hQ75Q==",
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.11.0.tgz",
+ "integrity": "sha512-HW6/2oThc4X086mGkZxpdP4P+aHpYbjHa6wr9l1F/R+snpk6G8/EuRXEcTkgQUl2t/NdNz3lj8re0AQBG5faSA==",
"dependencies": {
"googleapis-common": "^7.0.0"
},
@@ -2864,9 +2864,9 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
- "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==",
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.1.tgz",
+ "integrity": "sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==",
"engines": {
"node": ">=14.0.0"
}
@@ -3507,16 +3507,19 @@
}
},
"node_modules/array.prototype.tosorted": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz",
- "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
"dev": true,
"dependencies": {
- "call-bind": "^1.0.5",
+ "call-bind": "^1.0.7",
"define-properties": "^1.2.1",
- "es-abstract": "^1.22.3",
- "es-errors": "^1.1.0",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
"es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
"node_modules/arraybuffer.prototype.slice": {
@@ -5848,16 +5851,16 @@
}
},
"node_modules/eslint-plugin-react": {
- "version": "7.34.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz",
- "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==",
+ "version": "7.34.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz",
+ "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==",
"dev": true,
"dependencies": {
"array-includes": "^3.1.8",
"array.prototype.findlast": "^1.2.5",
"array.prototype.flatmap": "^1.3.2",
"array.prototype.toreversed": "^1.1.2",
- "array.prototype.tosorted": "^1.1.3",
+ "array.prototype.tosorted": "^1.1.4",
"doctrine": "^2.1.0",
"es-iterator-helpers": "^1.0.19",
"estraverse": "^5.3.0",
@@ -10247,11 +10250,11 @@
}
},
"node_modules/marked-emoji": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-1.4.0.tgz",
- "integrity": "sha512-/2TJfGzXpiBBq+X3akHHbTrAjZPJDwR+7FV6SyQLECnQEfaoVkrpKZJzHhPTAq3Sl/A1l2frMT0u6b38VBBlNg==",
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-1.4.1.tgz",
+ "integrity": "sha512-3xHWQn8XD1LyhMpHxWpHTDWBZ9bpXLlW8JIqvyXTO6he7okKIB/W9fD/3fTg0DQuZlSQvPZ6Ub5hN6Rnmn7j9g==",
"peerDependencies": {
- "marked": ">=4 <13"
+ "marked": ">=4 <14"
}
},
"node_modules/marked-extended-tables": {
@@ -10263,9 +10266,9 @@
}
},
"node_modules/marked-gfm-heading-id": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-3.1.3.tgz",
- "integrity": "sha512-A0cRU4PCueX/5m8VE4mT8uTQ36l3xMYRojz3Eqnk4BmUFZ0T+9Xhn2KvHcANP4qbhfOeuMrWJCTQbASIBR5xeg==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-3.2.0.tgz",
+ "integrity": "sha512-Xfxpr5lXLDLY10XqzSCA9l2dDaiabQUgtYM9hw8yunyVsB/xYBRpiic6BOiY/EAJw1ik1eWr1ET1HKOAPZBhXg==",
"dependencies": {
"github-slugger": "^2.0.0"
},
@@ -10656,9 +10659,9 @@
}
},
"node_modules/mongoose": {
- "version": "8.4.1",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.1.tgz",
- "integrity": "sha512-odQ2WEWGL3hb0Qex+QMN4eH6D34WdMEw7F1If2MGABApSDmG9cMmqv/G1H6WsXmuaH9mkuuadW/WbLE5+tHJwA==",
+ "version": "8.4.5",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.5.tgz",
+ "integrity": "sha512-E5KjBThxST2uFSKKXuiMa9H9Zx4DLTSLuxodAnIzJRixNwc1ARTlJUK1m0a80EB+ZKGP4QNTasyUYRG9DUSHOA==",
"dependencies": {
"bson": "^6.7.0",
"kareem": "2.6.3",
@@ -12090,11 +12093,11 @@
"dev": true
},
"node_modules/react-router": {
- "version": "6.23.1",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz",
- "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==",
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.1.tgz",
+ "integrity": "sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==",
"dependencies": {
- "@remix-run/router": "1.16.1"
+ "@remix-run/router": "1.17.1"
},
"engines": {
"node": ">=14.0.0"
@@ -12104,12 +12107,12 @@
}
},
"node_modules/react-router-dom": {
- "version": "6.23.1",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz",
- "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==",
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.1.tgz",
+ "integrity": "sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==",
"dependencies": {
- "@remix-run/router": "1.16.1",
- "react-router": "6.23.1"
+ "@remix-run/router": "1.17.1",
+ "react-router": "6.24.1"
},
"engines": {
"node": ">=14.0.0"
@@ -15180,9 +15183,9 @@
}
},
"node_modules/ws": {
- "version": "7.5.9",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
- "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"engines": {
"node": ">=8.3.0"
},
diff --git a/package.json b/package.json
index b5b7824b3..321f9afbe 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
- "version": "3.12.0",
+ "version": "3.13.1",
"engines": {
"npm": "^10.2.x",
"node": "^20.8.x"
@@ -86,7 +86,7 @@
"@babel/plugin-transform-runtime": "^7.24.7",
"@babel/preset-env": "^7.24.7",
"@babel/preset-react": "^7.24.7",
- "@googleapis/drive": "^8.10.0",
+ "@googleapis/drive": "^8.11.0",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
"codemirror": "^5.65.6",
@@ -104,19 +104,19 @@
"less": "^3.13.1",
"lodash": "^4.17.21",
"marked": "11.2.0",
- "marked-emoji": "^1.4.0",
+ "marked-emoji": "^1.4.1",
"marked-extended-tables": "^1.0.8",
- "marked-gfm-heading-id": "^3.1.3",
+ "marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
- "mongoose": "^8.4.1",
+ "mongoose": "^8.4.5",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-frame-component": "^4.1.3",
- "react-router-dom": "6.23.1",
+ "react-router-dom": "6.24.1",
"sanitize-filename": "1.6.3",
"superagent": "^9.0.2",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
@@ -124,7 +124,7 @@
"devDependencies": {
"eslint": "^8.57.0",
"eslint-plugin-jest": "^28.6.0",
- "eslint-plugin-react": "^7.34.2",
+ "eslint-plugin-react": "^7.34.3",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
diff --git a/shared/naturalcrit/codeEditor/codeEditor.less b/shared/naturalcrit/codeEditor/codeEditor.less
index 0f29eff7b..cb73b0a88 100644
--- a/shared/naturalcrit/codeEditor/codeEditor.less
+++ b/shared/naturalcrit/codeEditor/codeEditor.less
@@ -8,6 +8,7 @@
@import (less) './themes/fonts/iconFonts/diceFont.less';
@import (less) './themes/fonts/iconFonts/elderberryInn.less';
@import (less) './themes/fonts/iconFonts/gameIcons.less';
+@import (less) './themes/fonts/iconFonts/fontAwesome.less';
@keyframes sourceMoveAnimation {
50% {background-color: red; color: white;}
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js
index 95431487d..39939f306 100644
--- a/shared/naturalcrit/markdown.js
+++ b/shared/naturalcrit/markdown.js
@@ -102,7 +102,7 @@ const mustacheSpans = {
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
- const inlineRegex = /{{(?=((?:[:=](?:"['\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *|}}/g;
+ const inlineRegex = /{{(?=((?:[:=](?:"['\w,\-()#%=?. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *|}}/g;
const match = completeSpan.exec(src);
if(match) {
//Find closing delimiter
@@ -159,7 +159,7 @@ const mustacheDivs = {
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
- const blockRegex = /^ *{{(?=((?:[:=](?:"['\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *$|^ *}}$/gm;
+ const blockRegex = /^ *{{(?=((?:[:=](?:"['\w,\-()#%=?. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *$|^ *}}$/gm;
const match = completeBlock.exec(src);
if(match) {
//Find closing delimiter
@@ -214,7 +214,7 @@ const mustacheInjectInline = {
level : 'inline',
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
- const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/g;
+ const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%=?. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/g;
const match = inlineRegex.exec(src);
if(match) {
const lastToken = tokens[tokens.length - 1];
@@ -265,7 +265,7 @@ const mustacheInjectBlock = {
level : 'block',
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
- const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
+ const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%=?. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
const match = inlineRegex.exec(src);
if(match) {
const lastToken = tokens[tokens.length - 1];
@@ -771,7 +771,8 @@ const processStyleTags = (string)=>{
const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"'))
?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
.reduce((obj, attr)=>{
- let [key, value] = attr.split('=');
+ const index = attr.indexOf('=');
+ let [key, value] = [attr.substring(0, index), attr.substring(index + 1)];
value = value.replace(/"/g, '');
obj[key] = value;
return obj;
@@ -786,14 +787,17 @@ const processStyleTags = (string)=>{
};
};
+//Given a string representing an HTML element, extract all of its properties (id, class, style, and other attributes)
const extractHTMLStyleTags = (htmlString)=>{
- const id = htmlString.match(/id="([^"]*)"/)?.[1] || null;
- const classes = htmlString.match(/class="([^"]*)"/)?.[1] || null;
- const styles = htmlString.match(/style="([^"]*)"/)?.[1] || null;
- const attributes = htmlString.match(/[a-zA-Z]+="[^"]*"/g)
+ const firstElementOnly = htmlString.split('>')[0];
+ const id = firstElementOnly.match(/id="([^"]*)"/)?.[1] || null;
+ const classes = firstElementOnly.match(/class="([^"]*)"/)?.[1] || null;
+ const styles = firstElementOnly.match(/style="([^"]*)"/)?.[1] || null;
+ const attributes = firstElementOnly.match(/[a-zA-Z]+="[^"]*"/g)
?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
.reduce((obj, attr)=>{
- let [key, value] = attr.split('=');
+ const index = attr.indexOf('=');
+ let [key, value] = [attr.substring(0, index), attr.substring(index + 1)];
value = value.replace(/"/g, '');
obj[key] = value;
return obj;
diff --git a/tests/markdown/mustache-syntax.test.js b/tests/markdown/mustache-syntax.test.js
index b32876353..3f7f2529b 100644
--- a/tests/markdown/mustache-syntax.test.js
+++ b/tests/markdown/mustache-syntax.test.js
@@ -333,11 +333,30 @@ describe('Injection: When an injection tag follows an element', ()=>{
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
text{background:blue}
');
});
+ it('Renders an parent and child element, each modified by an injector', function() {
+ const source = dedent`**bolded text**{color:red}
+ {color:blue}`;
+ const rendered = Markdown.render(source).trimReturns();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
bolded text
');
+ });
+
it('Renders an image with added attributes', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

`);
});
+
+ it('Renders an image with "=" in the url, and added attributes', function() {
+ const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
+ const rendered = Markdown.render(source).trimReturns();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

`);
+ });
+
+ it('Renders an image and added attributes with "=" in the value, ', function() {
+ const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e,otherUrl="url?auth=12345"}`;
+ const rendered = Markdown.render(source).trimReturns();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

`);
+ });
});
describe('and that element is a block', ()=>{
diff --git a/themes/V3/5ePHB/snippets.js b/themes/V3/5ePHB/snippets.js
index c0933d70d..d5f37ac65 100644
--- a/themes/V3/5ePHB/snippets.js
+++ b/themes/V3/5ePHB/snippets.js
@@ -21,9 +21,43 @@ module.exports = [
view : 'text',
snippets : [
{
- name : 'Table of Contents',
- icon : 'fas fa-book',
- gen : TableOfContentsGen
+ name : 'Table of Contents',
+ icon : 'fas fa-book',
+ gen : TableOfContentsGen,
+ experimental : true,
+ subsnippets : [
+ {
+ name : 'Table of Contents',
+ icon : 'fas fa-book',
+ gen : TableOfContentsGen,
+ experimental : true
+ },
+ {
+ name : 'Include in ToC up to H3',
+ icon : 'fas fa-dice-three',
+ gen : dedent `\n{{tocDepthH3
+ }}\n`,
+
+ },
+ {
+ name : 'Include in ToC up to H4',
+ icon : 'fas fa-dice-four',
+ gen : dedent `\n{{tocDepthH4
+ }}\n`,
+ },
+ {
+ name : 'Include in ToC up to H5',
+ icon : 'fas fa-dice-five',
+ gen : dedent `\n{{tocDepthH5
+ }}\n`,
+ },
+ {
+ name : 'Include in ToC up to H6',
+ icon : 'fas fa-dice-six',
+ gen : dedent `\n{{tocDepthH6
+ }}\n`,
+ }
+ ]
},
{
name : 'Index',
diff --git a/themes/V3/5ePHB/snippets/tableOfContents.gen.js b/themes/V3/5ePHB/snippets/tableOfContents.gen.js
index 04ff77f3f..b212dea36 100644
--- a/themes/V3/5ePHB/snippets/tableOfContents.gen.js
+++ b/themes/V3/5ePHB/snippets/tableOfContents.gen.js
@@ -2,77 +2,68 @@ const _ = require('lodash');
const dedent = require('dedent-tabs').default;
const getTOC = (pages)=>{
- const add1 = (title, page)=>{
- res.push({
- title : title,
- page : page + 1,
- children : []
- });
- };
- const add2 = (title, page)=>{
- if(!_.last(res)) add1(null, page);
- _.last(res).children.push({
- title : title,
- page : page + 1,
- children : []
- });
- };
- const add3 = (title, page)=>{
- if(!_.last(res)) add1(null, page);
- if(!_.last(_.last(res).children)) add2(null, page);
- _.last(_.last(res).children).children.push({
- title : title,
- page : page + 1,
- children : []
- });
+
+ const recursiveAdd = (title, page, targetDepth, child, curDepth=0)=>{
+ if(curDepth > 5) return; // Something went wrong.
+ if(curDepth == targetDepth) {
+ child.push({
+ title : title,
+ page : page,
+ children : []
+ });
+ } else {
+ if(child.length == 0) {
+ child.push({
+ title : null,
+ page : page,
+ children : []
+ });
+ }
+ recursiveAdd(title, page, targetDepth, _.last(child).children, curDepth+1,);
+ }
};
const res = [];
- _.each(pages, (page, pageNum)=>{
- if(!page.includes('{{frontCover}}') && !page.includes('{{insideCover}}') && !page.includes('{{partCover}}') && !page.includes('{{backCover}}')) {
- const lines = page.split('\n');
- _.each(lines, (line)=>{
- if(_.startsWith(line, '# ')){
- const title = line.replace('# ', '');
- add1(title, pageNum);
- }
- if(_.startsWith(line, '## ')){
- const title = line.replace('## ', '');
- add2(title, pageNum);
- }
- if(_.startsWith(line, '### ')){
- const title = line.replace('### ', '');
- add3(title, pageNum);
- }
- });
+
+ const iframe = document.getElementById('BrewRenderer');
+ const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
+ const headings = iframeDocument.querySelectorAll('h1, h2, h3, h4, h5, h6');
+ const headerDepth = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
+
+ _.each(headings, (heading)=>{
+ const onPage = parseInt(heading.closest('.page').id?.replace(/^p/, ''));
+ const ToCExclude = getComputedStyle(heading).getPropertyValue('--TOC');
+
+ if(ToCExclude != 'exclude') {
+ recursiveAdd(heading.innerText.trim(), onPage, headerDepth.indexOf(heading.tagName), res);
}
});
return res;
};
+
+const ToCIterate = (entries, curDepth=0)=>{
+ const levelPad = ['- ###', ' - ####', ' - ', ' - ', ' - ', ' - '];
+ const toc = [];
+ if(entries.title !== null){
+ toc.push(`${levelPad[curDepth]} [{{ ${entries.title}}}{{ ${entries.page}}}](#p${entries.page})`);
+ }
+ if(entries.children.length) {
+ _.each(entries.children, (entry, idx)=>{
+ const children = ToCIterate(entry, entry.title == null ? curDepth : curDepth+1);
+ if(children.length) {
+ toc.push(...children);
+ }
+ });
+ }
+ return toc;
+};
+
module.exports = function(props){
const pages = props.brew.text.split('\\page');
const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
- if(g1.title !== null) {
- r.push(`- ### [{{ ${g1.title}}}{{ ${g1.page}}}](#p${g1.page})`);
- }
- if(g1.children.length){
- _.each(g1.children, (g2, idx2)=>{
- if(g2.title !== null) {
- r.push(` - #### [{{ ${g2.title}}}{{ ${g2.page}}}](#p${g2.page})`);
- }
- if(g2.children.length){
- _.each(g2.children, (g3, idx3)=>{
- if(g2.title !== null) {
- r.push(` - [{{ ${g3.title}}}{{ ${g3.page}}}](#p${g3.page})`);
- } else { // Don't over-indent if no level-2 parent entry
- r.push(` - [{{ ${g3.title}}}{{ ${g3.page}}}](#p${g3.page})`);
- }
- });
- }
- });
- }
+ r.push(ToCIterate(g1).join('\n'));
return r;
}, []).join('\n');
diff --git a/themes/V3/5ePHB/style.less b/themes/V3/5ePHB/style.less
index 400174ab0..f8a14f46e 100644
--- a/themes/V3/5ePHB/style.less
+++ b/themes/V3/5ePHB/style.less
@@ -786,6 +786,39 @@
// *****************************
// * TABLE OF CONTENTS
// *****************************/
+
+// Default Exclusions
+// Anything not exlcuded is included, default Headers are H1, H2, and H3.
+h4,
+h5,
+h6,
+.page:has(.frontCover),
+.page:has(.backCover),
+.page:has(.insideCover),
+.monster,
+.noToC,
+.toc { --TOC: exclude; }
+
+.tocDepthH2 :is(h1, h2) {--TOC: include; }
+.tocDepthH3 :is(h1, h2, h3) {--TOC: include; }
+.tocDepthH4 :is(h1, h2, h3, h4) {--TOC: include; }
+.tocDepthH5 :is(h1, h2, h3, h4, h5) {--TOC: include; }
+.tocDepthH6 :is(h1, h2, h3, h4, h5, h6) {--TOC: include; }
+
+.tocIncludeH1 h1 {--TOC: include; }
+.tocIncludeH2 h2 {--TOC: include; }
+.tocIncludeH3 h3 {--TOC: include; }
+.tocIncludeH4 h4 {--TOC: include; }
+.tocIncludeH5 h5 {--TOC: include; }
+.tocIncludeH6 h6 {--TOC: include; }
+
+.page:has(.partCover) {
+ --TOC: exclude;
+ & h1 {
+ --TOC: include;
+ }
+ }
+
.page {
&:has(.toc)::after { display : none; }
.toc {
diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less
index 24e87504f..0f779c38b 100644
--- a/themes/V3/Blank/style.less
+++ b/themes/V3/Blank/style.less
@@ -3,6 +3,7 @@
@import (less) './themes/fonts/iconFonts/elderberryInn.less';
@import (less) './themes/fonts/iconFonts/diceFont.less';
@import (less) './themes/fonts/iconFonts/gameIcons.less';
+@import (less) './themes/fonts/iconFonts/fontAwesome.less';
:root {
//Colors
diff --git a/themes/fonts/iconFonts/diceFont.less b/themes/fonts/iconFonts/diceFont.less
index 6fe226a05..ec80f132b 100644
--- a/themes/fonts/iconFonts/diceFont.less
+++ b/themes/fonts/iconFonts/diceFont.less
@@ -7,7 +7,7 @@
}
.df {
- display : inline-block;
+ display : inline;
font-family : 'DiceFont';
font-style : normal;
font-weight : normal;
@@ -16,8 +16,11 @@
text-decoration : inherit;
text-transform : none;
text-rendering : optimizeLegibility;
- -moz-osx-font-smoothing : grayscale;
+
+ /* Better Font Rendering =========== */
-webkit-font-smoothing : antialiased;
+ -moz-osx-font-smoothing : grayscale;
+
&.F::before { content : '\f190'; }
&.F-minus::before { content : '\f191'; }
&.F-plus::before { content : '\f192'; }
diff --git a/themes/fonts/iconFonts/elderberryInn.less b/themes/fonts/iconFonts/elderberryInn.less
index c956563fc..958d1b265 100644
--- a/themes/fonts/iconFonts/elderberryInn.less
+++ b/themes/fonts/iconFonts/elderberryInn.less
@@ -7,15 +7,16 @@
}
.ei {
- display : inline-block;
- margin-right : 3px;
+ display : inline;
font-family : 'Elderberry-Inn';
line-height : 1;
vertical-align : baseline;
- -moz-osx-font-smoothing : grayscale;
- -webkit-font-smoothing : antialiased;
text-rendering : auto;
+ /* Better Font Rendering =========== */
+ -webkit-font-smoothing : antialiased;
+ -moz-osx-font-smoothing : grayscale;
+
&.book::before { content : '\E900'; }
&.screen::before { content : '\E901'; }
diff --git a/themes/fonts/iconFonts/fontAwesome.less b/themes/fonts/iconFonts/fontAwesome.less
new file mode 100644
index 000000000..5f626c645
--- /dev/null
+++ b/themes/fonts/iconFonts/fontAwesome.less
@@ -0,0 +1,2 @@
+/* Icon Font: Font Awesome */
+.far,.fas,.fab { display : inline; }
\ No newline at end of file
diff --git a/themes/fonts/iconFonts/gameIcons.less b/themes/fonts/iconFonts/gameIcons.less
index ea7b3aba5..a32ebdd08 100644
--- a/themes/fonts/iconFonts/gameIcons.less
+++ b/themes/fonts/iconFonts/gameIcons.less
@@ -8,19 +8,15 @@
.gi {
/* use !important to prevent issues with browser extensions that change fonts */
- display : inline-block;
- margin-right : 3px;
+ display : inline;
font-family : 'Game-Icons' !important;
line-height : 1;
vertical-align : baseline;
- -moz-osx-font-smoothing : grayscale;
- -webkit-font-smoothing : antialiased;
text-rendering : auto;
/* Better Font Rendering =========== */
-webkit-font-smoothing : antialiased;
-moz-osx-font-smoothing : grayscale;
-
&.zigzag-leaf::before { content : '\e900'; }
&.zebra-shield::before { content : '\e901'; }