mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-24 18:32:41 +00:00
Merge branch 'master' into pr/1549
This commit is contained in:
13
changelog.md
13
changelog.md
@@ -6,6 +6,17 @@ h5 {
|
||||
|
||||
# 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
|
||||
|
||||
##### 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.
|
||||
- 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
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ const BrewRenderer = createClass({
|
||||
if(this.props.renderer == 'legacy') {
|
||||
pages = this.props.text.split('\\page');
|
||||
} else {
|
||||
pages = this.props.text.split(/^\\page/gm);
|
||||
pages = this.props.text.split(/^\\page$/gm);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -62,7 +62,7 @@ const BrewRenderer = createClass({
|
||||
if(this.props.renderer == 'legacy') {
|
||||
pages = this.props.text.split('\\page');
|
||||
} else {
|
||||
pages = this.props.text.split(/^\\page/gm);
|
||||
pages = this.props.text.split(/^\\page$/gm);
|
||||
}
|
||||
this.setState({
|
||||
pages : pages,
|
||||
@@ -130,8 +130,14 @@ const BrewRenderer = createClass({
|
||||
renderPage : function(pageText, index){
|
||||
if(this.props.renderer == 'legacy')
|
||||
return <div className='phb page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(pageText) }} key={index} />;
|
||||
else
|
||||
return <div className='phb3 page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: Markdown.render(pageText) }} key={index} />;
|
||||
else {
|
||||
pageText += `\n\\column\n `; //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(){
|
||||
|
||||
@@ -68,7 +68,9 @@ const Editor = createClass({
|
||||
},
|
||||
|
||||
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 cursorPos = this.refs.codeEditor.getCursorPosition();
|
||||
@@ -76,7 +78,7 @@ const Editor = createClass({
|
||||
|
||||
this.refs.codeEditor.setCursorPosition(cursorPos.line + injectText.split('\n').length, cursorPos.ch + injectText.length);
|
||||
|
||||
if(this.isText()) this.props.onTextChange(lines.join('\n'));
|
||||
if(this.isText()) this.props.onTextChange(lines.join('\n'));
|
||||
if(this.isStyle()) this.props.onStyleChange(lines.join('\n'));
|
||||
},
|
||||
|
||||
@@ -119,7 +121,7 @@ const Editor = createClass({
|
||||
|
||||
// New Codemirror styling for V3 renderer
|
||||
if(this.props.renderer == 'V3') {
|
||||
if(line.startsWith('\\page')){
|
||||
if(line.match(/^\\page$/)){
|
||||
codeMirror.addLineClass(lineNumber, 'background', 'pageLine');
|
||||
r.push(lineNumber);
|
||||
}
|
||||
|
||||
@@ -67,9 +67,6 @@
|
||||
.button(@silver);
|
||||
}
|
||||
small{
|
||||
position : absolute;
|
||||
bottom : -15px;
|
||||
left : 0px;
|
||||
font-size : 0.6em;
|
||||
font-style : italic;
|
||||
}
|
||||
|
||||
@@ -2,86 +2,77 @@ const _ = require('lodash');
|
||||
|
||||
const features = [
|
||||
'Astrological Botany',
|
||||
'Astrological Chemistry',
|
||||
'Biochemical Sorcery',
|
||||
'Civil Alchemy',
|
||||
'Consecrated Biochemistry',
|
||||
'Civil Divination',
|
||||
'Consecrated Augury',
|
||||
'Demonic Anthropology',
|
||||
'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',
|
||||
'Genetic Banishing',
|
||||
'Gunpowder Torturer',
|
||||
'Orbital Gravedigger',
|
||||
'Phased Linguist',
|
||||
'Mathematical Pharmacist',
|
||||
'Plasma Outlaw',
|
||||
'Gunslinger Corruptor',
|
||||
'Hermetic Geography',
|
||||
'Immunological Cultist',
|
||||
'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',
|
||||
'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge'];
|
||||
const classnames = ['Ackerman', 'Berserker-Typist', 'Concierge', 'Fishmonger',
|
||||
'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 getFeature = (level)=>{
|
||||
let res = [];
|
||||
if(_.includes([4, 6, 8, 12, 14, 16, 19], level+1)){
|
||||
res = ['Ability Score Improvement'];
|
||||
}
|
||||
res = _.union(res, _.sampleSize(features, _.sample([0, 1, 1, 1, 1, 1])));
|
||||
if(!res.length) return '─';
|
||||
return res.join(', ');
|
||||
const maxes = [4, 3, 3, 3, 3, 2, 2, 1, 1];
|
||||
|
||||
const drawSlots = function(Slots, rows, padding){
|
||||
let slots = Number(Slots);
|
||||
return _.times(rows, function(i){
|
||||
const max = maxes[i];
|
||||
if(slots < 1) return _.pad('—', padding);
|
||||
const res = _.min([max, slots]);
|
||||
slots -= res;
|
||||
return _.pad(res.toString(), padding);
|
||||
}).join(' | ');
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
full : function(){
|
||||
full : function(classes){
|
||||
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 spells = 1;
|
||||
let slots = 2;
|
||||
return `{{classTable,wide\n##### The ${classname}\n` +
|
||||
`| Level | Proficiency | Features | Cantrips | Spells | --- Spell Slots Per Level --- |||||||||\n`+
|
||||
`| ^| Bonus ^| ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th |\n`+
|
||||
`|:-----:|:-----------:|:---------|:--------:|:------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n${
|
||||
return `{{${classes}\n##### The ${classname}\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`+
|
||||
`|:-----:|:-----------:|:-------------|:--------:|:------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|\n${
|
||||
_.map(levels, function(levelName, level){
|
||||
const res = [
|
||||
levelName,
|
||||
`+${profBonus[level]}`,
|
||||
getFeature(level),
|
||||
cantrips,
|
||||
spells,
|
||||
drawSlots(slots)
|
||||
_.pad(levelName, 5),
|
||||
_.pad(`+${profBonus[level]}`, 2),
|
||||
_.padEnd(_.sample(features), 21),
|
||||
_.pad(cantrips.toString(), 8),
|
||||
_.pad(spells.toString(), 6),
|
||||
drawSlots(slots, 9, 2),
|
||||
].join(' | ');
|
||||
|
||||
cantrips += _.random(0, 1);
|
||||
@@ -92,24 +83,50 @@ module.exports = {
|
||||
}).join('\n')}\n}}\n\n`;
|
||||
},
|
||||
|
||||
half : function(){
|
||||
half : function(classes){
|
||||
const classname = _.sample(classnames);
|
||||
|
||||
let featureScore = 1;
|
||||
return `<div class='classTable'>\n##### The ${classname}\n` +
|
||||
`| Level | Proficiency Bonus | Features | ${_.sample(features)}|\n` +
|
||||
`|:---:|:---:|:---|:---:|\n${
|
||||
return `{{${classes}\n##### The ${classname}\n` +
|
||||
`| Level | Proficiency Bonus | Features | ${_.pad(_.sample(features), 21)} |\n` +
|
||||
`|:-----:|:-----------------:|:---------|:---------------------:|\n${
|
||||
_.map(levels, function(levelName, level){
|
||||
const res = [
|
||||
levelName,
|
||||
`+${profBonus[level]}`,
|
||||
getFeature(level),
|
||||
`+${featureScore}`
|
||||
_.pad(levelName, 5),
|
||||
_.pad(`+${profBonus[level]}`, 2),
|
||||
_.padEnd(_.sample(features), 23),
|
||||
_.pad(`+${featureScore}`, 21),
|
||||
].join(' | ');
|
||||
|
||||
featureScore += _.random(0, 1);
|
||||
|
||||
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`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,8 +44,8 @@ module.exports = [
|
||||
{{wide
|
||||
Everything in here will be extra wide. Tables, text, everything!
|
||||
Beware though, CSS columns can behave a bit weird sometimes. You may
|
||||
have to rely on the automatic column-break rather than \`\column\` if
|
||||
you mix columns and wide blocks on the same page.
|
||||
have to manually place column breaks with \`\column\` to make the
|
||||
surrounding text flow with this wide block the way you want.
|
||||
}}
|
||||
\n`
|
||||
},
|
||||
@@ -262,12 +262,32 @@ module.exports = [
|
||||
{
|
||||
name : 'Class 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',
|
||||
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',
|
||||
|
||||
@@ -305,7 +305,7 @@ module.exports = [
|
||||
name : 'Ink Friendly',
|
||||
icon : 'fas fa-tint',
|
||||
gen : dedent`
|
||||
/* Ink Friendly */',
|
||||
/* Ink Friendly */
|
||||
.phb, .phb blockquote, .phb hr+blockquote {
|
||||
background : white;
|
||||
box-shadow : 0px 0px 3px;
|
||||
|
||||
@@ -196,11 +196,14 @@ const EditPage = createClass({
|
||||
|
||||
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(transfer) {
|
||||
const res = await request
|
||||
.post('/api/newGoogle/')
|
||||
.send(this.state.brew)
|
||||
.send(brew)
|
||||
.catch((err)=>{
|
||||
console.log(err.status === 401
|
||||
? 'Not signed in!'
|
||||
@@ -211,7 +214,7 @@ const EditPage = createClass({
|
||||
if(!res) { return; }
|
||||
|
||||
console.log('Deleting Local Copy');
|
||||
await request.delete(`/api/${this.state.brew.editId}`)
|
||||
await request.delete(`/api/${brew.editId}`)
|
||||
.send()
|
||||
.catch((err)=>{
|
||||
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
|
||||
} else {
|
||||
const res = await request
|
||||
.put(`/api/updateGoogle/${this.state.brew.editId}`)
|
||||
.send(this.state.brew)
|
||||
.put(`/api/updateGoogle/${brew.editId}`)
|
||||
.send(brew)
|
||||
.catch((err)=>{
|
||||
console.log(err.status === 401
|
||||
? 'Not signed in!'
|
||||
@@ -236,14 +239,14 @@ const EditPage = createClass({
|
||||
} else {
|
||||
if(transfer) {
|
||||
const res = await request.post('/api')
|
||||
.send(this.state.brew)
|
||||
.send(brew)
|
||||
.catch((err)=>{
|
||||
console.log('Error creating Local Copy');
|
||||
this.setState({ errors: err });
|
||||
return;
|
||||
});
|
||||
|
||||
await request.get(`/api/removeGoogle/${this.state.brew.googleId}${this.state.brew.editId}`)
|
||||
await request.get(`/api/removeGoogle/${brew.googleId}${brew.editId}`)
|
||||
.send()
|
||||
.catch((err)=>{
|
||||
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
|
||||
} else {
|
||||
const res = await request
|
||||
.put(`/api/update/${this.state.brew.editId}`)
|
||||
.send(this.state.brew)
|
||||
.put(`/api/update/${brew.editId}`)
|
||||
.send(brew)
|
||||
.catch((err)=>{
|
||||
console.log('Error Updating Local Brew');
|
||||
this.setState({ errors: err });
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
.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
|
||||
|
||||
<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
|
||||
|
||||
@@ -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/).
|
||||
|
||||
|
||||
|
||||
<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
|
||||
|
||||
## Markdown+
|
||||
|
||||
@@ -161,6 +161,8 @@ const NewPage = createClass({
|
||||
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) {
|
||||
const res = await request
|
||||
.post('/api/newGoogle/')
|
||||
|
||||
@@ -37,20 +37,21 @@ const PrintPage = createClass({
|
||||
|
||||
renderPages : function(){
|
||||
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
|
||||
className='phb page'
|
||||
id={`p${index + 1}`}
|
||||
dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(page) }}
|
||||
dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(pageText) }}
|
||||
key={index} />;
|
||||
});
|
||||
} else {
|
||||
return _.map(this.state.brewText.split(/^\\page/gm), (page, index)=>{
|
||||
return <div
|
||||
className='phb3 page'
|
||||
id={`p${index + 1}`}
|
||||
dangerouslySetInnerHTML={{ __html: Markdown.render(page) }}
|
||||
key={index} />;
|
||||
return _.map(this.state.brewText.split(/^\\page$/gm), (pageText, index)=>{
|
||||
pageText += `\n\\column\n `; //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>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ const BrewItem = createClass({
|
||||
if(!this.props.brew.editId) return;
|
||||
|
||||
return <a onClick={this.deleteBrew}>
|
||||
<i className='fas fa-trash-alt' />
|
||||
<i className='fas fa-trash-alt' title='Delete' />
|
||||
</a>;
|
||||
},
|
||||
|
||||
@@ -61,7 +61,7 @@ const BrewItem = createClass({
|
||||
}
|
||||
|
||||
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>;
|
||||
},
|
||||
|
||||
@@ -74,7 +74,7 @@ const BrewItem = createClass({
|
||||
}
|
||||
|
||||
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>;
|
||||
},
|
||||
|
||||
@@ -87,7 +87,7 @@ const BrewItem = createClass({
|
||||
}
|
||||
|
||||
return <a href={`/download/${shareLink}`}>
|
||||
<i className='fas fa-download' />
|
||||
<i className='fas fa-download' title='Download' />
|
||||
</a>;
|
||||
},
|
||||
|
||||
@@ -99,31 +99,33 @@ const BrewItem = createClass({
|
||||
</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(){
|
||||
const brew = this.props.brew;
|
||||
return <div className='brewItem' title={this.getTooltipData()}>
|
||||
<h2>{brew.title}</h2>
|
||||
<p className='description'>{brew.description}</p>
|
||||
<hr />
|
||||
const dateFormatString = 'YYYY-MM-DD HH:mm:ss';
|
||||
|
||||
return <div className='brewItem'>
|
||||
<div className='text'>
|
||||
<h2>{brew.title}</h2>
|
||||
<p className='description'>{brew.description}</p>
|
||||
</div>
|
||||
<hr />
|
||||
<div className='info'>
|
||||
<span>
|
||||
<i className='fas fa-user' /> {brew.authors.join(', ')}
|
||||
</span>
|
||||
<span>
|
||||
<i className='fas fa-eye' /> {brew.views}
|
||||
<span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>
|
||||
<i className='fas fa-eye'/> {brew.views}
|
||||
</span>
|
||||
{brew.pageCount &&
|
||||
<span title={`Page count: ${brew.pageCount}`}>
|
||||
<i className='far fa-file' /> {brew.pageCount}
|
||||
</span>
|
||||
}
|
||||
<span>
|
||||
<i className='fas fa-sync-alt' /> {moment(brew.updatedAt).fromNow()}
|
||||
</span>
|
||||
{this.renderGoogleDriveIcon()}
|
||||
<br />
|
||||
<span title={`Authors:\n${brew.authors.join('\n')}`}>
|
||||
<i className='fas fa-user'/> {brew.authors.join(', ')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className='links'>
|
||||
|
||||
@@ -10,24 +10,28 @@
|
||||
min-height : 105px;
|
||||
margin-right : 15px;
|
||||
margin-bottom : 15px;
|
||||
padding : 5px 15px 5px 8px;
|
||||
padding : 5px 15px 2px 8px;
|
||||
padding-right : 15px;
|
||||
border : 1px solid #c9ad6a;
|
||||
border-radius : 5px;
|
||||
-webkit-column-break-inside : avoid;
|
||||
page-break-inside : avoid;
|
||||
break-inside : avoid;
|
||||
h4{
|
||||
margin-bottom : 5px;
|
||||
font-size : 2.2em;
|
||||
.text {
|
||||
min-height : 54px;
|
||||
h4{
|
||||
margin-bottom : 5px;
|
||||
font-size : 2.2em;
|
||||
}
|
||||
}
|
||||
.info{
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
position: initial;
|
||||
bottom: 2px;
|
||||
margin-bottom: 4px;
|
||||
font-family : ScalySans;
|
||||
font-size : 1.2em;
|
||||
&>span{
|
||||
display : float;
|
||||
margin-right : 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,9 @@ const UserPage = createClass({
|
||||
},
|
||||
getInitialState : function() {
|
||||
return {
|
||||
sortType : 'alpha',
|
||||
sortDir : 'asc'
|
||||
sortType : 'alpha',
|
||||
sortDir : 'asc',
|
||||
filterString : ''
|
||||
};
|
||||
},
|
||||
getUsernameWithS : function() {
|
||||
@@ -44,7 +45,7 @@ const UserPage = createClass({
|
||||
renderBrews : function(brews){
|
||||
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 <BrewItem brew={brew} key={idx}/>;
|
||||
@@ -52,6 +53,7 @@ const UserPage = createClass({
|
||||
},
|
||||
|
||||
sortBrewOrder : function(brew){
|
||||
if(!brew.title){brew.title = 'No Title';}
|
||||
const mapping = {
|
||||
'alpha' : _.deburr(brew.title.toLowerCase()),
|
||||
'created' : moment(brew.createdAt).format(),
|
||||
@@ -90,6 +92,26 @@ const UserPage = createClass({
|
||||
</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(){
|
||||
return <div className='sort-container'>
|
||||
<table>
|
||||
@@ -114,6 +136,7 @@ const UserPage = createClass({
|
||||
{`${(this.state.sortDir == 'asc' ? '\u25B2 ASC' : '\u25BC DESC')}`}
|
||||
</button>
|
||||
</td>
|
||||
{this.renderFilterOption()}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -121,7 +144,12 @@ const UserPage = createClass({
|
||||
},
|
||||
|
||||
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');
|
||||
});
|
||||
},
|
||||
|
||||
@@ -34,8 +34,9 @@
|
||||
font-family : 'Open Sans', sans-serif;
|
||||
position : fixed;
|
||||
top : 35px;
|
||||
left : calc(50vw - 408px);
|
||||
border : 2px solid #58180D;
|
||||
width : 675px;
|
||||
width : 800px;
|
||||
background-color : #EEE5CE;
|
||||
padding : 2px;
|
||||
text-align : center;
|
||||
@@ -52,6 +53,9 @@
|
||||
vertical-align : middle;
|
||||
tbody tr{
|
||||
background-color: transparent !important;
|
||||
i{
|
||||
padding-right : 5px
|
||||
}
|
||||
button{
|
||||
background-color : transparent;
|
||||
color : #58180D;
|
||||
|
||||
298
package-lock.json
generated
298
package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "homebrewery",
|
||||
"version": "2.13.3",
|
||||
"version": "2.13.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "2.13.3",
|
||||
"version": "2.13.4",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -15,7 +15,7 @@
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"body-parser": "^1.19.0",
|
||||
"classnames": "^2.3.1",
|
||||
"codemirror": "^5.62.2",
|
||||
"codemirror": "^5.62.3",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"create-react-class": "^15.7.0",
|
||||
"dedent-tabs": "^0.9.0",
|
||||
@@ -27,7 +27,7 @@
|
||||
"jwt-simple": "^0.5.6",
|
||||
"less": "^3.13.1",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "2.1.3",
|
||||
"marked": "3.0.2",
|
||||
"markedLegacy": "npm:marked@^0.3.19",
|
||||
"moment": "^2.29.1",
|
||||
"mongoose": "^5.13.7",
|
||||
@@ -38,14 +38,14 @@
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-frame-component": "4.1.3",
|
||||
"react-router-dom": "5.2.0",
|
||||
"react-router-dom": "5.2.1",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"superagent": "^6.1.0",
|
||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-react": "^7.24.0",
|
||||
"eslint-plugin-react": "^7.25.1",
|
||||
"pico-check": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1665,11 +1665,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz",
|
||||
"integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==",
|
||||
"version": "7.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz",
|
||||
"integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
|
||||
@@ -3126,9 +3129,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "5.62.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz",
|
||||
"integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw=="
|
||||
"version": "5.62.3",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.3.tgz",
|
||||
"integrity": "sha512-zZAyOfN8TU67ngqrxhOgtkSAGV9jSpN1snbl8elPtnh9Z5A11daR405+dhLzLnuXrwX0WCShWlybxPN3QC/9Pg=="
|
||||
},
|
||||
"node_modules/collection-visit": {
|
||||
"version": "1.0.0",
|
||||
@@ -3856,14 +3859,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react": {
|
||||
"version": "7.24.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz",
|
||||
"integrity": "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==",
|
||||
"version": "7.25.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.1.tgz",
|
||||
"integrity": "sha512-P4j9K1dHoFXxDNP05AtixcJEvIT6ht8FhYKsrkY0MPCPaUMYijhpWwNiRDZVtA8KFuZOkGSeft6QwH8KuVpJug==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.3",
|
||||
"array.prototype.flatmap": "^1.2.4",
|
||||
"doctrine": "^2.1.0",
|
||||
"estraverse": "^5.2.0",
|
||||
"has": "^1.0.3",
|
||||
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
@@ -3893,6 +3897,15 @@
|
||||
"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": {
|
||||
"version": "2.0.0-next.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
|
||||
@@ -5961,14 +5974,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz",
|
||||
"integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz",
|
||||
"integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA==",
|
||||
"bin": {
|
||||
"marked": "bin/marked"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/markedLegacy": {
|
||||
@@ -6185,15 +6198,6 @@
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"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",
|
||||
"integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q=="
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
||||
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.1.tgz",
|
||||
"integrity": "sha512-xhFFkBGVcIVPbWM2KEYzED+nuHQPmulVa7sqIs3ESxzYd1pYg8N8rxPnQ4T2o1zu/2QeDUWcaqST131SO1LR3w==",
|
||||
"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",
|
||||
"hoist-non-react-statics": "^3.1.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
@@ -7302,75 +7336,22 @@
|
||||
"react-is": "^16.6.0",
|
||||
"tiny-invariant": "^1.0.2",
|
||||
"tiny-warning": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/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==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"history": "^4.9.0",
|
||||
"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": {
|
||||
"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-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"
|
||||
"peerDependencies": {
|
||||
"react": ">=15"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"node_modules/react-router-dom/node_modules/react-router/node_modules/mini-create-react-context": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
|
||||
"integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
|
||||
"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"
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"tiny-warning": "^1.0.3"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.0.0",
|
||||
"react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-only-stream": {
|
||||
@@ -10616,9 +10597,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz",
|
||||
"integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==",
|
||||
"version": "7.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz",
|
||||
"integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
},
|
||||
@@ -11828,9 +11809,9 @@
|
||||
}
|
||||
},
|
||||
"codemirror": {
|
||||
"version": "5.62.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz",
|
||||
"integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw=="
|
||||
"version": "5.62.3",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.3.tgz",
|
||||
"integrity": "sha512-zZAyOfN8TU67ngqrxhOgtkSAGV9jSpN1snbl8elPtnh9Z5A11daR405+dhLzLnuXrwX0WCShWlybxPN3QC/9Pg=="
|
||||
},
|
||||
"collection-visit": {
|
||||
"version": "1.0.0",
|
||||
@@ -12517,14 +12498,15 @@
|
||||
}
|
||||
},
|
||||
"eslint-plugin-react": {
|
||||
"version": "7.24.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz",
|
||||
"integrity": "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==",
|
||||
"version": "7.25.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.1.tgz",
|
||||
"integrity": "sha512-P4j9K1dHoFXxDNP05AtixcJEvIT6ht8FhYKsrkY0MPCPaUMYijhpWwNiRDZVtA8KFuZOkGSeft6QwH8KuVpJug==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-includes": "^3.1.3",
|
||||
"array.prototype.flatmap": "^1.2.4",
|
||||
"doctrine": "^2.1.0",
|
||||
"estraverse": "^5.2.0",
|
||||
"has": "^1.0.3",
|
||||
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
@@ -12545,6 +12527,12 @@
|
||||
"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": {
|
||||
"version": "2.0.0-next.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
|
||||
@@ -14071,9 +14059,9 @@
|
||||
}
|
||||
},
|
||||
"marked": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz",
|
||||
"integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA=="
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-3.0.2.tgz",
|
||||
"integrity": "sha512-TMJQQ79Z0e3rJYazY0tIoMsFzteUGw9fB3FD+gzuIT3zLuG9L9ckIvUfF51apdJkcqc208jJN2KbtPbOvXtbjA=="
|
||||
},
|
||||
"markedLegacy": {
|
||||
"version": "npm:marked@0.3.19",
|
||||
@@ -14247,15 +14235,6 @@
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"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",
|
||||
"integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q=="
|
||||
},
|
||||
"react-router": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
||||
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
|
||||
"react-router-dom": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.1.tgz",
|
||||
"integrity": "sha512-xhFFkBGVcIVPbWM2KEYzED+nuHQPmulVa7sqIs3ESxzYd1pYg8N8rxPnQ4T2o1zu/2QeDUWcaqST131SO1LR3w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"history": "^4.9.0",
|
||||
"hoist-non-react-statics": "^3.1.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
"mini-create-react-context": "^0.4.0",
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-is": "^16.6.0",
|
||||
"react-router": "5.2.1",
|
||||
"tiny-invariant": "^1.0.2",
|
||||
"tiny-warning": "^1.0.0"
|
||||
},
|
||||
@@ -15138,58 +15114,30 @@
|
||||
"isarray": "0.0.1"
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||
"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==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.8.1"
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"history": "^4.9.0",
|
||||
"hoist-non-react-statics": "^3.1.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
"mini-create-react-context": "^0.4.0",
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-is": "^16.6.0",
|
||||
"tiny-invariant": "^1.0.2",
|
||||
"tiny-warning": "^1.0.0"
|
||||
},
|
||||
"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==",
|
||||
"mini-create-react-context": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
|
||||
"integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
|
||||
"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",
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"prop-types": {
|
||||
"version": "15.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"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"
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"tiny-warning": "^1.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
package.json
10
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "homebrewery",
|
||||
"description": "Create authentic looking D&D homebrews using only markdown",
|
||||
"version": "2.13.3",
|
||||
"version": "2.13.4",
|
||||
"engines": {
|
||||
"node": "14.15.x"
|
||||
},
|
||||
@@ -46,7 +46,7 @@
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"body-parser": "^1.19.0",
|
||||
"classnames": "^2.3.1",
|
||||
"codemirror": "^5.62.2",
|
||||
"codemirror": "^5.62.3",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"create-react-class": "^15.7.0",
|
||||
"dedent-tabs": "^0.9.0",
|
||||
@@ -58,7 +58,7 @@
|
||||
"jwt-simple": "^0.5.6",
|
||||
"less": "^3.13.1",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "2.1.3",
|
||||
"marked": "3.0.2",
|
||||
"markedLegacy": "npm:marked@^0.3.19",
|
||||
"moment": "^2.29.1",
|
||||
"mongoose": "^5.13.7",
|
||||
@@ -69,14 +69,14 @@
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-frame-component": "4.1.3",
|
||||
"react-router-dom": "5.2.0",
|
||||
"react-router-dom": "5.2.1",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"superagent": "^6.1.0",
|
||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-react": "^7.24.0",
|
||||
"eslint-plugin-react": "^7.25.1",
|
||||
"pico-check": "^2.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,8 +134,9 @@ app.get('/v3_preview', async (req, res, next)=>{
|
||||
//Changelog page
|
||||
app.get('/changelog', async (req, res, next)=>{
|
||||
const brew = {
|
||||
title : 'Changelog',
|
||||
text : changelogText
|
||||
title : 'Changelog',
|
||||
text : changelogText,
|
||||
renderer : 'V3'
|
||||
};
|
||||
req.brew = brew;
|
||||
return next();
|
||||
|
||||
@@ -17,7 +17,7 @@ GoogleActions = {
|
||||
if(!account || !account.googleId){ // If not signed into Google
|
||||
const err = new Error('Not Signed In');
|
||||
err.status = 401;
|
||||
throw err;
|
||||
throw (err);
|
||||
}
|
||||
|
||||
const oAuth2Client = new google.auth.OAuth2(
|
||||
@@ -60,6 +60,7 @@ GoogleActions = {
|
||||
.catch((err)=>{
|
||||
console.log('Error searching Google Drive Folders');
|
||||
console.error(err);
|
||||
throw (err);
|
||||
});
|
||||
|
||||
let folderId;
|
||||
@@ -69,8 +70,9 @@ GoogleActions = {
|
||||
resource : fileMetadata
|
||||
})
|
||||
.catch((err)=>{
|
||||
console.log('Error creating google app folder');
|
||||
console.log('Error creating Google Drive folder');
|
||||
console.error(err);
|
||||
throw (err);
|
||||
});
|
||||
|
||||
folderId = obj.data.id;
|
||||
@@ -99,7 +101,9 @@ GoogleActions = {
|
||||
q : 'mimeType != \'application/vnd.google-apps.folder\' and trashed = false'
|
||||
})
|
||||
.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.
|
||||
});
|
||||
|
||||
@@ -109,24 +113,23 @@ GoogleActions = {
|
||||
|
||||
const brews = obj.data.files.map((file)=>{
|
||||
return {
|
||||
text : '',
|
||||
shareId : file.properties.shareId,
|
||||
editId : file.properties.editId,
|
||||
createdAt : file.createdTime,
|
||||
updatedAt : file.modifiedTime,
|
||||
gDrive : true,
|
||||
googleId : file.id,
|
||||
|
||||
title : file.properties.title,
|
||||
description : file.description,
|
||||
text : '',
|
||||
shareId : file.properties.shareId,
|
||||
editId : file.properties.editId,
|
||||
createdAt : file.createdTime,
|
||||
updatedAt : file.modifiedTime,
|
||||
gDrive : true,
|
||||
googleId : file.id,
|
||||
pageCount : file.properties.pageCount,
|
||||
title : file.properties.title,
|
||||
description : file.description,
|
||||
views : file.properties.views,
|
||||
tags : '',
|
||||
published : file.properties.published ? file.properties.published == 'true' : false,
|
||||
authors : [req.account.username], //TODO: properly save and load authors to google drive
|
||||
systems : []
|
||||
};
|
||||
});
|
||||
|
||||
tags : '',
|
||||
published : file.properties.published ? file.properties.published == 'true' : false,
|
||||
authors : [req.account.username], //TODO: properly save and load authors to google drive
|
||||
systems : []
|
||||
};
|
||||
});
|
||||
return brews;
|
||||
},
|
||||
|
||||
@@ -136,7 +139,7 @@ GoogleActions = {
|
||||
const result = await drive.files.get({ fileId: id })
|
||||
.catch((err)=>{
|
||||
console.log('error checking file exists...');
|
||||
console.log(err);
|
||||
console.error(err);
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -151,19 +154,22 @@ GoogleActions = {
|
||||
if(await GoogleActions.existsGoogleBrew(auth, brew.googleId) == true) {
|
||||
await drive.files.update({
|
||||
fileId : brew.googleId,
|
||||
resource : { name : `${brew.title}.txt`,
|
||||
description : `${brew.description}`,
|
||||
properties : { title : brew.title,
|
||||
published : brew.published,
|
||||
lastViewed : brew.lastViewed,
|
||||
views : brew.views,
|
||||
version : brew.version,
|
||||
renderer : brew.renderer,
|
||||
tags : brew.tags,
|
||||
systems : brew.systems.join() }
|
||||
},
|
||||
media : { mimeType : 'text/plain',
|
||||
body : brew.text }
|
||||
resource : {
|
||||
name : `${brew.title}.txt`,
|
||||
description : `${brew.description}`,
|
||||
properties : {
|
||||
title : brew.title,
|
||||
published : brew.published,
|
||||
version : brew.version,
|
||||
renderer : brew.renderer,
|
||||
tags : brew.tags,
|
||||
systems : brew.systems.join()
|
||||
}
|
||||
},
|
||||
media : {
|
||||
mimeType : 'text/plain',
|
||||
body : brew.text
|
||||
}
|
||||
})
|
||||
.catch((err)=>{
|
||||
console.log('Error saving to google');
|
||||
@@ -191,11 +197,12 @@ GoogleActions = {
|
||||
'description' : `${brew.description}`,
|
||||
'parents' : [folderId],
|
||||
'properties' : { //AppProperties is not accessible
|
||||
'shareId' : nanoid(12),
|
||||
'editId' : nanoid(12),
|
||||
'title' : brew.title,
|
||||
'views' : '0',
|
||||
'renderer' : brew.renderer || 'legacy'
|
||||
'shareId' : nanoid(12),
|
||||
'editId' : nanoid(12),
|
||||
'title' : brew.title,
|
||||
'views' : '0',
|
||||
'pageCount' : brew.pageCount,
|
||||
'renderer' : brew.renderer || 'legacy'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -230,6 +237,7 @@ GoogleActions = {
|
||||
updatedAt : new Date(),
|
||||
gDrive : true,
|
||||
googleId : obj.data.id,
|
||||
pageCount : fileMetadata.properties.pageCount,
|
||||
|
||||
title : brew.title,
|
||||
description : brew.description,
|
||||
@@ -301,6 +309,7 @@ GoogleActions = {
|
||||
createdAt : obj.data.createdTime,
|
||||
updatedAt : obj.data.modifiedTime,
|
||||
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
|
||||
version : parseInt(obj.data.properties.version) || 0,
|
||||
renderer : obj.data.properties.renderer ? obj.data.properties.renderer : 'legacy',
|
||||
@@ -361,8 +370,13 @@ GoogleActions = {
|
||||
|
||||
await drive.files.update({
|
||||
fileId : brew.googleId,
|
||||
resource : { properties : { views : brew.views + 1,
|
||||
lastViewed : new Date() } }
|
||||
resource : {
|
||||
modifiedTime : brew.updatedAt,
|
||||
properties : {
|
||||
views : brew.views + 1,
|
||||
lastViewed : new Date()
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err)=>{
|
||||
console.log('Error updating Google views');
|
||||
|
||||
@@ -19,6 +19,15 @@ const getGoodBrewTitle = (text)=>{
|
||||
.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)=>{
|
||||
if(typeof style !== 'undefined') {
|
||||
text = `\`\`\`css\n` +
|
||||
@@ -64,7 +73,8 @@ const newBrew = (req, res)=>{
|
||||
const updateBrew = (req, res)=>{
|
||||
HomebrewModel.get({ editId: req.params.id })
|
||||
.then((brew)=>{
|
||||
brew = _.merge(brew, req.body);
|
||||
const updateBrew = excludePropsFromUpdate(req.body);
|
||||
brew = _.merge(brew, updateBrew);
|
||||
brew.text = mergeBrewText(brew.text, brew.style);
|
||||
|
||||
// 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); }
|
||||
|
||||
const brew = req.body;
|
||||
const brew = excludePropsFromUpdate(req.body);
|
||||
brew.text = mergeBrewText(brew.text, brew.style);
|
||||
|
||||
try {
|
||||
|
||||
@@ -4,11 +4,12 @@ const _ = require('lodash');
|
||||
const zlib = require('zlib');
|
||||
|
||||
const HomebrewSchema = mongoose.Schema({
|
||||
shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
|
||||
editId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
|
||||
title : { type: String, default: '' },
|
||||
text : { type: String, default: '' },
|
||||
textBin : { type: Buffer },
|
||||
shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
|
||||
editId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
|
||||
title : { type: String, default: '' },
|
||||
text : { type: String, default: '' },
|
||||
textBin : { type: Buffer },
|
||||
pageCount : { type: Number, default: 1 },
|
||||
|
||||
description : { type: String, default: '' },
|
||||
tags : { type: String, default: '' },
|
||||
|
||||
@@ -65,13 +65,13 @@ const mustacheSpans = {
|
||||
raw : raw, // Text to consume from the source
|
||||
text : text, // Additional custom properties
|
||||
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) {
|
||||
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
|
||||
text : text, // Additional custom properties
|
||||
tags : tags,
|
||||
tokens : this.inline(this.blockTokens(text))
|
||||
tokens : this.lexer.blockTokens(text)
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
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) {
|
||||
token.type = token.originalType;
|
||||
const text = this.parseInline([token]);
|
||||
const text = this.parser.parseInline([token]);
|
||||
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
|
||||
if(openingTag) {
|
||||
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
|
||||
@@ -182,7 +182,7 @@ const mustacheInjectBlock = {
|
||||
},
|
||||
renderer(token) {
|
||||
token.type = token.originalType;
|
||||
const text = this.parse([token]);
|
||||
const text = this.parser.parse([token]);
|
||||
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
|
||||
if(openingTag) {
|
||||
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
|
||||
@@ -211,8 +211,8 @@ const definitionLists = {
|
||||
const definitions = [];
|
||||
while (match = regex.exec(src)) {
|
||||
definitions.push({
|
||||
dt : this.inlineTokens(match[1].trim()),
|
||||
dd : this.inlineTokens(match[2].trim())
|
||||
dt : this.lexer.inlineTokens(match[1].trim()),
|
||||
dd : this.lexer.inlineTokens(match[2].trim())
|
||||
});
|
||||
endIndex = regex.lastIndex;
|
||||
}
|
||||
@@ -227,8 +227,8 @@ const definitionLists = {
|
||||
renderer(token) {
|
||||
return `<dl>
|
||||
${token.definitions.reduce((html, def)=>{
|
||||
return `${html}<dt>${this.parseInline(def.dt)}</dt>`
|
||||
+ `<dd>${this.parseInline(def.dd)}</dd>\n`;
|
||||
return `${html}<dt>${this.parser.parseInline(def.dt)}</dt>`
|
||||
+ `<dd>${this.parser.parseInline(def.dd)}</dd>\n`;
|
||||
}, '')}
|
||||
</dl>`;
|
||||
}
|
||||
@@ -302,7 +302,7 @@ const spanTable = {
|
||||
row = item.header[j];
|
||||
for (k = 0; k < row.length; k++) {
|
||||
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];
|
||||
for (k = 0; k < row.length; k++) {
|
||||
row[k].tokens = [];
|
||||
this.inlineTokens(row[k].text, row[k].tokens);
|
||||
this.lexer.inlineTokens(row[k].text, row[k].tokens);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
@@ -329,7 +329,7 @@ const spanTable = {
|
||||
output += `<tr>`;
|
||||
for (j = 0; j < row.length; j++) {
|
||||
cell = row[j];
|
||||
text = this.parseInline(cell.tokens);
|
||||
text = this.parser.parseInline(cell.tokens);
|
||||
output += getTableCell(text, cell, 'th', token.align[col]);
|
||||
col += cell.colspan;
|
||||
}
|
||||
@@ -344,7 +344,7 @@ const spanTable = {
|
||||
output += `<tr>`;
|
||||
for (j = 0; j < row.length; j++) {
|
||||
cell = row[j];
|
||||
text = this.parseInline(cell.tokens);
|
||||
text = this.parser.parseInline(cell.tokens);
|
||||
output += getTableCell(text, cell, 'td', token.align[col]);
|
||||
col += cell.colspan;
|
||||
}
|
||||
@@ -527,7 +527,7 @@ const processStyleTags = (string)=>{
|
||||
module.exports = {
|
||||
marked : Markdown,
|
||||
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`);
|
||||
return Markdown(
|
||||
sanatizeScriptTags(rawBrewText),
|
||||
|
||||
@@ -34,9 +34,9 @@ body {
|
||||
letter-spacing : -0.02em;
|
||||
}
|
||||
}
|
||||
.useColumns(@multiplier : 1){
|
||||
.useColumns(@multiplier : 1, @fillMode: balance){
|
||||
column-count : 2;
|
||||
column-fill : auto;
|
||||
column-fill : @fillMode;
|
||||
column-gap : 0.9cm;
|
||||
column-width : 8cm * @multiplier;
|
||||
-webkit-column-count : 2;
|
||||
@@ -46,6 +46,11 @@ body {
|
||||
-webkit-column-gap : 0.9cm;
|
||||
-moz-column-gap : 0.9cm;
|
||||
}
|
||||
.columnWrapper{
|
||||
max-height : 100%;
|
||||
column-span : all;
|
||||
columns : inherit;
|
||||
}
|
||||
.page{
|
||||
.useColumns();
|
||||
counter-increment : phb-page-numbers;
|
||||
@@ -55,9 +60,9 @@ body {
|
||||
overflow : hidden;
|
||||
height : 279.4mm;
|
||||
width : 215.9mm;
|
||||
padding : 1.4cm 1.9cm 1.7cm;
|
||||
background-color : @background;
|
||||
background-image : @backgroundImage;
|
||||
padding : 1.4cm 1.9cm 1.7cm;
|
||||
font-family : BookInsanityRemake;
|
||||
font-size : 0.34cm;
|
||||
text-rendering : optimizeLegibility;
|
||||
@@ -68,10 +73,13 @@ body {
|
||||
// *****************************/
|
||||
p{
|
||||
overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
|
||||
margin-bottom : 0.8em;
|
||||
display : block;
|
||||
line-height : 1.3em;
|
||||
&+* {
|
||||
margin-top : 0.27cm;
|
||||
}
|
||||
&+p{
|
||||
margin-top : -0.8em;
|
||||
margin-top : 0;
|
||||
}
|
||||
}
|
||||
ul{
|
||||
@@ -121,50 +129,49 @@ body {
|
||||
color : @headerText;
|
||||
}
|
||||
h1{
|
||||
margin-bottom : 0.18cm;
|
||||
margin-bottom : 0.18cm; //Margin-bottom only because this is WIDE
|
||||
column-span : all;
|
||||
font-size : 0.89cm;
|
||||
-webkit-column-span : all;
|
||||
-moz-column-span : all;
|
||||
&+p::first-letter{
|
||||
float : left;
|
||||
font-family : SolberaImitationRemake;
|
||||
line-height : 0.8em;
|
||||
font-size: 3.5cm;
|
||||
padding-left: 40px;
|
||||
margin-left: -40px;
|
||||
padding-top:10px;
|
||||
margin-top:-8px;
|
||||
padding-bottom:10px;
|
||||
margin-bottom:-20px;
|
||||
background-image: linear-gradient(-45deg, #322814, #998250, #322814);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: rgba(0, 0, 0, 0);
|
||||
float : left;
|
||||
font-family : SolberaImitationRemake;
|
||||
line-height : 1em;
|
||||
font-size : 3.5cm;
|
||||
padding-left : 40px; //Allow background color to extend into margins
|
||||
margin-left : -40px;
|
||||
margin-top :-0.3cm;
|
||||
padding-bottom :2px;
|
||||
margin-bottom :-20px;
|
||||
background-image : linear-gradient(-45deg, #322814, #998250, #322814);
|
||||
background-clip : text;
|
||||
-webkit-background-clip : text;
|
||||
color : rgba(0, 0, 0, 0);
|
||||
}
|
||||
&+p::first-line{
|
||||
font-variant : small-caps;
|
||||
}
|
||||
}
|
||||
h2{
|
||||
margin-top : 0px;
|
||||
margin-bottom : 0.05cm;
|
||||
//margin-top : 0px; //Font is misaligned. Shift up slightly
|
||||
//margin-bottom : 0.05cm;
|
||||
font-size : 0.75cm;
|
||||
}
|
||||
h3{
|
||||
margin-top : -0.1cm;
|
||||
margin-bottom : 0.1cm;
|
||||
//margin-top : -0.1cm; //Font is misaligned. Shift up slightly
|
||||
//margin-bottom : 0.1cm;
|
||||
font-size : 0.575cm;
|
||||
border-bottom : 2px solid @headerUnderline;
|
||||
}
|
||||
h4{
|
||||
margin-top : -0.02cm;
|
||||
margin-bottom : 0.02cm;
|
||||
//margin-top : -0.02cm; //Font is misaligned. Shift up slightly
|
||||
//margin-bottom : 0.02cm;
|
||||
font-size : 0.458cm;
|
||||
}
|
||||
h5{
|
||||
margin-top : -0.02cm;
|
||||
margin-bottom : 0.02cm;
|
||||
//margin-top : -0.02cm; //Font is misaligned. Shift up slightly
|
||||
//margin-bottom : 0.02cm;
|
||||
font-family : ScalySansSmallCapsRemake;
|
||||
font-size : 0.423cm;
|
||||
font-weight : 900;
|
||||
@@ -206,7 +213,6 @@ body {
|
||||
border-width : 11px;
|
||||
border-image : @noteBorderImage 12;
|
||||
border-image-outset : 9px 0px;
|
||||
box-shadow : 1px 4px 14px #888;
|
||||
position : absolute;
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
@@ -219,6 +225,7 @@ body {
|
||||
margin-left : -0.1em;
|
||||
margin-right : -0.1em;
|
||||
background-color : @noteGreen;
|
||||
filter : drop-shadow(1px 4px 6px #888);
|
||||
padding : 0.5em 0.6em;
|
||||
& + * {
|
||||
margin-top : 1.3em;
|
||||
@@ -239,7 +246,7 @@ body {
|
||||
// ************************************/
|
||||
.descriptive{
|
||||
.useSansSerif();
|
||||
display : block-inline;
|
||||
display : inline-block;
|
||||
margin-top : 1.4em;
|
||||
background-color : #faf7ea;
|
||||
font-family : ScalySansRemake;
|
||||
@@ -247,7 +254,7 @@ body {
|
||||
border-width : 7px;
|
||||
border-image : @descriptiveBoxImage 12 stretch;
|
||||
border-image-outset : 4px;
|
||||
box-shadow : 0px 0px 6px #faf7ea;
|
||||
filter : drop-shadow(0 0 3px #faf7ea);
|
||||
padding : 0.1em;
|
||||
& + * {
|
||||
margin-top : 1.4em;
|
||||
@@ -329,7 +336,7 @@ body {
|
||||
border-image-outset : 0px 2px;
|
||||
background-blend-mode : overlay;
|
||||
background-attachment : fixed;
|
||||
box-shadow : 1px 4px 14px #888;
|
||||
filter : drop-shadow(1px 4px 6px #888);
|
||||
padding : 4px 2px;
|
||||
margin : 0px -6px 1em;
|
||||
}
|
||||
@@ -383,16 +390,19 @@ body {
|
||||
dl {
|
||||
color : @headerText;
|
||||
}
|
||||
hr:last-of-type~dl{
|
||||
color : inherit; // After the HRs, hanging indents remain black.
|
||||
}
|
||||
|
||||
// Monster Ability table
|
||||
hr + table:first-of-type{
|
||||
margin : 0;
|
||||
column-span : 1;
|
||||
column-span : none;
|
||||
color : @headerText;
|
||||
background-color : transparent;
|
||||
border-style : none;
|
||||
border-image : none;
|
||||
-webkit-column-span : 1;
|
||||
-webkit-column-span : none;
|
||||
tr {
|
||||
background-color : transparent;
|
||||
}
|
||||
@@ -404,7 +414,7 @@ body {
|
||||
|
||||
//Full Width
|
||||
.monster.wide{
|
||||
.useColumns(0.96);
|
||||
.useColumns(0.96, @fillMode: balance);
|
||||
}
|
||||
|
||||
//*****************************
|
||||
@@ -501,6 +511,9 @@ body {
|
||||
break-after : always;
|
||||
-moz-column-break-after : always;
|
||||
break-before : column;
|
||||
&+* {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
//Avoid breaking up
|
||||
blockquote,table{
|
||||
@@ -561,22 +574,32 @@ body {
|
||||
column-span : all;
|
||||
-webkit-column-span : all;
|
||||
-moz-column-span : all;
|
||||
display : block;
|
||||
margin-bottom : 0.34cm;
|
||||
&+* {
|
||||
margin-top : 0;
|
||||
}
|
||||
}
|
||||
//*****************************
|
||||
// * CLASS TABLE
|
||||
// *****************************/
|
||||
.page .classTable{
|
||||
margin-top : 25px;
|
||||
margin-bottom : 40px;
|
||||
border-collapse : separate;
|
||||
background-color : white;
|
||||
border : initial;
|
||||
border-style : solid;
|
||||
border-image-outset : 25px 17px;
|
||||
border-image-repeat : stretch;
|
||||
border-image-slice : 150 200 150 200;
|
||||
border-image-source : @frameBorderImage;
|
||||
border-image-width : 47px;
|
||||
th[colspan]:not([rowspan]) {
|
||||
white-space : nowrap;
|
||||
}
|
||||
&.frame {
|
||||
margin-top : 25px;
|
||||
margin-bottom : 40px;
|
||||
border-collapse : separate;
|
||||
background-color : white;
|
||||
border : initial;
|
||||
border-style : solid;
|
||||
border-image-outset : 25px 17px;
|
||||
border-image-repeat : stretch;
|
||||
border-image-slice : 150 200 150 200;
|
||||
border-image-source : @frameBorderImage;
|
||||
border-image-width : 47px;
|
||||
}
|
||||
h5{
|
||||
margin-bottom : 10px;
|
||||
}
|
||||
@@ -650,7 +673,7 @@ body {
|
||||
}
|
||||
}
|
||||
&.wide{
|
||||
.useColumns(0.96);
|
||||
.useColumns(0.96, @fillMode: balance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,7 +688,6 @@ body {
|
||||
.inline-block {
|
||||
display : inline-block;
|
||||
text-indent : initial;
|
||||
line-height : 1.3em;
|
||||
}
|
||||
div {
|
||||
column-gap : 0.5cm; //Default spacing if a div uses multicolumns
|
||||
@@ -680,15 +702,18 @@ body {
|
||||
line-height : 1.3em;
|
||||
padding-left : 1em;
|
||||
text-indent : -1em;
|
||||
& + * {
|
||||
margin-top : 0.28cm;
|
||||
}
|
||||
& + dl {
|
||||
margin-top : 0;
|
||||
}
|
||||
}
|
||||
dl + * {
|
||||
margin-top : 0.28cm;
|
||||
}
|
||||
dl + p {
|
||||
margin-top : 0.5em;
|
||||
margin-top : 0.17cm;
|
||||
}
|
||||
p + dl {
|
||||
margin-top: -0.5em;
|
||||
margin-top: 0.17cm;
|
||||
}
|
||||
dt {
|
||||
display : inline;
|
||||
|
||||
@@ -231,11 +231,11 @@ body {
|
||||
// Monster Ability table
|
||||
hr+table{
|
||||
margin : 0;
|
||||
column-span : 1;
|
||||
column-span : none;
|
||||
background-color : transparent;
|
||||
border-style : none;
|
||||
border-image : none;
|
||||
-webkit-column-span : 1;
|
||||
-webkit-column-span : none;
|
||||
tbody{
|
||||
tr:nth-child(odd), tr:nth-child(even){
|
||||
background-color : transparent;
|
||||
@@ -416,7 +416,7 @@ body {
|
||||
// * DESCRIPTIVE TEXT BOX
|
||||
// ************************************/
|
||||
.phb .descriptive{
|
||||
display : block-inline;
|
||||
display : inline-block;
|
||||
margin-bottom : 1em;
|
||||
background-color : #faf7ea;
|
||||
font-family : ScalySans;
|
||||
|
||||
Reference in New Issue
Block a user