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

Merge branch 'master' into pr/1549

This commit is contained in:
Trevor Buckner
2021-08-31 16:45:14 -04:00
24 changed files with 532 additions and 440 deletions

View File

@@ -6,6 +6,17 @@ h5 {
# changelog # changelog
### Tuesday, 17/08/2021 - v2.13.4
- Fixed user page crashing when user has untitled brew
##### G-Ambatte:
- Tweaks to user page tool tips
- Fix view counts being reset on Google Drive files
##### Gazook89 :
- New **PHB → Artist Credit** snippet
- **PRINT** snippets moved to the **Style Editor** tab
### Monday, 09/08/2021 - v2.13.3 ### Monday, 09/08/2021 - v2.13.3
##### G-Ambatte : ##### G-Ambatte :
@@ -48,6 +59,8 @@ myStyle {color: black}
- Pasting your brew into a "New" page and saving will transfer any CSS in the code fence to the Style tab. - Pasting your brew into a "New" page and saving will transfer any CSS in the code fence to the Style tab.
- Unsaved work in the New page Style tab is now cached to your browser storage if you navigate away. - Unsaved work in the New page Style tab is now cached to your browser storage if you navigate away.
\page
### Thursday, 10/6/2021 - v2.12.0 ### Thursday, 10/6/2021 - v2.12.0

View File

@@ -30,7 +30,7 @@ const BrewRenderer = createClass({
if(this.props.renderer == 'legacy') { if(this.props.renderer == 'legacy') {
pages = this.props.text.split('\\page'); pages = this.props.text.split('\\page');
} else { } else {
pages = this.props.text.split(/^\\page/gm); pages = this.props.text.split(/^\\page$/gm);
} }
return { return {
@@ -62,7 +62,7 @@ const BrewRenderer = createClass({
if(this.props.renderer == 'legacy') { if(this.props.renderer == 'legacy') {
pages = this.props.text.split('\\page'); pages = this.props.text.split('\\page');
} else { } else {
pages = this.props.text.split(/^\\page/gm); pages = this.props.text.split(/^\\page$/gm);
} }
this.setState({ this.setState({
pages : pages, pages : pages,
@@ -130,8 +130,14 @@ const BrewRenderer = createClass({
renderPage : function(pageText, index){ renderPage : function(pageText, index){
if(this.props.renderer == 'legacy') if(this.props.renderer == 'legacy')
return <div className='phb page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(pageText) }} key={index} />; return <div className='phb page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(pageText) }} key={index} />;
else else {
return <div className='phb3 page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: Markdown.render(pageText) }} key={index} />; pageText += `\n\\column\n&nbsp;`; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
return (
<div className='page' id={`p${index + 1}`} key={index} >
<div className='columnWrapper' dangerouslySetInnerHTML={{ __html: Markdown.render(pageText) }} />
</div>
);
}
}, },
renderPages : function(){ renderPages : function(){

View File

@@ -68,7 +68,9 @@ const Editor = createClass({
}, },
handleInject : function(injectText){ handleInject : function(injectText){
const text = (this.isText() ? this.props.brew.text : this.props.brew.style); let text;
if(this.isText()) text = this.props.brew.text;
if(this.isStyle()) text = this.props.brew.style ?? DEFAULT_STYLE_TEXT;
const lines = text.split('\n'); const lines = text.split('\n');
const cursorPos = this.refs.codeEditor.getCursorPosition(); const cursorPos = this.refs.codeEditor.getCursorPosition();
@@ -119,7 +121,7 @@ const Editor = createClass({
// New Codemirror styling for V3 renderer // New Codemirror styling for V3 renderer
if(this.props.renderer == 'V3') { if(this.props.renderer == 'V3') {
if(line.startsWith('\\page')){ if(line.match(/^\\page$/)){
codeMirror.addLineClass(lineNumber, 'background', 'pageLine'); codeMirror.addLineClass(lineNumber, 'background', 'pageLine');
r.push(lineNumber); r.push(lineNumber);
} }

View File

@@ -67,9 +67,6 @@
.button(@silver); .button(@silver);
} }
small{ small{
position : absolute;
bottom : -15px;
left : 0px;
font-size : 0.6em; font-size : 0.6em;
font-style : italic; font-style : italic;
} }

View File

@@ -2,86 +2,77 @@ const _ = require('lodash');
const features = [ const features = [
'Astrological Botany', 'Astrological Botany',
'Astrological Chemistry',
'Biochemical Sorcery', 'Biochemical Sorcery',
'Civil Alchemy', 'Civil Divination',
'Consecrated Biochemistry', 'Consecrated Augury',
'Demonic Anthropology', 'Demonic Anthropology',
'Divinatory Mineralogy', 'Divinatory Mineralogy',
'Genetic Banishing',
'Hermetic Geography',
'Immunological Incantations',
'Nuclear Illusionism',
'Ritual Astronomy',
'Seismological Divination',
'Spiritual Biochemistry',
'Statistical Occultism',
'Police Necromancer',
'Sixgun Poisoner',
'Pharmaceutical Gunslinger',
'Infernal Banker',
'Spell Analyst',
'Gunslinger Corruptor',
'Torque Interfacer',
'Exo Interfacer', 'Exo Interfacer',
'Genetic Banishing',
'Gunpowder Torturer', 'Gunpowder Torturer',
'Orbital Gravedigger', 'Gunslinger Corruptor',
'Phased Linguist', 'Hermetic Geography',
'Mathematical Pharmacist', 'Immunological Cultist',
'Plasma Outlaw',
'Malefic Chemist', 'Malefic Chemist',
'Police Cultist' 'Mathematical Pharmacy',
'Nuclear Biochemistry',
'Orbital Gravedigger',
'Pharmaceutical Outlaw',
'Phased Linguist',
'Plasma Gunslinger',
'Police Necromancer',
'Ritual Astronomy',
'Sixgun Poisoner',
'Seismological Alchemy',
'Spiritual Illusionism',
'Statistical Occultism',
'Spell Analyst',
'Torque Interfacer'
]; ];
const classnames = ['Archivist', 'Fancyman', 'Linguist', 'Fletcher', const classnames = ['Ackerman', 'Berserker-Typist', 'Concierge', 'Fishmonger',
'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge']; 'Haberdasher', 'Manicurist', 'Netrunner', 'Weirkeeper'];
const levels = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th', '8th', '9th', '10th', '11th', '12th', '13th', '14th', '15th', '16th', '17th', '18th', '19th', '20th']; const levels = ['1st', '2nd', '3rd', '4th', '5th',
'6th', '7th', '8th', '9th', '10th',
'11th', '12th', '13th', '14th', '15th',
'16th', '17th', '18th', '19th', '20th'];
const profBonus = [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6]; const profBonus = [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6];
const getFeature = (level)=>{ const maxes = [4, 3, 3, 3, 3, 2, 2, 1, 1];
let res = [];
if(_.includes([4, 6, 8, 12, 14, 16, 19], level+1)){ const drawSlots = function(Slots, rows, padding){
res = ['Ability Score Improvement']; let slots = Number(Slots);
} return _.times(rows, function(i){
res = _.union(res, _.sampleSize(features, _.sample([0, 1, 1, 1, 1, 1]))); const max = maxes[i];
if(!res.length) return '─'; if(slots < 1) return _.pad('—', padding);
return res.join(', '); const res = _.min([max, slots]);
slots -= res;
return _.pad(res.toString(), padding);
}).join(' | ');
}; };
module.exports = { module.exports = {
full : function(){ full : function(classes){
const classname = _.sample(classnames); const classname = _.sample(classnames);
const maxes = [4, 3, 3, 3, 3, 2, 2, 1, 1];
const drawSlots = function(Slots){
let slots = Number(Slots);
return _.times(9, function(i){
const max = maxes[i];
if(slots < 1) return '—';
const res = _.min([max, slots]);
slots -= res;
return res;
}).join(' | ');
};
let cantrips = 3; let cantrips = 3;
let spells = 1; let spells = 1;
let slots = 2; let slots = 2;
return `{{classTable,wide\n##### The ${classname}\n` + return `{{${classes}\n##### The ${classname}\n` +
`| Level | Proficiency | Features | Cantrips | Spells | --- Spell Slots Per Level --- |||||||||\n`+ `| Level | Proficiency | Features | Cantrips | Spells | --- Spell Slots Per Spell Level ---|||||||||\n`+
`| ^| Bonus ^| ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th |\n`+ `| ^| Bonus ^| ^| Known ^| Known ^|1st |2nd |3rd |4th |5th |6th |7th |8th |9th |\n`+
`|:-----:|:-----------:|:---------|:--------:|:------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n${ `|:-----:|:-----------:|:-------------|:--------:|:------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|\n${
_.map(levels, function(levelName, level){ _.map(levels, function(levelName, level){
const res = [ const res = [
levelName, _.pad(levelName, 5),
`+${profBonus[level]}`, _.pad(`+${profBonus[level]}`, 2),
getFeature(level), _.padEnd(_.sample(features), 21),
cantrips, _.pad(cantrips.toString(), 8),
spells, _.pad(spells.toString(), 6),
drawSlots(slots) drawSlots(slots, 9, 2),
].join(' | '); ].join(' | ');
cantrips += _.random(0, 1); cantrips += _.random(0, 1);
@@ -92,24 +83,50 @@ module.exports = {
}).join('\n')}\n}}\n\n`; }).join('\n')}\n}}\n\n`;
}, },
half : function(){ half : function(classes){
const classname = _.sample(classnames); const classname = _.sample(classnames);
let featureScore = 1; let featureScore = 1;
return `<div class='classTable'>\n##### The ${classname}\n` + return `{{${classes}\n##### The ${classname}\n` +
`| Level | Proficiency Bonus | Features | ${_.sample(features)}|\n` + `| Level | Proficiency Bonus | Features | ${_.pad(_.sample(features), 21)} |\n` +
`|:---:|:---:|:---|:---:|\n${ `|:-----:|:-----------------:|:---------|:---------------------:|\n${
_.map(levels, function(levelName, level){ _.map(levels, function(levelName, level){
const res = [ const res = [
levelName, _.pad(levelName, 5),
`+${profBonus[level]}`, _.pad(`+${profBonus[level]}`, 2),
getFeature(level), _.padEnd(_.sample(features), 23),
`+${featureScore}` _.pad(`+${featureScore}`, 21),
].join(' | '); ].join(' | ');
featureScore += _.random(0, 1); featureScore += _.random(0, 1);
return `| ${res} |`; return `| ${res} |`;
}).join('\n')}\n</div>\n\n`; }).join('\n')}\n}}\n\n`;
},
third : function(classes){
const classname = _.sample(classnames);
let cantrips = 3;
let spells = 1;
let slots = 2;
return `{{${classes}\n##### ${classname} Spellcasting\n` +
`| Class | Cantrips | Spells |--- Spells Slots per Spell Level ---||||\n` +
`| Level ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th |\n` +
`|:------:|:--------:|:-------:|:-------:|:-------:|:-------:|:-------:|\n${
_.map(levels, function(levelName, level){
const res = [
_.pad(levelName, 6),
_.pad(cantrips.toString(), 8),
_.pad(spells.toString(), 7),
drawSlots(slots, 4, 7),
].join(' | ');
cantrips += _.random(0, 1);
spells += _.random(0, 1);
slots += _.random(0, 1);
return `| ${res} |`;
}).join('\n')}\n}}\n\n`;
} }
}; };

View File

@@ -44,8 +44,8 @@ module.exports = [
{{wide {{wide
Everything in here will be extra wide. Tables, text, everything! Everything in here will be extra wide. Tables, text, everything!
Beware though, CSS columns can behave a bit weird sometimes. You may Beware though, CSS columns can behave a bit weird sometimes. You may
have to rely on the automatic column-break rather than \`\column\` if have to manually place column breaks with \`\column\` to make the
you mix columns and wide blocks on the same page. surrounding text flow with this wide block the way you want.
}} }}
\n` \n`
}, },
@@ -262,12 +262,32 @@ module.exports = [
{ {
name : 'Class Table', name : 'Class Table',
icon : 'fas fa-table', icon : 'fas fa-table',
gen : ClassTableGen.full, gen : ClassTableGen.full('classTable,frame,wide'),
}, },
{ {
name : 'Half Class Table', name : 'Class Table (unframed)',
icon : 'fas fa-border-none',
gen : ClassTableGen.full('classTable,wide'),
},
{
name : '1/2 Class Table',
icon : 'fas fa-list-alt', icon : 'fas fa-list-alt',
gen : ClassTableGen.half, gen : ClassTableGen.half('classTable,frame'),
},
{
name : '1/2 Class Table (unframed)',
icon : 'fas fa-border-none',
gen : ClassTableGen.half('classTable'),
},
{
name : '1/3 Class Table',
icon : 'fas fa-border-all',
gen : ClassTableGen.third('classTable,frame'),
},
{
name : '1/3 Class Table (unframed)',
icon : 'fas fa-border-none',
gen : ClassTableGen.third('classTable'),
}, },
{ {
name : 'Table', name : 'Table',

View File

@@ -305,7 +305,7 @@ module.exports = [
name : 'Ink Friendly', name : 'Ink Friendly',
icon : 'fas fa-tint', icon : 'fas fa-tint',
gen : dedent` gen : dedent`
/* Ink Friendly */', /* Ink Friendly */
.phb, .phb blockquote, .phb hr+blockquote { .phb, .phb blockquote, .phb hr+blockquote {
background : white; background : white;
box-shadow : 0px 0px 3px; box-shadow : 0px 0px 3px;

View File

@@ -196,11 +196,14 @@ const EditPage = createClass({
const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId);
const brew = this.state.brew;
brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
if(this.state.saveGoogle) { if(this.state.saveGoogle) {
if(transfer) { if(transfer) {
const res = await request const res = await request
.post('/api/newGoogle/') .post('/api/newGoogle/')
.send(this.state.brew) .send(brew)
.catch((err)=>{ .catch((err)=>{
console.log(err.status === 401 console.log(err.status === 401
? 'Not signed in!' ? 'Not signed in!'
@@ -211,7 +214,7 @@ const EditPage = createClass({
if(!res) { return; } if(!res) { return; }
console.log('Deleting Local Copy'); console.log('Deleting Local Copy');
await request.delete(`/api/${this.state.brew.editId}`) await request.delete(`/api/${brew.editId}`)
.send() .send()
.catch((err)=>{ .catch((err)=>{
console.log('Error deleting Local Copy'); console.log('Error deleting Local Copy');
@@ -221,8 +224,8 @@ const EditPage = createClass({
history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID
} else { } else {
const res = await request const res = await request
.put(`/api/updateGoogle/${this.state.brew.editId}`) .put(`/api/updateGoogle/${brew.editId}`)
.send(this.state.brew) .send(brew)
.catch((err)=>{ .catch((err)=>{
console.log(err.status === 401 console.log(err.status === 401
? 'Not signed in!' ? 'Not signed in!'
@@ -236,14 +239,14 @@ const EditPage = createClass({
} else { } else {
if(transfer) { if(transfer) {
const res = await request.post('/api') const res = await request.post('/api')
.send(this.state.brew) .send(brew)
.catch((err)=>{ .catch((err)=>{
console.log('Error creating Local Copy'); console.log('Error creating Local Copy');
this.setState({ errors: err }); this.setState({ errors: err });
return; return;
}); });
await request.get(`/api/removeGoogle/${this.state.brew.googleId}${this.state.brew.editId}`) await request.get(`/api/removeGoogle/${brew.googleId}${brew.editId}`)
.send() .send()
.catch((err)=>{ .catch((err)=>{
console.log('Error Deleting Google Brew'); console.log('Error Deleting Google Brew');
@@ -253,8 +256,8 @@ const EditPage = createClass({
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID
} else { } else {
const res = await request const res = await request
.put(`/api/update/${this.state.brew.editId}`) .put(`/api/update/${brew.editId}`)
.send(this.state.brew) .send(brew)
.catch((err)=>{ .catch((err)=>{
console.log('Error Updating Local Brew'); console.log('Error Updating Local Brew');
this.setState({ errors: err }); this.setState({ errors: err });

View File

@@ -4,7 +4,7 @@
} }
.page { .page {
padding-bottom : 1.6cm; padding-bottom : 1.3cm;
} }
@@ -37,7 +37,10 @@ After clicking the "Print" item in the navbar a new page will open and a print d
If you want to save ink or have a monochrome printer, add the {{fas,fa-tint}} **Ink Friendly** snippet to your brew before you print If you want to save ink or have a monochrome printer, add the {{fas,fa-tint}} **Ink Friendly** snippet to your brew before you print
<img src='https://i.imgur.com/hMna6G0.png' style='position:absolute;bottom:50px;left:120px;width:180px' />
<div class='pageNumber'>1</div>
<div class='footnote'>PART 1 | FANCINESS</div>
\column \column
@@ -75,15 +78,6 @@ If you'd like to credit The Homebrewery in your brew, I'd be flattered! Just ref
If you are looking for more 5e Homebrew resources check out [r/UnearthedArcana](https://www.reddit.com/r/UnearthedArcana/) and their list of useful resources [here](https://www.reddit.com/r/UnearthedArcana/comments/3uwxx9/resources_open_to_the_community/). If you are looking for more 5e Homebrew resources check out [r/UnearthedArcana](https://www.reddit.com/r/UnearthedArcana/) and their list of useful resources [here](https://www.reddit.com/r/UnearthedArcana/comments/3uwxx9/resources_open_to_the_community/).
<img src='https://i.imgur.com/hMna6G0.png' style='position:absolute;bottom:50px;left:120px;width:180px' />
<div class='pageNumber'>1</div>
<div class='footnote'>PART 1 | FANCINESS</div>
\page \page
## Markdown+ ## Markdown+

View File

@@ -161,6 +161,8 @@ const NewPage = createClass({
brew.text = brew.text.slice(index + 5); brew.text = brew.text.slice(index + 5);
}; };
brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
if(this.state.saveGoogle) { if(this.state.saveGoogle) {
const res = await request const res = await request
.post('/api/newGoogle/') .post('/api/newGoogle/')

View File

@@ -37,20 +37,21 @@ const PrintPage = createClass({
renderPages : function(){ renderPages : function(){
if(this.props.brew.renderer == 'legacy') { if(this.props.brew.renderer == 'legacy') {
return _.map(this.state.brewText.split('\\page'), (page, index)=>{ return _.map(this.state.brewText.split('\\page'), (pageText, index)=>{
return <div return <div
className='phb page' className='phb page'
id={`p${index + 1}`} id={`p${index + 1}`}
dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(page) }} dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(pageText) }}
key={index} />; key={index} />;
}); });
} else { } else {
return _.map(this.state.brewText.split(/^\\page/gm), (page, index)=>{ return _.map(this.state.brewText.split(/^\\page$/gm), (pageText, index)=>{
return <div pageText += `\n\\column\n&nbsp;`; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
className='phb3 page' return (
id={`p${index + 1}`} <div className='page' id={`p${index + 1}`} key={index} >
dangerouslySetInnerHTML={{ __html: Markdown.render(page) }} <div className='columnWrapper' dangerouslySetInnerHTML={{ __html: Markdown.render(pageText) }} />
key={index} />; </div>
);
}); });
} }

View File

@@ -48,7 +48,7 @@ const BrewItem = createClass({
if(!this.props.brew.editId) return; if(!this.props.brew.editId) return;
return <a onClick={this.deleteBrew}> return <a onClick={this.deleteBrew}>
<i className='fas fa-trash-alt' /> <i className='fas fa-trash-alt' title='Delete' />
</a>; </a>;
}, },
@@ -61,7 +61,7 @@ const BrewItem = createClass({
} }
return <a href={`/edit/${editLink}`} target='_blank' rel='noopener noreferrer'> return <a href={`/edit/${editLink}`} target='_blank' rel='noopener noreferrer'>
<i className='fas fa-pencil-alt' /> <i className='fas fa-pencil-alt' title='Edit' />
</a>; </a>;
}, },
@@ -74,7 +74,7 @@ const BrewItem = createClass({
} }
return <a href={`/share/${shareLink}`} target='_blank' rel='noopener noreferrer'> return <a href={`/share/${shareLink}`} target='_blank' rel='noopener noreferrer'>
<i className='fas fa-share-alt' /> <i className='fas fa-share-alt' title='Share' />
</a>; </a>;
}, },
@@ -87,7 +87,7 @@ const BrewItem = createClass({
} }
return <a href={`/download/${shareLink}`}> return <a href={`/download/${shareLink}`}>
<i className='fas fa-download' /> <i className='fas fa-download' title='Download' />
</a>; </a>;
}, },
@@ -99,31 +99,33 @@ const BrewItem = createClass({
</span>; </span>;
}, },
getTooltipData : function(){
const dateFormatString = 'YYYY-MM-DD HH:mm:ss';
let outputString = `Created: ${this.props.brew.createdAt ? moment(this.props.brew.createdAt).local().format(dateFormatString) : 'UNKNOWN'}\n`;
outputString += `Last updated: ${this.props.brew.updatedAt ? moment(this.props.brew.updatedAt).local().format(dateFormatString) : 'UNKNOWN'}`;
return outputString;
},
render : function(){ render : function(){
const brew = this.props.brew; const brew = this.props.brew;
return <div className='brewItem' title={this.getTooltipData()}> const dateFormatString = 'YYYY-MM-DD HH:mm:ss';
return <div className='brewItem'>
<div className='text'>
<h2>{brew.title}</h2> <h2>{brew.title}</h2>
<p className='description'>{brew.description}</p> <p className='description'>{brew.description}</p>
</div>
<hr /> <hr />
<div className='info'> <div className='info'>
<span> <span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>
<i className='fas fa-user' /> {brew.authors.join(', ')} <i className='fas fa-eye'/> {brew.views}
</span> </span>
<span> {brew.pageCount &&
<i className='fas fa-eye' /> {brew.views} <span title={`Page count: ${brew.pageCount}`}>
<i className='far fa-file' /> {brew.pageCount}
</span> </span>
}
<span> <span>
<i className='fas fa-sync-alt' /> {moment(brew.updatedAt).fromNow()} <i className='fas fa-sync-alt' /> {moment(brew.updatedAt).fromNow()}
</span> </span>
{this.renderGoogleDriveIcon()} {this.renderGoogleDriveIcon()}
<br />
<span title={`Authors:\n${brew.authors.join('\n')}`}>
<i className='fas fa-user'/> {brew.authors.join(', ')}
</span>
</div> </div>
<div className='links'> <div className='links'>

View File

@@ -10,24 +10,28 @@
min-height : 105px; min-height : 105px;
margin-right : 15px; margin-right : 15px;
margin-bottom : 15px; margin-bottom : 15px;
padding : 5px 15px 5px 8px; padding : 5px 15px 2px 8px;
padding-right : 15px; padding-right : 15px;
border : 1px solid #c9ad6a; border : 1px solid #c9ad6a;
border-radius : 5px; border-radius : 5px;
-webkit-column-break-inside : avoid; -webkit-column-break-inside : avoid;
page-break-inside : avoid; page-break-inside : avoid;
break-inside : avoid; break-inside : avoid;
.text {
min-height : 54px;
h4{ h4{
margin-bottom : 5px; margin-bottom : 5px;
font-size : 2.2em; font-size : 2.2em;
} }
}
.info{ .info{
position: absolute; position: initial;
bottom: 0px; bottom: 2px;
margin-bottom: 4px; margin-bottom: 4px;
font-family : ScalySans; font-family : ScalySans;
font-size : 1.2em; font-size : 1.2em;
&>span{ &>span{
display : float;
margin-right : 12px; margin-right : 12px;
} }
} }

View File

@@ -32,7 +32,8 @@ const UserPage = createClass({
getInitialState : function() { getInitialState : function() {
return { return {
sortType : 'alpha', sortType : 'alpha',
sortDir : 'asc' sortDir : 'asc',
filterString : ''
}; };
}, },
getUsernameWithS : function() { getUsernameWithS : function() {
@@ -44,7 +45,7 @@ const UserPage = createClass({
renderBrews : function(brews){ renderBrews : function(brews){
if(!brews || !brews.length) return <div className='noBrews'>No Brews.</div>; if(!brews || !brews.length) return <div className='noBrews'>No Brews.</div>;
const sortedBrews = this.sortBrews(brews, this.state.sortType); const sortedBrews = this.sortBrews(brews);
return _.map(sortedBrews, (brew, idx)=>{ return _.map(sortedBrews, (brew, idx)=>{
return <BrewItem brew={brew} key={idx}/>; return <BrewItem brew={brew} key={idx}/>;
@@ -52,6 +53,7 @@ const UserPage = createClass({
}, },
sortBrewOrder : function(brew){ sortBrewOrder : function(brew){
if(!brew.title){brew.title = 'No Title';}
const mapping = { const mapping = {
'alpha' : _.deburr(brew.title.toLowerCase()), 'alpha' : _.deburr(brew.title.toLowerCase()),
'created' : moment(brew.createdAt).format(), 'created' : moment(brew.createdAt).format(),
@@ -90,6 +92,26 @@ const UserPage = createClass({
</td>; </td>;
}, },
handleFilterTextChange : function(e){
this.setState({
filterString : e.target.value
});
return;
},
renderFilterOption : function(){
return <td>
<label>
<i className='fas fa-search'></i>
<input
type='search'
placeholder='search title/description'
onChange={this.handleFilterTextChange}
/>
</label>
</td>;
},
renderSortOptions : function(){ renderSortOptions : function(){
return <div className='sort-container'> return <div className='sort-container'>
<table> <table>
@@ -114,6 +136,7 @@ const UserPage = createClass({
{`${(this.state.sortDir == 'asc' ? '\u25B2 ASC' : '\u25BC DESC')}`} {`${(this.state.sortDir == 'asc' ? '\u25B2 ASC' : '\u25BC DESC')}`}
</button> </button>
</td> </td>
{this.renderFilterOption()}
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -121,7 +144,12 @@ const UserPage = createClass({
}, },
getSortedBrews : function(){ getSortedBrews : function(){
return _.groupBy(this.props.brews, (brew)=>{ const testString = _.deburr(this.state.filterString).toLowerCase();
const brewCollection = this.state.filterString ? _.filter(this.props.brews, (brew)=>{
return (_.deburr(brew.title).toLowerCase().includes(testString)) ||
(_.deburr(brew.description).toLowerCase().includes(testString));
}) : this.props.brews;
return _.groupBy(brewCollection, (brew)=>{
return (brew.published ? 'published' : 'private'); return (brew.published ? 'published' : 'private');
}); });
}, },

View File

@@ -34,8 +34,9 @@
font-family : 'Open Sans', sans-serif; font-family : 'Open Sans', sans-serif;
position : fixed; position : fixed;
top : 35px; top : 35px;
left : calc(50vw - 408px);
border : 2px solid #58180D; border : 2px solid #58180D;
width : 675px; width : 800px;
background-color : #EEE5CE; background-color : #EEE5CE;
padding : 2px; padding : 2px;
text-align : center; text-align : center;
@@ -52,6 +53,9 @@
vertical-align : middle; vertical-align : middle;
tbody tr{ tbody tr{
background-color: transparent !important; background-color: transparent !important;
i{
padding-right : 5px
}
button{ button{
background-color : transparent; background-color : transparent;
color : #58180D; color : #58180D;

290
package-lock.json generated
View File

@@ -1,11 +1,11 @@
{ {
"name": "homebrewery", "name": "homebrewery",
"version": "2.13.3", "version": "2.13.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"version": "2.13.3", "version": "2.13.4",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -15,7 +15,7 @@
"@babel/preset-react": "^7.14.5", "@babel/preset-react": "^7.14.5",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"codemirror": "^5.62.2", "codemirror": "^5.62.3",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
"create-react-class": "^15.7.0", "create-react-class": "^15.7.0",
"dedent-tabs": "^0.9.0", "dedent-tabs": "^0.9.0",
@@ -27,7 +27,7 @@
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
"less": "^3.13.1", "less": "^3.13.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"marked": "2.1.3", "marked": "3.0.2",
"markedLegacy": "npm:marked@^0.3.19", "markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.29.1", "moment": "^2.29.1",
"mongoose": "^5.13.7", "mongoose": "^5.13.7",
@@ -38,14 +38,14 @@
"react": "^16.14.0", "react": "^16.14.0",
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"react-frame-component": "4.1.3", "react-frame-component": "4.1.3",
"react-router-dom": "5.2.0", "react-router-dom": "5.2.1",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
"superagent": "^6.1.0", "superagent": "^6.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git" "vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-plugin-react": "^7.24.0", "eslint-plugin-react": "^7.25.1",
"pico-check": "^2.1.3" "pico-check": "^2.1.3"
}, },
"engines": { "engines": {
@@ -1665,11 +1665,14 @@
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.9.6", "version": "7.15.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz",
"integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.13.4" "regenerator-runtime": "^0.13.4"
},
"engines": {
"node": ">=6.9.0"
} }
}, },
"node_modules/@babel/runtime/node_modules/regenerator-runtime": { "node_modules/@babel/runtime/node_modules/regenerator-runtime": {
@@ -3126,9 +3129,9 @@
} }
}, },
"node_modules/codemirror": { "node_modules/codemirror": {
"version": "5.62.2", "version": "5.62.3",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.3.tgz",
"integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw==" "integrity": "sha512-zZAyOfN8TU67ngqrxhOgtkSAGV9jSpN1snbl8elPtnh9Z5A11daR405+dhLzLnuXrwX0WCShWlybxPN3QC/9Pg=="
}, },
"node_modules/collection-visit": { "node_modules/collection-visit": {
"version": "1.0.0", "version": "1.0.0",
@@ -3856,14 +3859,15 @@
} }
}, },
"node_modules/eslint-plugin-react": { "node_modules/eslint-plugin-react": {
"version": "7.24.0", "version": "7.25.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.1.tgz",
"integrity": "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==", "integrity": "sha512-P4j9K1dHoFXxDNP05AtixcJEvIT6ht8FhYKsrkY0MPCPaUMYijhpWwNiRDZVtA8KFuZOkGSeft6QwH8KuVpJug==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"array-includes": "^3.1.3", "array-includes": "^3.1.3",
"array.prototype.flatmap": "^1.2.4", "array.prototype.flatmap": "^1.2.4",
"doctrine": "^2.1.0", "doctrine": "^2.1.0",
"estraverse": "^5.2.0",
"has": "^1.0.3", "has": "^1.0.3",
"jsx-ast-utils": "^2.4.1 || ^3.0.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
@@ -3893,6 +3897,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/eslint-plugin-react/node_modules/estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/eslint-plugin-react/node_modules/resolve": { "node_modules/eslint-plugin-react/node_modules/resolve": {
"version": "2.0.0-next.3", "version": "2.0.0-next.3",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
@@ -5961,14 +5974,14 @@
} }
}, },
"node_modules/marked": { "node_modules/marked": {
"version": "2.1.3", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz",
"integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", "integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA==",
"bin": { "bin": {
"marked": "bin/marked" "marked": "bin/marked"
}, },
"engines": { "engines": {
"node": ">= 10" "node": ">= 12"
} }
}, },
"node_modules/markedLegacy": { "node_modules/markedLegacy": {
@@ -6185,15 +6198,6 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/mini-create-react-context": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz",
"integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"tiny-warning": "^1.0.3"
}
},
"node_modules/minimalistic-assert": { "node_modules/minimalistic-assert": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -7287,12 +7291,42 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
"integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q=="
}, },
"node_modules/react-router": { "node_modules/react-router-dom": {
"version": "5.2.0", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.1.tgz",
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", "integrity": "sha512-xhFFkBGVcIVPbWM2KEYzED+nuHQPmulVa7sqIs3ESxzYd1pYg8N8rxPnQ4T2o1zu/2QeDUWcaqST131SO1LR3w==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.1.2", "@babel/runtime": "^7.12.13",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.2.1",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
},
"peerDependencies": {
"react": ">=15"
}
},
"node_modules/react-router-dom/node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"node_modules/react-router-dom/node_modules/path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"dependencies": {
"isarray": "0.0.1"
}
},
"node_modules/react-router-dom/node_modules/react-router": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz",
"integrity": "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==",
"dependencies": {
"@babel/runtime": "^7.12.13",
"history": "^4.9.0", "history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0", "hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1", "loose-envify": "^1.3.1",
@@ -7302,75 +7336,22 @@
"react-is": "^16.6.0", "react-is": "^16.6.0",
"tiny-invariant": "^1.0.2", "tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0" "tiny-warning": "^1.0.0"
},
"peerDependencies": {
"react": ">=15"
} }
}, },
"node_modules/react-router-dom": { "node_modules/react-router-dom/node_modules/react-router/node_modules/mini-create-react-context": {
"version": "5.2.0", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.1.2", "@babel/runtime": "^7.12.1",
"history": "^4.9.0", "tiny-warning": "^1.0.3"
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.2.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
}, },
"node_modules/react-router-dom/node_modules/prop-types": { "peerDependencies": {
"version": "15.7.2", "prop-types": "^15.0.0",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"node_modules/react-router-dom/node_modules/prop-types/node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/react-router/node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"node_modules/react-router/node_modules/path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"dependencies": {
"isarray": "0.0.1"
}
},
"node_modules/react-router/node_modules/prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"node_modules/react-router/node_modules/prop-types/node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
} }
}, },
"node_modules/read-only-stream": { "node_modules/read-only-stream": {
@@ -10616,9 +10597,9 @@
} }
}, },
"@babel/runtime": { "@babel/runtime": {
"version": "7.9.6", "version": "7.15.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz",
"integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==",
"requires": { "requires": {
"regenerator-runtime": "^0.13.4" "regenerator-runtime": "^0.13.4"
}, },
@@ -11828,9 +11809,9 @@
} }
}, },
"codemirror": { "codemirror": {
"version": "5.62.2", "version": "5.62.3",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.3.tgz",
"integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw==" "integrity": "sha512-zZAyOfN8TU67ngqrxhOgtkSAGV9jSpN1snbl8elPtnh9Z5A11daR405+dhLzLnuXrwX0WCShWlybxPN3QC/9Pg=="
}, },
"collection-visit": { "collection-visit": {
"version": "1.0.0", "version": "1.0.0",
@@ -12517,14 +12498,15 @@
} }
}, },
"eslint-plugin-react": { "eslint-plugin-react": {
"version": "7.24.0", "version": "7.25.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.1.tgz",
"integrity": "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==", "integrity": "sha512-P4j9K1dHoFXxDNP05AtixcJEvIT6ht8FhYKsrkY0MPCPaUMYijhpWwNiRDZVtA8KFuZOkGSeft6QwH8KuVpJug==",
"dev": true, "dev": true,
"requires": { "requires": {
"array-includes": "^3.1.3", "array-includes": "^3.1.3",
"array.prototype.flatmap": "^1.2.4", "array.prototype.flatmap": "^1.2.4",
"doctrine": "^2.1.0", "doctrine": "^2.1.0",
"estraverse": "^5.2.0",
"has": "^1.0.3", "has": "^1.0.3",
"jsx-ast-utils": "^2.4.1 || ^3.0.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
@@ -12545,6 +12527,12 @@
"esutils": "^2.0.2" "esutils": "^2.0.2"
} }
}, },
"estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true
},
"resolve": { "resolve": {
"version": "2.0.0-next.3", "version": "2.0.0-next.3",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
@@ -14071,9 +14059,9 @@
} }
}, },
"marked": { "marked": {
"version": "2.1.3", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz",
"integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==" "integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA=="
}, },
"markedLegacy": { "markedLegacy": {
"version": "npm:marked@0.3.19", "version": "npm:marked@0.3.19",
@@ -14247,15 +14235,6 @@
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
}, },
"mini-create-react-context": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz",
"integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==",
"requires": {
"@babel/runtime": "^7.5.5",
"tiny-warning": "^1.0.3"
}
},
"minimalistic-assert": { "minimalistic-assert": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -15108,19 +15087,16 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
"integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q=="
}, },
"react-router": { "react-router-dom": {
"version": "5.2.0", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.1.tgz",
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", "integrity": "sha512-xhFFkBGVcIVPbWM2KEYzED+nuHQPmulVa7sqIs3ESxzYd1pYg8N8rxPnQ4T2o1zu/2QeDUWcaqST131SO1LR3w==",
"requires": { "requires": {
"@babel/runtime": "^7.1.2", "@babel/runtime": "^7.12.13",
"history": "^4.9.0", "history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1", "loose-envify": "^1.3.1",
"mini-create-react-context": "^0.4.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react-is": "^16.6.0", "react-router": "5.2.1",
"tiny-invariant": "^1.0.2", "tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0" "tiny-warning": "^1.0.0"
}, },
@@ -15138,58 +15114,30 @@
"isarray": "0.0.1" "isarray": "0.0.1"
} }
}, },
"prop-types": { "react-router": {
"version": "15.7.2", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "integrity": "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==",
"requires": { "requires": {
"loose-envify": "^1.4.0", "@babel/runtime": "^7.12.13",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
},
"dependencies": {
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
}
}
}
}
},
"react-router-dom": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0", "history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1", "loose-envify": "^1.3.1",
"mini-create-react-context": "^0.4.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react-router": "5.2.0", "react-is": "^16.6.0",
"tiny-invariant": "^1.0.2", "tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0" "tiny-warning": "^1.0.0"
}, },
"dependencies": { "dependencies": {
"prop-types": { "mini-create-react-context": {
"version": "15.7.2", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
"requires": { "requires": {
"loose-envify": "^1.4.0", "@babel/runtime": "^7.12.1",
"object-assign": "^4.1.1", "tiny-warning": "^1.0.3"
"react-is": "^16.8.1"
},
"dependencies": {
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
} }
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "homebrewery", "name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown", "description": "Create authentic looking D&D homebrews using only markdown",
"version": "2.13.3", "version": "2.13.4",
"engines": { "engines": {
"node": "14.15.x" "node": "14.15.x"
}, },
@@ -46,7 +46,7 @@
"@babel/preset-react": "^7.14.5", "@babel/preset-react": "^7.14.5",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"codemirror": "^5.62.2", "codemirror": "^5.62.3",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
"create-react-class": "^15.7.0", "create-react-class": "^15.7.0",
"dedent-tabs": "^0.9.0", "dedent-tabs": "^0.9.0",
@@ -58,7 +58,7 @@
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
"less": "^3.13.1", "less": "^3.13.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"marked": "2.1.3", "marked": "3.0.2",
"markedLegacy": "npm:marked@^0.3.19", "markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.29.1", "moment": "^2.29.1",
"mongoose": "^5.13.7", "mongoose": "^5.13.7",
@@ -69,14 +69,14 @@
"react": "^16.14.0", "react": "^16.14.0",
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"react-frame-component": "4.1.3", "react-frame-component": "4.1.3",
"react-router-dom": "5.2.0", "react-router-dom": "5.2.1",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
"superagent": "^6.1.0", "superagent": "^6.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git" "vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-plugin-react": "^7.24.0", "eslint-plugin-react": "^7.25.1",
"pico-check": "^2.1.3" "pico-check": "^2.1.3"
} }
} }

View File

@@ -135,7 +135,8 @@ app.get('/v3_preview', async (req, res, next)=>{
app.get('/changelog', async (req, res, next)=>{ app.get('/changelog', async (req, res, next)=>{
const brew = { const brew = {
title : 'Changelog', title : 'Changelog',
text : changelogText text : changelogText,
renderer : 'V3'
}; };
req.brew = brew; req.brew = brew;
return next(); return next();

View File

@@ -17,7 +17,7 @@ GoogleActions = {
if(!account || !account.googleId){ // If not signed into Google if(!account || !account.googleId){ // If not signed into Google
const err = new Error('Not Signed In'); const err = new Error('Not Signed In');
err.status = 401; err.status = 401;
throw err; throw (err);
} }
const oAuth2Client = new google.auth.OAuth2( const oAuth2Client = new google.auth.OAuth2(
@@ -60,6 +60,7 @@ GoogleActions = {
.catch((err)=>{ .catch((err)=>{
console.log('Error searching Google Drive Folders'); console.log('Error searching Google Drive Folders');
console.error(err); console.error(err);
throw (err);
}); });
let folderId; let folderId;
@@ -69,8 +70,9 @@ GoogleActions = {
resource : fileMetadata resource : fileMetadata
}) })
.catch((err)=>{ .catch((err)=>{
console.log('Error creating google app folder'); console.log('Error creating Google Drive folder');
console.error(err); console.error(err);
throw (err);
}); });
folderId = obj.data.id; folderId = obj.data.id;
@@ -99,7 +101,9 @@ GoogleActions = {
q : 'mimeType != \'application/vnd.google-apps.folder\' and trashed = false' q : 'mimeType != \'application/vnd.google-apps.folder\' and trashed = false'
}) })
.catch((err)=>{ .catch((err)=>{
return console.error(`Error Listing Google Brews: ${err}`); console.log(`Error Listing Google Brews`);
console.error(err);
throw (err);
//TODO: Should break out here, but continues on for some reason. //TODO: Should break out here, but continues on for some reason.
}); });
@@ -116,7 +120,7 @@ GoogleActions = {
updatedAt : file.modifiedTime, updatedAt : file.modifiedTime,
gDrive : true, gDrive : true,
googleId : file.id, googleId : file.id,
pageCount : file.properties.pageCount,
title : file.properties.title, title : file.properties.title,
description : file.description, description : file.description,
views : file.properties.views, views : file.properties.views,
@@ -126,7 +130,6 @@ GoogleActions = {
systems : [] systems : []
}; };
}); });
return brews; return brews;
}, },
@@ -136,7 +139,7 @@ GoogleActions = {
const result = await drive.files.get({ fileId: id }) const result = await drive.files.get({ fileId: id })
.catch((err)=>{ .catch((err)=>{
console.log('error checking file exists...'); console.log('error checking file exists...');
console.log(err); console.error(err);
return false; return false;
}); });
@@ -151,19 +154,22 @@ GoogleActions = {
if(await GoogleActions.existsGoogleBrew(auth, brew.googleId) == true) { if(await GoogleActions.existsGoogleBrew(auth, brew.googleId) == true) {
await drive.files.update({ await drive.files.update({
fileId : brew.googleId, fileId : brew.googleId,
resource : { name : `${brew.title}.txt`, resource : {
name : `${brew.title}.txt`,
description : `${brew.description}`, description : `${brew.description}`,
properties : { title : brew.title, properties : {
title : brew.title,
published : brew.published, published : brew.published,
lastViewed : brew.lastViewed,
views : brew.views,
version : brew.version, version : brew.version,
renderer : brew.renderer, renderer : brew.renderer,
tags : brew.tags, tags : brew.tags,
systems : brew.systems.join() } systems : brew.systems.join()
}
}, },
media : { mimeType : 'text/plain', media : {
body : brew.text } mimeType : 'text/plain',
body : brew.text
}
}) })
.catch((err)=>{ .catch((err)=>{
console.log('Error saving to google'); console.log('Error saving to google');
@@ -195,6 +201,7 @@ GoogleActions = {
'editId' : nanoid(12), 'editId' : nanoid(12),
'title' : brew.title, 'title' : brew.title,
'views' : '0', 'views' : '0',
'pageCount' : brew.pageCount,
'renderer' : brew.renderer || 'legacy' 'renderer' : brew.renderer || 'legacy'
} }
}; };
@@ -230,6 +237,7 @@ GoogleActions = {
updatedAt : new Date(), updatedAt : new Date(),
gDrive : true, gDrive : true,
googleId : obj.data.id, googleId : obj.data.id,
pageCount : fileMetadata.properties.pageCount,
title : brew.title, title : brew.title,
description : brew.description, description : brew.description,
@@ -301,6 +309,7 @@ GoogleActions = {
createdAt : obj.data.createdTime, createdAt : obj.data.createdTime,
updatedAt : obj.data.modifiedTime, updatedAt : obj.data.modifiedTime,
lastViewed : obj.data.properties.lastViewed, lastViewed : obj.data.properties.lastViewed,
pageCount : obj.data.properties.pageCount,
views : parseInt(obj.data.properties.views) || 0, //brews with no view parameter will return undefined views : parseInt(obj.data.properties.views) || 0, //brews with no view parameter will return undefined
version : parseInt(obj.data.properties.version) || 0, version : parseInt(obj.data.properties.version) || 0,
renderer : obj.data.properties.renderer ? obj.data.properties.renderer : 'legacy', renderer : obj.data.properties.renderer ? obj.data.properties.renderer : 'legacy',
@@ -361,8 +370,13 @@ GoogleActions = {
await drive.files.update({ await drive.files.update({
fileId : brew.googleId, fileId : brew.googleId,
resource : { properties : { views : brew.views + 1, resource : {
lastViewed : new Date() } } modifiedTime : brew.updatedAt,
properties : {
views : brew.views + 1,
lastViewed : new Date()
}
}
}) })
.catch((err)=>{ .catch((err)=>{
console.log('Error updating Google views'); console.log('Error updating Google views');

View File

@@ -19,6 +19,15 @@ const getGoodBrewTitle = (text)=>{
.slice(0, MAX_TITLE_LENGTH); .slice(0, MAX_TITLE_LENGTH);
}; };
const excludePropsFromUpdate = (brew)=>{
// Remove undesired properties
const propsToExclude = ['views', 'lastViewed'];
for (const prop of propsToExclude) {
delete brew[prop];
};
return brew;
};
const mergeBrewText = (text, style)=>{ const mergeBrewText = (text, style)=>{
if(typeof style !== 'undefined') { if(typeof style !== 'undefined') {
text = `\`\`\`css\n` + text = `\`\`\`css\n` +
@@ -64,7 +73,8 @@ const newBrew = (req, res)=>{
const updateBrew = (req, res)=>{ const updateBrew = (req, res)=>{
HomebrewModel.get({ editId: req.params.id }) HomebrewModel.get({ editId: req.params.id })
.then((brew)=>{ .then((brew)=>{
brew = _.merge(brew, req.body); const updateBrew = excludePropsFromUpdate(req.body);
brew = _.merge(brew, updateBrew);
brew.text = mergeBrewText(brew.text, brew.style); brew.text = mergeBrewText(brew.text, brew.style);
// Compress brew text to binary before saving // Compress brew text to binary before saving
@@ -154,7 +164,7 @@ const updateGoogleBrew = async (req, res, next)=>{
try { oAuth2Client = GoogleActions.authCheck(req.account, res); } catch (err) { return res.status(err.status).send(err.message); } try { oAuth2Client = GoogleActions.authCheck(req.account, res); } catch (err) { return res.status(err.status).send(err.message); }
const brew = req.body; const brew = excludePropsFromUpdate(req.body);
brew.text = mergeBrewText(brew.text, brew.style); brew.text = mergeBrewText(brew.text, brew.style);
try { try {

View File

@@ -9,6 +9,7 @@ const HomebrewSchema = mongoose.Schema({
title : { type: String, default: '' }, title : { type: String, default: '' },
text : { type: String, default: '' }, text : { type: String, default: '' },
textBin : { type: Buffer }, textBin : { type: Buffer },
pageCount : { type: Number, default: 1 },
description : { type: String, default: '' }, description : { type: String, default: '' },
tags : { type: String, default: '' }, tags : { type: String, default: '' },

View File

@@ -65,13 +65,13 @@ const mustacheSpans = {
raw : raw, // Text to consume from the source raw : raw, // Text to consume from the source
text : text, // Additional custom properties text : text, // Additional custom properties
tags : tags, tags : tags,
tokens : this.inlineTokens(text) // inlineTokens to process **bold**, *italics*, etc. tokens : this.lexer.inlineTokens(text) // inlineTokens to process **bold**, *italics*, etc.
}; };
} }
} }
}, },
renderer(token) { renderer(token) {
return `<span class="inline-block${token.tags}>${this.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML return `<span class="inline-block${token.tags}>${this.parser.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML
} }
}; };
@@ -114,13 +114,13 @@ const mustacheDivs = {
raw : raw, // Text to consume from the source raw : raw, // Text to consume from the source
text : text, // Additional custom properties text : text, // Additional custom properties
tags : tags, tags : tags,
tokens : this.inline(this.blockTokens(text)) tokens : this.lexer.blockTokens(text)
}; };
} }
} }
}, },
renderer(token) { renderer(token) {
return `<div class="block${token.tags}>${this.parse(token.tokens)}</div>`; // parseInline to turn child tokens into HTML return `<div class="block${token.tags}>${this.parser.parse(token.tokens)}</div>`; // parseInline to turn child tokens into HTML
} }
}; };
@@ -149,7 +149,7 @@ const mustacheInjectInline = {
}, },
renderer(token) { renderer(token) {
token.type = token.originalType; token.type = token.originalType;
const text = this.parseInline([token]); const text = this.parser.parseInline([token]);
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text); const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
if(openingTag) { if(openingTag) {
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`; return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
@@ -182,7 +182,7 @@ const mustacheInjectBlock = {
}, },
renderer(token) { renderer(token) {
token.type = token.originalType; token.type = token.originalType;
const text = this.parse([token]); const text = this.parser.parse([token]);
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text); const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
if(openingTag) { if(openingTag) {
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`; return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
@@ -211,8 +211,8 @@ const definitionLists = {
const definitions = []; const definitions = [];
while (match = regex.exec(src)) { while (match = regex.exec(src)) {
definitions.push({ definitions.push({
dt : this.inlineTokens(match[1].trim()), dt : this.lexer.inlineTokens(match[1].trim()),
dd : this.inlineTokens(match[2].trim()) dd : this.lexer.inlineTokens(match[2].trim())
}); });
endIndex = regex.lastIndex; endIndex = regex.lastIndex;
} }
@@ -227,8 +227,8 @@ const definitionLists = {
renderer(token) { renderer(token) {
return `<dl> return `<dl>
${token.definitions.reduce((html, def)=>{ ${token.definitions.reduce((html, def)=>{
return `${html}<dt>${this.parseInline(def.dt)}</dt>` return `${html}<dt>${this.parser.parseInline(def.dt)}</dt>`
+ `<dd>${this.parseInline(def.dd)}</dd>\n`; + `<dd>${this.parser.parseInline(def.dd)}</dd>\n`;
}, '')} }, '')}
</dl>`; </dl>`;
} }
@@ -302,7 +302,7 @@ const spanTable = {
row = item.header[j]; row = item.header[j];
for (k = 0; k < row.length; k++) { for (k = 0; k < row.length; k++) {
row[k].tokens = []; row[k].tokens = [];
this.inlineTokens(row[k].text, row[k].tokens); this.lexer.inlineTokens(row[k].text, row[k].tokens);
} }
} }
@@ -312,7 +312,7 @@ const spanTable = {
row = item.rows[j]; row = item.rows[j];
for (k = 0; k < row.length; k++) { for (k = 0; k < row.length; k++) {
row[k].tokens = []; row[k].tokens = [];
this.inlineTokens(row[k].text, row[k].tokens); this.lexer.inlineTokens(row[k].text, row[k].tokens);
} }
} }
return item; return item;
@@ -329,7 +329,7 @@ const spanTable = {
output += `<tr>`; output += `<tr>`;
for (j = 0; j < row.length; j++) { for (j = 0; j < row.length; j++) {
cell = row[j]; cell = row[j];
text = this.parseInline(cell.tokens); text = this.parser.parseInline(cell.tokens);
output += getTableCell(text, cell, 'th', token.align[col]); output += getTableCell(text, cell, 'th', token.align[col]);
col += cell.colspan; col += cell.colspan;
} }
@@ -344,7 +344,7 @@ const spanTable = {
output += `<tr>`; output += `<tr>`;
for (j = 0; j < row.length; j++) { for (j = 0; j < row.length; j++) {
cell = row[j]; cell = row[j];
text = this.parseInline(cell.tokens); text = this.parser.parseInline(cell.tokens);
output += getTableCell(text, cell, 'td', token.align[col]); output += getTableCell(text, cell, 'td', token.align[col]);
col += cell.colspan; col += cell.colspan;
} }
@@ -527,7 +527,7 @@ const processStyleTags = (string)=>{
module.exports = { module.exports = {
marked : Markdown, marked : Markdown,
render : (rawBrewText)=>{ render : (rawBrewText)=>{
rawBrewText = rawBrewText.replace(/^\\column$/gm, `<div class='columnSplit'></div>`) rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`); .replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
return Markdown( return Markdown(
sanatizeScriptTags(rawBrewText), sanatizeScriptTags(rawBrewText),

View File

@@ -34,9 +34,9 @@ body {
letter-spacing : -0.02em; letter-spacing : -0.02em;
} }
} }
.useColumns(@multiplier : 1){ .useColumns(@multiplier : 1, @fillMode: balance){
column-count : 2; column-count : 2;
column-fill : auto; column-fill : @fillMode;
column-gap : 0.9cm; column-gap : 0.9cm;
column-width : 8cm * @multiplier; column-width : 8cm * @multiplier;
-webkit-column-count : 2; -webkit-column-count : 2;
@@ -46,6 +46,11 @@ body {
-webkit-column-gap : 0.9cm; -webkit-column-gap : 0.9cm;
-moz-column-gap : 0.9cm; -moz-column-gap : 0.9cm;
} }
.columnWrapper{
max-height : 100%;
column-span : all;
columns : inherit;
}
.page{ .page{
.useColumns(); .useColumns();
counter-increment : phb-page-numbers; counter-increment : phb-page-numbers;
@@ -55,9 +60,9 @@ body {
overflow : hidden; overflow : hidden;
height : 279.4mm; height : 279.4mm;
width : 215.9mm; width : 215.9mm;
padding : 1.4cm 1.9cm 1.7cm;
background-color : @background; background-color : @background;
background-image : @backgroundImage; background-image : @backgroundImage;
padding : 1.4cm 1.9cm 1.7cm;
font-family : BookInsanityRemake; font-family : BookInsanityRemake;
font-size : 0.34cm; font-size : 0.34cm;
text-rendering : optimizeLegibility; text-rendering : optimizeLegibility;
@@ -68,10 +73,13 @@ body {
// *****************************/ // *****************************/
p{ p{
overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
margin-bottom : 0.8em; display : block;
line-height : 1.3em; line-height : 1.3em;
&+* {
margin-top : 0.27cm;
}
&+p{ &+p{
margin-top : -0.8em; margin-top : 0;
} }
} }
ul{ ul{
@@ -121,7 +129,7 @@ body {
color : @headerText; color : @headerText;
} }
h1{ h1{
margin-bottom : 0.18cm; margin-bottom : 0.18cm; //Margin-bottom only because this is WIDE
column-span : all; column-span : all;
font-size : 0.89cm; font-size : 0.89cm;
-webkit-column-span : all; -webkit-column-span : all;
@@ -129,42 +137,41 @@ body {
&+p::first-letter{ &+p::first-letter{
float : left; float : left;
font-family : SolberaImitationRemake; font-family : SolberaImitationRemake;
line-height : 0.8em; line-height : 1em;
font-size: 3.5cm; font-size : 3.5cm;
padding-left: 40px; padding-left : 40px; //Allow background color to extend into margins
margin-left: -40px; margin-left : -40px;
padding-top:10px; margin-top :-0.3cm;
margin-top:-8px; padding-bottom :2px;
padding-bottom:10px; margin-bottom :-20px;
margin-bottom:-20px; background-image : linear-gradient(-45deg, #322814, #998250, #322814);
background-image: linear-gradient(-45deg, #322814, #998250, #322814); background-clip : text;
background-clip: text; -webkit-background-clip : text;
-webkit-background-clip: text; color : rgba(0, 0, 0, 0);
color: rgba(0, 0, 0, 0);
} }
&+p::first-line{ &+p::first-line{
font-variant : small-caps; font-variant : small-caps;
} }
} }
h2{ h2{
margin-top : 0px; //margin-top : 0px; //Font is misaligned. Shift up slightly
margin-bottom : 0.05cm; //margin-bottom : 0.05cm;
font-size : 0.75cm; font-size : 0.75cm;
} }
h3{ h3{
margin-top : -0.1cm; //margin-top : -0.1cm; //Font is misaligned. Shift up slightly
margin-bottom : 0.1cm; //margin-bottom : 0.1cm;
font-size : 0.575cm; font-size : 0.575cm;
border-bottom : 2px solid @headerUnderline; border-bottom : 2px solid @headerUnderline;
} }
h4{ h4{
margin-top : -0.02cm; //margin-top : -0.02cm; //Font is misaligned. Shift up slightly
margin-bottom : 0.02cm; //margin-bottom : 0.02cm;
font-size : 0.458cm; font-size : 0.458cm;
} }
h5{ h5{
margin-top : -0.02cm; //margin-top : -0.02cm; //Font is misaligned. Shift up slightly
margin-bottom : 0.02cm; //margin-bottom : 0.02cm;
font-family : ScalySansSmallCapsRemake; font-family : ScalySansSmallCapsRemake;
font-size : 0.423cm; font-size : 0.423cm;
font-weight : 900; font-weight : 900;
@@ -206,7 +213,6 @@ body {
border-width : 11px; border-width : 11px;
border-image : @noteBorderImage 12; border-image : @noteBorderImage 12;
border-image-outset : 9px 0px; border-image-outset : 9px 0px;
box-shadow : 1px 4px 14px #888;
position : absolute; position : absolute;
width : 100%; width : 100%;
height : 100%; height : 100%;
@@ -219,6 +225,7 @@ body {
margin-left : -0.1em; margin-left : -0.1em;
margin-right : -0.1em; margin-right : -0.1em;
background-color : @noteGreen; background-color : @noteGreen;
filter : drop-shadow(1px 4px 6px #888);
padding : 0.5em 0.6em; padding : 0.5em 0.6em;
& + * { & + * {
margin-top : 1.3em; margin-top : 1.3em;
@@ -239,7 +246,7 @@ body {
// ************************************/ // ************************************/
.descriptive{ .descriptive{
.useSansSerif(); .useSansSerif();
display : block-inline; display : inline-block;
margin-top : 1.4em; margin-top : 1.4em;
background-color : #faf7ea; background-color : #faf7ea;
font-family : ScalySansRemake; font-family : ScalySansRemake;
@@ -247,7 +254,7 @@ body {
border-width : 7px; border-width : 7px;
border-image : @descriptiveBoxImage 12 stretch; border-image : @descriptiveBoxImage 12 stretch;
border-image-outset : 4px; border-image-outset : 4px;
box-shadow : 0px 0px 6px #faf7ea; filter : drop-shadow(0 0 3px #faf7ea);
padding : 0.1em; padding : 0.1em;
& + * { & + * {
margin-top : 1.4em; margin-top : 1.4em;
@@ -329,7 +336,7 @@ body {
border-image-outset : 0px 2px; border-image-outset : 0px 2px;
background-blend-mode : overlay; background-blend-mode : overlay;
background-attachment : fixed; background-attachment : fixed;
box-shadow : 1px 4px 14px #888; filter : drop-shadow(1px 4px 6px #888);
padding : 4px 2px; padding : 4px 2px;
margin : 0px -6px 1em; margin : 0px -6px 1em;
} }
@@ -383,16 +390,19 @@ body {
dl { dl {
color : @headerText; color : @headerText;
} }
hr:last-of-type~dl{
color : inherit; // After the HRs, hanging indents remain black.
}
// Monster Ability table // Monster Ability table
hr + table:first-of-type{ hr + table:first-of-type{
margin : 0; margin : 0;
column-span : 1; column-span : none;
color : @headerText; color : @headerText;
background-color : transparent; background-color : transparent;
border-style : none; border-style : none;
border-image : none; border-image : none;
-webkit-column-span : 1; -webkit-column-span : none;
tr { tr {
background-color : transparent; background-color : transparent;
} }
@@ -404,7 +414,7 @@ body {
//Full Width //Full Width
.monster.wide{ .monster.wide{
.useColumns(0.96); .useColumns(0.96, @fillMode: balance);
} }
//***************************** //*****************************
@@ -501,6 +511,9 @@ body {
break-after : always; break-after : always;
-moz-column-break-after : always; -moz-column-break-after : always;
break-before : column; break-before : column;
&+* {
margin-top: 0;
}
} }
//Avoid breaking up //Avoid breaking up
blockquote,table{ blockquote,table{
@@ -561,11 +574,20 @@ body {
column-span : all; column-span : all;
-webkit-column-span : all; -webkit-column-span : all;
-moz-column-span : all; -moz-column-span : all;
display : block;
margin-bottom : 0.34cm;
&+* {
margin-top : 0;
}
} }
//***************************** //*****************************
// * CLASS TABLE // * CLASS TABLE
// *****************************/ // *****************************/
.page .classTable{ .page .classTable{
th[colspan]:not([rowspan]) {
white-space : nowrap;
}
&.frame {
margin-top : 25px; margin-top : 25px;
margin-bottom : 40px; margin-bottom : 40px;
border-collapse : separate; border-collapse : separate;
@@ -577,6 +599,7 @@ body {
border-image-slice : 150 200 150 200; border-image-slice : 150 200 150 200;
border-image-source : @frameBorderImage; border-image-source : @frameBorderImage;
border-image-width : 47px; border-image-width : 47px;
}
h5{ h5{
margin-bottom : 10px; margin-bottom : 10px;
} }
@@ -650,7 +673,7 @@ body {
} }
} }
&.wide{ &.wide{
.useColumns(0.96); .useColumns(0.96, @fillMode: balance);
} }
} }
@@ -665,7 +688,6 @@ body {
.inline-block { .inline-block {
display : inline-block; display : inline-block;
text-indent : initial; text-indent : initial;
line-height : 1.3em;
} }
div { div {
column-gap : 0.5cm; //Default spacing if a div uses multicolumns column-gap : 0.5cm; //Default spacing if a div uses multicolumns
@@ -680,15 +702,18 @@ body {
line-height : 1.3em; line-height : 1.3em;
padding-left : 1em; padding-left : 1em;
text-indent : -1em; text-indent : -1em;
} & + * {
dl + * {
margin-top : 0.28cm; margin-top : 0.28cm;
} }
dl + p { & + dl {
margin-top : 0.5em; margin-top : 0;
}
}
dl + * {
margin-top : 0.17cm;
} }
p + dl { p + dl {
margin-top: -0.5em; margin-top: 0.17cm;
} }
dt { dt {
display : inline; display : inline;

View File

@@ -231,11 +231,11 @@ body {
// Monster Ability table // Monster Ability table
hr+table{ hr+table{
margin : 0; margin : 0;
column-span : 1; column-span : none;
background-color : transparent; background-color : transparent;
border-style : none; border-style : none;
border-image : none; border-image : none;
-webkit-column-span : 1; -webkit-column-span : none;
tbody{ tbody{
tr:nth-child(odd), tr:nth-child(even){ tr:nth-child(odd), tr:nth-child(even){
background-color : transparent; background-color : transparent;
@@ -416,7 +416,7 @@ body {
// * DESCRIPTIVE TEXT BOX // * DESCRIPTIVE TEXT BOX
// ************************************/ // ************************************/
.phb .descriptive{ .phb .descriptive{
display : block-inline; display : inline-block;
margin-bottom : 1em; margin-bottom : 1em;
background-color : #faf7ea; background-color : #faf7ea;
font-family : ScalySans; font-family : ScalySans;