69
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 99
|
||||||
|
ignore:
|
||||||
|
- dependency-name: eslint
|
||||||
|
versions:
|
||||||
|
- 7.19.0
|
||||||
|
- 7.22.0
|
||||||
|
- 7.23.0
|
||||||
|
- 7.24.0
|
||||||
|
- dependency-name: "@babel/core"
|
||||||
|
versions:
|
||||||
|
- 7.12.13
|
||||||
|
- 7.12.16
|
||||||
|
- 7.12.17
|
||||||
|
- 7.13.13
|
||||||
|
- 7.13.14
|
||||||
|
- 7.13.15
|
||||||
|
- dependency-name: googleapis
|
||||||
|
versions:
|
||||||
|
- 68.0.0
|
||||||
|
- 70.0.0
|
||||||
|
- 71.0.0
|
||||||
|
- dependency-name: "@babel/preset-env"
|
||||||
|
versions:
|
||||||
|
- 7.12.13
|
||||||
|
- 7.12.16
|
||||||
|
- 7.12.17
|
||||||
|
- 7.13.0
|
||||||
|
- 7.13.12
|
||||||
|
- 7.13.8
|
||||||
|
- dependency-name: mongoose
|
||||||
|
versions:
|
||||||
|
- 5.11.14
|
||||||
|
- 5.11.15
|
||||||
|
- 5.11.16
|
||||||
|
- 5.11.17
|
||||||
|
- 5.11.18
|
||||||
|
- 5.11.19
|
||||||
|
- 5.12.1
|
||||||
|
- 5.12.2
|
||||||
|
- 5.12.3
|
||||||
|
- dependency-name: eslint-plugin-react
|
||||||
|
versions:
|
||||||
|
- 7.23.0
|
||||||
|
- 7.23.1
|
||||||
|
- dependency-name: query-string
|
||||||
|
versions:
|
||||||
|
- 7.0.0
|
||||||
|
- dependency-name: nanoid
|
||||||
|
versions:
|
||||||
|
- 3.1.22
|
||||||
|
- dependency-name: "@babel/preset-react"
|
||||||
|
versions:
|
||||||
|
- 7.13.13
|
||||||
|
- dependency-name: codemirror
|
||||||
|
versions:
|
||||||
|
- 5.59.3
|
||||||
|
- 5.60.0
|
||||||
|
- dependency-name: classnames
|
||||||
|
versions:
|
||||||
|
- 2.3.0
|
||||||
|
- dependency-name: marked
|
||||||
|
versions:
|
||||||
|
- 1.2.8
|
||||||
26
changelog.md
@@ -6,6 +6,11 @@ h5 {
|
|||||||
|
|
||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
|
### Saturday, 02/5/2021 - v2.11.2
|
||||||
|
|
||||||
|
- Fix for edge case where brews could accidentally transfer from Google Drive back to Homebrewery.
|
||||||
|
- Move cursor to end of snippet after insertion
|
||||||
|
|
||||||
### Saturday, 20/3/2021 - v2.11.1
|
### Saturday, 20/3/2021 - v2.11.1
|
||||||
|
|
||||||
- Warning when opening brew in your Google Drive trash
|
- Warning when opening brew in your Google Drive trash
|
||||||
@@ -52,10 +57,6 @@ h5 {
|
|||||||
### Monday, 19/10/2020 - v2.10.2
|
### Monday, 19/10/2020 - v2.10.2
|
||||||
- Fixed issue with "recent" item links not updating when transferring between Google Drive.
|
- Fixed issue with "recent" item links not updating when transferring between Google Drive.
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
```
|
|
||||||
|
|
||||||
### Monday, 12/10/2020 - v2.10.1
|
### Monday, 12/10/2020 - v2.10.1
|
||||||
- Fixed issue with users unable to create new brews
|
- Fixed issue with users unable to create new brews
|
||||||
- Fixing brews being lost when loaded via back button
|
- Fixing brews being lost when loaded via back button
|
||||||
@@ -73,6 +74,13 @@ h5 {
|
|||||||
### Wednesday, 20/05/2020 - v2.9.0
|
### Wednesday, 20/05/2020 - v2.9.0
|
||||||
- Major refactoring of site backend to work with updated dependencies for security (should be invisible to users)
|
- Major refactoring of site backend to work with updated dependencies for security (should be invisible to users)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
### Wednesday, 11/03/2020 - v2.8.2
|
### Wednesday, 11/03/2020 - v2.8.2
|
||||||
- Fixed delete button removing everyone's copy for brews with multiple authors
|
- Fixed delete button removing everyone's copy for brews with multiple authors
|
||||||
- Compressed homebrew text in database
|
- Compressed homebrew text in database
|
||||||
@@ -100,9 +108,6 @@ h5 {
|
|||||||
### Saturday, 22/04/2017 - v2.7.4
|
### Saturday, 22/04/2017 - v2.7.4
|
||||||
- Give ability to hide the render warning notification
|
- Give ability to hide the render warning notification
|
||||||
|
|
||||||
|
|
||||||
\page
|
|
||||||
|
|
||||||
### Friday, 03/03/2017 - v2.7.3
|
### Friday, 03/03/2017 - v2.7.3
|
||||||
- Increasing the range on the Partial Page Rendering for a quick-fix for it getting out of sync on long brews.
|
- Increasing the range on the Partial Page Rendering for a quick-fix for it getting out of sync on long brews.
|
||||||
|
|
||||||
@@ -144,9 +149,6 @@ h5 {
|
|||||||
- Added in a snippet for a split table
|
- Added in a snippet for a split table
|
||||||
- Added an account nav item to new page
|
- Added an account nav item to new page
|
||||||
|
|
||||||
```
|
|
||||||
```
|
|
||||||
|
|
||||||
### Sunday, 27/11/2016 - v2.5.1
|
### Sunday, 27/11/2016 - v2.5.1
|
||||||
- Fixed the column rendering on the new user page. Really should have tested that better
|
- Fixed the column rendering on the new user page. Really should have tested that better
|
||||||
- Added a hover tooltip to fully read the brew description
|
- Added a hover tooltip to fully read the brew description
|
||||||
@@ -167,6 +169,8 @@ h5 {
|
|||||||
- You can now print from a new page without saving
|
- You can now print from a new page without saving
|
||||||
- Added the ability to use ctrl+p and ctrl+s to print and save respectively.
|
- Added the ability to use ctrl+p and ctrl+s to print and save respectively.
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
### Monday, 07/11/2016
|
### Monday, 07/11/2016
|
||||||
- Added final touches to the html validator and updating the rest of the branch
|
- Added final touches to the html validator and updating the rest of the branch
|
||||||
- If anyone finds issues with the new HTML validator, please let me know. I hope this will bring a more consistent feel to Homebrewery rendering.
|
- If anyone finds issues with the new HTML validator, please let me know. I hope this will bring a more consistent feel to Homebrewery rendering.
|
||||||
@@ -186,8 +190,6 @@ h5 {
|
|||||||
- Fixed the noteblock overlapping into titles (thanks u/dsompura!)
|
- Fixed the noteblock overlapping into titles (thanks u/dsompura!)
|
||||||
- Fixed a bad search route in the admin panel (thanks u/SnappyTom!)
|
- Fixed a bad search route in the admin panel (thanks u/SnappyTom!)
|
||||||
|
|
||||||
\page
|
|
||||||
|
|
||||||
### Friday, 29/07/2016 - v2.2.7
|
### Friday, 29/07/2016 - v2.2.7
|
||||||
- Adding in descriptive note blocks. (Thanks calculuschild!)
|
- Adding in descriptive note blocks. (Thanks calculuschild!)
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ const Editor = createClass({
|
|||||||
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
|
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
|
||||||
|
|
||||||
this.handleTextChange(lines.join('\n'));
|
this.handleTextChange(lines.join('\n'));
|
||||||
this.refs.codeEditor.setCursorPosition(this.cursorPosition.line, this.cursorPosition.ch + injectText.length);
|
this.refs.codeEditor.setCursorPosition(this.cursorPosition.line + injectText.split('\n').length, this.cursorPosition.ch + injectText.length);
|
||||||
},
|
},
|
||||||
handgleToggle : function(){
|
handgleToggle : function(){
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ module.exports = {
|
|||||||
`- **Components:** ${components}`,
|
`- **Components:** ${components}`,
|
||||||
`- **Duration:** ${_.sample(['Until dispelled', '1 round', 'Instantaneous', 'Concentration, up to 10 minutes', '1 hour'])}`,
|
`- **Duration:** ${_.sample(['Until dispelled', '1 round', 'Instantaneous', 'Concentration, up to 10 minutes', '1 hour'])}`,
|
||||||
'',
|
'',
|
||||||
'A flame, equivalent in brightness to a torch, springs from from an object that you touch. ',
|
'A flame, equivalent in brightness to a torch, springs from an object that you touch. ',
|
||||||
'The effect look like a regular flame, but it creates no heat and doesn\'t use oxygen. ',
|
'The effect look like a regular flame, but it creates no heat and doesn\'t use oxygen. ',
|
||||||
'A *continual flame* can be covered or hidden but not smothered or quenched.',
|
'A *continual flame* can be covered or hidden but not smothered or quenched.',
|
||||||
'\n\n\n'
|
'\n\n\n'
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
const genList = function(list, max){
|
const genList = function(list, max){
|
||||||
return _.sampleSize(list, _.random(0, max)).join(', ') || 'None';
|
return _.sampleSize(list, _.random(0, max)).join(', ') || 'None';
|
||||||
@@ -86,7 +87,7 @@ const getAlignment = function(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getStats = function(){
|
const getStats = function(){
|
||||||
return `>|${_.times(6, function(){
|
return `|${_.times(6, function(){
|
||||||
const num = _.random(1, 20);
|
const num = _.random(1, 20);
|
||||||
const mod = Math.ceil(num/2 - 5);
|
const mod = Math.ceil(num/2 - 5);
|
||||||
return `${num} (${mod >= 0 ? `+${mod}` : mod})`;
|
return `${num} (${mod >= 0 ? `+${mod}` : mod})`;
|
||||||
@@ -95,12 +96,12 @@ const getStats = function(){
|
|||||||
|
|
||||||
const genAbilities = function(){
|
const genAbilities = function(){
|
||||||
return _.sample([
|
return _.sample([
|
||||||
'> ***Pack Tactics.*** These guys work together. Like super well, you don\'t even know.',
|
'***Pack Tactics.*** These guys work together like peanut butter and jelly.',
|
||||||
'> ***Fowl Appearance.*** While the creature remains motionless, it is indistinguishable from a normal chicken.',
|
'***Fowl Appearance.*** While the creature remains motionless, it is indistinguishable from a normal chicken.',
|
||||||
'> ***Onion Stench.*** Any creatures within 5 feet of this thing develops an irrational craving for onion rings.',
|
'***Onion Stench.*** Any creatures within 5 feet of this thing develops an irrational craving for onion rings.',
|
||||||
'> ***Enormous Nose.*** This creature gains advantage on any check involving putting things in its nose.',
|
'***Enormous Nose.*** This creature gains advantage on any check involving putting things in its nose.',
|
||||||
'> ***Sassiness.*** When questioned, this creature will talk back instead of answering.',
|
'***Sassiness.*** When questioned, this creature will talk back instead of answering.',
|
||||||
'> ***Big Jerk.*** Thinks he is just *waaaay* better than you.',
|
'***Big Jerk.*** Whenever this creature makes an attack, it starts telling you how much cooler it is than you.',
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -133,68 +134,37 @@ const genAction = function(){
|
|||||||
'Turnbuckle Roll'
|
'Turnbuckle Roll'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return `> ***${name}.*** *Melee Weapon Attack:* +4 to hit, reach 5ft., one target. *Hit* 5 (1d6 + 2) `;
|
return `***${name}.*** *Melee Weapon Attack:* +4 to hit, reach 5ft., one target. *Hit* 5 (1d6 + 2) `;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
full : function(){
|
monster : function(classes, genLines){
|
||||||
return `${[
|
return dedent`
|
||||||
'___',
|
{{${classes}
|
||||||
'___',
|
## ${getMonsterName()}
|
||||||
`> ## ${getMonsterName()}`,
|
*${getType()}, ${getAlignment()}*
|
||||||
`>*${getType()}, ${getAlignment()}*`,
|
___
|
||||||
'> ___',
|
: **Armor Class** : ${_.random(10, 20)} (chain mail, shield)
|
||||||
`> - **Armor Class** ${_.random(10, 20)}`,
|
: **Hit Points** : ${_.random(1, 150)}(1d4 + 5)
|
||||||
`> - **Hit Points** ${_.random(1, 150)}(1d4 + 5)`,
|
: **Speed** : ${_.random(0, 50)}ft.
|
||||||
`> - **Speed** ${_.random(0, 50)}ft.`,
|
___
|
||||||
'>___',
|
| STR | DEX | CON | INT | WIS | CHA |
|
||||||
'>|STR|DEX|CON|INT|WIS|CHA|',
|
|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|
|
||||||
'>|:---:|:---:|:---:|:---:|:---:|:---:|',
|
${getStats()}
|
||||||
getStats(),
|
___
|
||||||
'>___',
|
: **Condition Immunities** : ${genList(['groggy', 'swagged', 'weak-kneed', 'buzzed', 'groovy', 'melancholy', 'drunk'], 3)}
|
||||||
`> - **Condition Immunities** ${genList(['groggy', 'swagged', 'weak-kneed', 'buzzed', 'groovy', 'melancholy', 'drunk'], 3)}`,
|
: **Senses** : darkvision 60 ft., passive Perception ${_.random(3, 20)}
|
||||||
`> - **Senses** passive Perception ${_.random(3, 20)}`,
|
: **Languages** : ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}
|
||||||
`> - **Languages** ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}`,
|
: **Challenge** : ${_.random(0, 15)} (${_.random(10, 10000)} XP)
|
||||||
`> - **Challenge** ${_.random(0, 15)} (${_.random(10, 10000)} XP)`,
|
___
|
||||||
'> ___',
|
:
|
||||||
_.times(_.random(3, 6), function(){
|
${_.times(_.random(genLines, genLines + 2), function(){return genAbilities();}).join('\n\t\t\t\n\t\t\t')}
|
||||||
return genAbilities();
|
:
|
||||||
}).join('\n>\n'),
|
### Actions
|
||||||
'> ### Actions',
|
${_.times(_.random(genLines, genLines + 2), function(){return genAction();}).join('\n\t\t\t\n\t\t\t')}
|
||||||
_.times(_.random(4, 6), function(){
|
}}
|
||||||
return genAction();
|
\n`;
|
||||||
}).join('\n>\n'),
|
|
||||||
].join('\n')}\n\n\n`;
|
|
||||||
},
|
|
||||||
|
|
||||||
half : function(){
|
|
||||||
return `${[
|
|
||||||
'___',
|
|
||||||
`> ## ${getMonsterName()}`,
|
|
||||||
`>*${getType()}, ${getAlignment()}*`,
|
|
||||||
'> ___',
|
|
||||||
`> - **Armor Class** ${_.random(10, 20)}`,
|
|
||||||
`> - **Hit Points** ${_.random(1, 150)}(1d4 + 5)`,
|
|
||||||
`> - **Speed** ${_.random(0, 50)}ft.`,
|
|
||||||
'>___',
|
|
||||||
'>|STR|DEX|CON|INT|WIS|CHA|',
|
|
||||||
'>|:---:|:---:|:---:|:---:|:---:|:---:|',
|
|
||||||
getStats(),
|
|
||||||
'>___',
|
|
||||||
`> - **Condition Immunities** ${genList(['groggy', 'swagged', 'weak-kneed', 'buzzed', 'groovy', 'melancholy', 'drunk'], 3)}`,
|
|
||||||
`> - **Senses** passive Perception ${_.random(3, 20)}`,
|
|
||||||
`> - **Languages** ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}`,
|
|
||||||
`> - **Challenge** ${_.random(0, 15)} (${_.random(10, 10000)} XP)`,
|
|
||||||
'> ___',
|
|
||||||
_.times(_.random(2, 3), function(){
|
|
||||||
return genAbilities();
|
|
||||||
}).join('\n>\n'),
|
|
||||||
'> ### Actions',
|
|
||||||
_.times(_.random(1, 2), function(){
|
|
||||||
return genAction();
|
|
||||||
}).join('\n>\n'),
|
|
||||||
].join('\n')}\n\n\n`;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const MonsterBlockGen = require('./monsterblock.gen.js');
|
|||||||
const ClassFeatureGen = require('./classfeature.gen.js');
|
const ClassFeatureGen = require('./classfeature.gen.js');
|
||||||
const CoverPageGen = require('./coverpage.gen.js');
|
const CoverPageGen = require('./coverpage.gen.js');
|
||||||
const TableOfContentsGen = require('./tableOfContents.gen.js');
|
const TableOfContentsGen = require('./tableOfContents.gen.js');
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
@@ -151,15 +152,20 @@ module.exports = [
|
|||||||
].join('\n');
|
].join('\n');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name : 'Monster Stat Block (unframed)',
|
||||||
|
icon : 'fas fa-paw',
|
||||||
|
gen : MonsterBlockGen.monster('monster', 2),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name : 'Monster Stat Block',
|
name : 'Monster Stat Block',
|
||||||
icon : 'fas fa-spider',
|
icon : 'fas fa-spider',
|
||||||
gen : MonsterBlockGen.half,
|
gen : MonsterBlockGen.monster('monster,frame', 2),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Wide Monster Stat Block',
|
name : 'Wide Monster Stat Block',
|
||||||
icon : 'fas fa-dragon',
|
icon : 'fas fa-dragon',
|
||||||
gen : MonsterBlockGen.full,
|
gen : MonsterBlockGen.monster('monster,frame,wide', 4),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Cover Page',
|
name : 'Cover Page',
|
||||||
@@ -196,63 +202,61 @@ module.exports = [
|
|||||||
name : 'Table',
|
name : 'Table',
|
||||||
icon : 'fas fa-th-list',
|
icon : 'fas fa-th-list',
|
||||||
gen : function(){
|
gen : function(){
|
||||||
return [
|
return dedent`
|
||||||
'##### Cookie Tastiness',
|
##### Character Advancement
|
||||||
'| Tastiness | Cookie Type |',
|
| Experience Points | Level | Proficiency Bonus |
|
||||||
'|:----:|:-------------|',
|
|:------------------|:-----:|:-----------------:|
|
||||||
'| -5 | Raisin |',
|
| 0 | 1 | +2 |
|
||||||
'| 8th | Chocolate Chip |',
|
| 300 | 2 | +2 |
|
||||||
'| 11th | 2 or lower |',
|
| 900 | 3 | +2 |
|
||||||
'| 14th | 3 or lower |',
|
| 2,700 | 4 | +2 |
|
||||||
'| 17th | 4 or lower |\n\n',
|
| 6,500 | 5 | +3 |
|
||||||
].join('\n');
|
| 14,000 | 6 | +3 |
|
||||||
},
|
\n`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Wide Table',
|
name : 'Wide Table',
|
||||||
icon : 'fas fa-list',
|
icon : 'fas fa-list',
|
||||||
gen : function(){
|
gen : function(){
|
||||||
return [
|
return dedent`
|
||||||
'<div class=\'wide\'>',
|
{{wide
|
||||||
'##### Cookie Tastiness',
|
##### Weapons
|
||||||
'| Tastiness | Cookie Type |',
|
| Name | Cost | Damage | Weight | Properties |
|
||||||
'|:----:|:-------------|',
|
|:------------------------|:-----:|:----------------|--------:|:-----------|
|
||||||
'| -5 | Raisin |',
|
| *Simple Melee Weapons* | | | | |
|
||||||
'| 8th | Chocolate Chip |',
|
|   Club | 1 sp | 1d4 bludgeoning | 2 lb. | Light |
|
||||||
'| 11th | 2 or lower |',
|
|   Dagger | 2 gp | 1d4 piercing | 1 lb. | Finesse |
|
||||||
'| 14th | 3 or lower |',
|
|   Spear | 1 gp | 1d6 piercing | 3 lb. | Thrown |
|
||||||
'| 17th | 4 or lower |',
|
| *Simple Ranged Weapons* | | | | |
|
||||||
'</div>\n\n'
|
|   Dart | 5 cp | 1d4 piercig | 1/4 lb. | Finesse |
|
||||||
].join('\n');
|
|   Shortbow | 25 gp | 1d6 piercing | 2 lb. | Ammunition |
|
||||||
},
|
|   Sling | 1 sp | 1d4 bludgeoning | — | Ammunition |
|
||||||
|
}}
|
||||||
|
\n`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Split Table',
|
name : 'Split Table',
|
||||||
icon : 'fas fa-th-large',
|
icon : 'fas fa-th-large',
|
||||||
gen : function(){
|
gen : function(){
|
||||||
return [
|
return dedent`
|
||||||
'<div style=\'column-count:2\'>',
|
##### Typical Difficulty Classes
|
||||||
'| d10 | Damage Type |',
|
{{column-count="2"
|
||||||
'|:---:|:------------|',
|
| Task Difficulty | DC |
|
||||||
'| 1 | Acid |',
|
|:----------------|:--:|
|
||||||
'| 2 | Cold |',
|
| Very easy | 5 |
|
||||||
'| 3 | Fire |',
|
| Easy | 10 |
|
||||||
'| 4 | Force |',
|
| Medium | 15 |
|
||||||
'| 5 | Lightning |',
|
|
||||||
'',
|
| Task Difficulty | DC |
|
||||||
'```',
|
|:------------------|:--:|
|
||||||
'```',
|
| Hard | 20 |
|
||||||
'',
|
| Very hard | 25 |
|
||||||
'| d10 | Damage Type |',
|
| Nearly impossible | 30 |
|
||||||
'|:---:|:------------|',
|
}}
|
||||||
'| 6 | Necrotic |',
|
\n`;
|
||||||
'| 7 | Poison |',
|
}
|
||||||
'| 8 | Psychic |',
|
|
||||||
'| 9 | Radiant |',
|
|
||||||
'| 10 | Thunder |',
|
|
||||||
'</div>\n\n',
|
|
||||||
].join('\n');
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ module.exports = {
|
|||||||
`- **Components:** ${components}`,
|
`- **Components:** ${components}`,
|
||||||
`- **Duration:** ${_.sample(['Until dispelled', '1 round', 'Instantaneous', 'Concentration, up to 10 minutes', '1 hour'])}`,
|
`- **Duration:** ${_.sample(['Until dispelled', '1 round', 'Instantaneous', 'Concentration, up to 10 minutes', '1 hour'])}`,
|
||||||
'',
|
'',
|
||||||
'A flame, equivalent in brightness to a torch, springs from from an object that you touch. ',
|
'A flame, equivalent in brightness to a torch, springs from an object that you touch. ',
|
||||||
'The effect look like a regular flame, but it creates no heat and doesn\'t use oxygen. ',
|
'The effect look like a regular flame, but it creates no heat and doesn\'t use oxygen. ',
|
||||||
'A *continual flame* can be covered or hidden but not smothered or quenched.',
|
'A *continual flame* can be covered or hidden but not smothered or quenched.',
|
||||||
'\n\n\n'
|
'\n\n\n'
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ const EditPage = createClass({
|
|||||||
isSaving : false,
|
isSaving : false,
|
||||||
isPending : false,
|
isPending : false,
|
||||||
alertTrashedGoogleBrew : this.props.brew.trashed,
|
alertTrashedGoogleBrew : this.props.brew.trashed,
|
||||||
|
alertLoginToTransfer : false,
|
||||||
saveGoogle : this.props.brew.googleId ? true : false,
|
saveGoogle : this.props.brew.googleId ? true : false,
|
||||||
confirmGoogleTransfer : false,
|
confirmGoogleTransfer : false,
|
||||||
errors : null,
|
errors : null,
|
||||||
@@ -140,15 +141,25 @@ const EditPage = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleGoogleClick : function(){
|
handleGoogleClick : function(){
|
||||||
|
console.log('handlegoogleclick');
|
||||||
|
if(!global.account?.googleId) {
|
||||||
|
this.setState({
|
||||||
|
alertLoginToTransfer : true
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
confirmGoogleTransfer : !prevState.confirmGoogleTransfer
|
confirmGoogleTransfer : !prevState.confirmGoogleTransfer
|
||||||
}));
|
}));
|
||||||
this.clearErrors();
|
this.clearErrors();
|
||||||
},
|
},
|
||||||
|
|
||||||
closeAlerts : function(){
|
closeAlerts : function(event){
|
||||||
|
event.stopPropagation(); //Only handle click once so alert doesn't reopen
|
||||||
this.setState({
|
this.setState({
|
||||||
alertTrashedGoogleBrew : false
|
alertTrashedGoogleBrew : false,
|
||||||
|
alertLoginToTransfer : false,
|
||||||
|
confirmGoogleTransfer : false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -187,7 +198,7 @@ const EditPage = createClass({
|
|||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log(err.status === 401
|
console.log(err.status === 401
|
||||||
? 'Not signed in!'
|
? 'Not signed in!'
|
||||||
: 'Error Saving to Google!');
|
: 'Error Transferring to Google!');
|
||||||
this.setState({ errors: err, saveGoogle: false });
|
this.setState({ errors: err, saveGoogle: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -210,7 +221,7 @@ const EditPage = createClass({
|
|||||||
console.log(err.status === 401
|
console.log(err.status === 401
|
||||||
? 'Not signed in!'
|
? 'Not signed in!'
|
||||||
: 'Error Saving to Google!');
|
: 'Error Saving to Google!');
|
||||||
this.setState({ errors: err, saveGoogle: false });
|
this.setState({ errors: err });
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -260,13 +271,19 @@ const EditPage = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
renderGoogleDriveIcon : function(){
|
renderGoogleDriveIcon : function(){
|
||||||
if(this.state.saveGoogle) {
|
|
||||||
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
|
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
|
||||||
<img src={googleDriveActive} alt='googleDriveActive' />
|
{this.state.saveGoogle
|
||||||
|
? <img src={googleDriveActive} alt='googleDriveActive'/>
|
||||||
|
: <img src={googleDriveInactive} alt='googleDriveInactive'/>
|
||||||
|
}
|
||||||
|
|
||||||
{this.state.confirmGoogleTransfer &&
|
{this.state.confirmGoogleTransfer &&
|
||||||
<div className='errorContainer'>
|
<div className='errorContainer' onClick={this.closeAlerts}>
|
||||||
Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?<br />
|
{ this.state.saveGoogle
|
||||||
|
? `Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?`
|
||||||
|
: `Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?`
|
||||||
|
}
|
||||||
|
<br />
|
||||||
<div className='confirm' onClick={this.toggleGoogleStorage}>
|
<div className='confirm' onClick={this.toggleGoogleStorage}>
|
||||||
Yes
|
Yes
|
||||||
</div>
|
</div>
|
||||||
@@ -275,24 +292,23 @@ const EditPage = createClass({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</Nav.item>;
|
|
||||||
} else {
|
|
||||||
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
|
|
||||||
<img src={googleDriveInactive} alt='googleDriveInactive' />
|
|
||||||
|
|
||||||
{this.state.confirmGoogleTransfer &&
|
{this.state.alertLoginToTransfer &&
|
||||||
<div className='errorContainer'>
|
<div className='errorContainer' onClick={this.closeAlerts}>
|
||||||
Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?<br />
|
You must be signed in to a Google account to transfer
|
||||||
<div className='confirm' onClick={this.toggleGoogleStorage}>
|
between the homebrewery and Google Drive!
|
||||||
Yes
|
<a target='_blank' rel='noopener noreferrer'
|
||||||
|
href={`http://naturalcrit.com/login?redirect=${this.state.url}`}>
|
||||||
|
<div className='confirm'>
|
||||||
|
Sign In
|
||||||
</div>
|
</div>
|
||||||
|
</a>
|
||||||
<div className='deny'>
|
<div className='deny'>
|
||||||
No
|
Not Now
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</Nav.item>;
|
</Nav.item>;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderSaveButton : function(){
|
renderSaveButton : function(){
|
||||||
@@ -311,7 +327,7 @@ const EditPage = createClass({
|
|||||||
to save this to<br />Google Drive!<br />
|
to save this to<br />Google Drive!<br />
|
||||||
<a target='_blank' rel='noopener noreferrer'
|
<a target='_blank' rel='noopener noreferrer'
|
||||||
href={`http://naturalcrit.com/login?redirect=${this.state.url}`}>
|
href={`http://naturalcrit.com/login?redirect=${this.state.url}`}>
|
||||||
<div className='confirm' onClick={this.toggleGoogleStorage}>
|
<div className='confirm'>
|
||||||
Sign In
|
Sign In
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ const NewPage = createClass({
|
|||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
return {
|
return {
|
||||||
brew : {
|
brew : {
|
||||||
text : this.props.brew.text,
|
text : this.props.brew.text || '',
|
||||||
gDrive : false,
|
gDrive : false,
|
||||||
title : '',
|
title : this.props.brew.title || '',
|
||||||
description : '',
|
description : this.props.brew.description || '',
|
||||||
tags : '',
|
tags : this.props.brew.tags || '',
|
||||||
published : false,
|
published : false,
|
||||||
authors : [],
|
authors : [],
|
||||||
systems : []
|
systems : this.props.brew.systems || []
|
||||||
},
|
},
|
||||||
|
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
|
|||||||
BIN
client/homebrew/phbStyle/images/descriptiveBorder.png
Normal file
|
After Width: | Height: | Size: 311 B |
BIN
client/homebrew/phbStyle/images/footerAccent.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
client/homebrew/phbStyle/images/frameBorder.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
client/homebrew/phbStyle/images/monsterBorderFancy.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
client/homebrew/phbStyle/images/monsterBorderLegacy.png
Normal file
|
After Width: | Height: | Size: 135 B |
BIN
client/homebrew/phbStyle/images/noteBorder.png
Normal file
|
After Width: | Height: | Size: 274 B |
BIN
client/homebrew/phbStyle/images/parchmentBackground.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
client/homebrew/phbStyle/images/parchmentBackgroundGrayscale.jpg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
client/homebrew/phbStyle/images/redTriangle.png
Normal file
|
After Width: | Height: | Size: 864 B |
@@ -7,7 +7,7 @@
|
|||||||
@headerUnderline : #c9ad6a;
|
@headerUnderline : #c9ad6a;
|
||||||
@horizontalRule : #9c2b1b;
|
@horizontalRule : #9c2b1b;
|
||||||
@headerText : #58180D;
|
@headerText : #58180D;
|
||||||
@monsterStatBackground : #FDF1DC;
|
@monsterStatBackground : #EEDBAB;
|
||||||
@page { margin: 0; }
|
@page { margin: 0; }
|
||||||
body {
|
body {
|
||||||
counter-reset : phb-page-numbers;
|
counter-reset : phb-page-numbers;
|
||||||
@@ -17,7 +17,11 @@ body {
|
|||||||
}
|
}
|
||||||
.useSansSerif(){
|
.useSansSerif(){
|
||||||
font-family : ScalySansRemake;
|
font-family : ScalySansRemake;
|
||||||
font-size : 10pt;
|
font-size : 0.325cm;
|
||||||
|
line-height : 1.2em;
|
||||||
|
p,dl,ul {
|
||||||
|
line-height : 1.2em;
|
||||||
|
}
|
||||||
em{
|
em{
|
||||||
font-style : italic;
|
font-style : italic;
|
||||||
}
|
}
|
||||||
@@ -29,14 +33,14 @@ body {
|
|||||||
.useColumns(@multiplier : 1){
|
.useColumns(@multiplier : 1){
|
||||||
column-count : 2;
|
column-count : 2;
|
||||||
column-fill : auto;
|
column-fill : auto;
|
||||||
column-gap : 1cm;
|
column-gap : 0.9cm;
|
||||||
column-width : 8cm * @multiplier;
|
column-width : 8cm * @multiplier;
|
||||||
-webkit-column-count : 2;
|
-webkit-column-count : 2;
|
||||||
-moz-column-count : 2;
|
-moz-column-count : 2;
|
||||||
-webkit-column-width : 8cm * @multiplier;
|
-webkit-column-width : 8cm * @multiplier;
|
||||||
-moz-column-width : 8cm * @multiplier;
|
-moz-column-width : 8cm * @multiplier;
|
||||||
-webkit-column-gap : 1cm;
|
-webkit-column-gap : 0.9cm;
|
||||||
-moz-column-gap : 1cm;
|
-moz-column-gap : 0.9cm;
|
||||||
}
|
}
|
||||||
.phb3{
|
.phb3{
|
||||||
.useColumns();
|
.useColumns();
|
||||||
@@ -47,11 +51,11 @@ body {
|
|||||||
overflow : hidden;
|
overflow : hidden;
|
||||||
height : 279.4mm;
|
height : 279.4mm;
|
||||||
width : 215.9mm;
|
width : 215.9mm;
|
||||||
padding : 1.0cm 1.7cm 1.5cm;
|
padding : 1.4cm 1.9cm 1.7cm;
|
||||||
background-color : @background;
|
background-color : @background;
|
||||||
background-image : @backgroundImage;
|
background-image : @backgroundImage;
|
||||||
font-family : BookInsanityRemake;
|
font-family : BookInsanityRemake;
|
||||||
font-size : 0.317cm;
|
font-size : 0.34cm;
|
||||||
text-rendering : optimizeLegibility;
|
text-rendering : optimizeLegibility;
|
||||||
page-break-before : always;
|
page-break-before : always;
|
||||||
page-break-after : always;
|
page-break-after : always;
|
||||||
@@ -59,11 +63,11 @@ body {
|
|||||||
// * BASE
|
// * BASE
|
||||||
// *****************************/
|
// *****************************/
|
||||||
p{
|
p{
|
||||||
overflow-wrap : break-word;
|
overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
|
||||||
padding-top : 0em;
|
margin-bottom : 1em;
|
||||||
line-height : 1.3em;
|
line-height : 1.3em;
|
||||||
&+p{
|
&+p{
|
||||||
padding-top : 0em;
|
margin-top : -1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ul{
|
ul{
|
||||||
@@ -89,7 +93,7 @@ body {
|
|||||||
}
|
}
|
||||||
strong{
|
strong{
|
||||||
font-weight : bold;
|
font-weight : bold;
|
||||||
letter-spacing : 0.03em;
|
letter-spacing : -0.02em;
|
||||||
}
|
}
|
||||||
em{
|
em{
|
||||||
font-style : italic;
|
font-style : italic;
|
||||||
@@ -108,15 +112,14 @@ body {
|
|||||||
// * HEADERS
|
// * HEADERS
|
||||||
// *****************************/
|
// *****************************/
|
||||||
h1,h2,h3,h4{
|
h1,h2,h3,h4{
|
||||||
margin-top : 0.2em;
|
|
||||||
margin-bottom : 0.2em;
|
|
||||||
font-family : MrEavesRemake;
|
font-family : MrEavesRemake;
|
||||||
font-weight : 800;
|
font-weight : 800;
|
||||||
color : @headerText;
|
color : @headerText;
|
||||||
}
|
}
|
||||||
h1{
|
h1{
|
||||||
|
margin-bottom : 0.18cm;
|
||||||
column-span : all;
|
column-span : all;
|
||||||
font-size : 0.987cm;
|
font-size : 0.89cm;
|
||||||
-webkit-column-span : all;
|
-webkit-column-span : all;
|
||||||
-moz-column-span : all;
|
-moz-column-span : all;
|
||||||
&+p::first-letter{
|
&+p::first-letter{
|
||||||
@@ -136,23 +139,28 @@ body {
|
|||||||
color: rgba(0, 0, 0, 0);
|
color: rgba(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
&+p::first-line{
|
&+p::first-line{
|
||||||
font-size : .385cm;
|
|
||||||
font-variant : small-caps;
|
font-variant : small-caps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h2{
|
h2{
|
||||||
font-size : 0.705cm;
|
margin-top : 0px;
|
||||||
|
margin-bottom : 0.05cm;
|
||||||
|
font-size : 0.75cm;
|
||||||
}
|
}
|
||||||
h3{
|
h3{
|
||||||
font-size : 0.529cm;
|
margin-top : -0.1cm;
|
||||||
|
margin-bottom : 0.1cm;
|
||||||
|
font-size : 0.575cm;
|
||||||
border-bottom : 2px solid @headerUnderline;
|
border-bottom : 2px solid @headerUnderline;
|
||||||
}
|
}
|
||||||
h4{
|
h4{
|
||||||
margin-bottom : 0.00em;
|
margin-top : -0.02cm;
|
||||||
|
margin-bottom : 0.02cm;
|
||||||
font-size : 0.458cm;
|
font-size : 0.458cm;
|
||||||
}
|
}
|
||||||
h5{
|
h5{
|
||||||
margin-bottom : 0.2em;
|
margin-top : -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;
|
||||||
@@ -164,21 +172,18 @@ body {
|
|||||||
.useSansSerif();
|
.useSansSerif();
|
||||||
width : 100%;
|
width : 100%;
|
||||||
margin-bottom : 1em;
|
margin-bottom : 1em;
|
||||||
font-size : 10pt;
|
|
||||||
thead{
|
thead{
|
||||||
display: table-row-group;
|
display: table-row-group;
|
||||||
font-weight : 800;
|
font-weight : 800;
|
||||||
th{
|
th{
|
||||||
vertical-align : bottom;
|
vertical-align : bottom;
|
||||||
padding-bottom : 0.3em;
|
padding : 0.14em 0.4em;
|
||||||
padding-right : 0.1em;
|
|
||||||
padding-left : 0.1em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody{
|
tbody{
|
||||||
tr{
|
tr{
|
||||||
td{
|
td{
|
||||||
padding : 0.3em 0.1em;
|
padding : 0.14em 0.4em;
|
||||||
}
|
}
|
||||||
&:nth-child(odd){
|
&:nth-child(odd){
|
||||||
background-color : @noteGreen;
|
background-color : @noteGreen;
|
||||||
@@ -213,66 +218,101 @@ body {
|
|||||||
//*****************************
|
//*****************************
|
||||||
// * MONSTER STAT BLOCK
|
// * MONSTER STAT BLOCK
|
||||||
// *****************************/
|
// *****************************/
|
||||||
hr+blockquote{
|
|
||||||
position : relative;
|
.monster {
|
||||||
padding-top : 15px;
|
&.frame {
|
||||||
background-color : @monsterStatBackground;
|
|
||||||
border-style : solid;
|
border-style : solid;
|
||||||
border-width : 10px;
|
border-width : 7px 6px;
|
||||||
border-image : @monsterBorderImage 10;
|
background-color : @monsterStatBackground;
|
||||||
|
background-image : @monsterBlockBackground;
|
||||||
|
border-image : @monsterBorderImage 14 round;
|
||||||
|
border-image-outset : 0px 2px;
|
||||||
|
background-blend-mode : overlay;
|
||||||
|
background-attachment : fixed;
|
||||||
|
box-shadow : 1px 4px 14px #888;
|
||||||
|
padding : 4px 2px;
|
||||||
|
margin : 0px -6px 1em;
|
||||||
|
}
|
||||||
|
.useSansSerif();
|
||||||
|
//-webkit-transform : translateZ(0); //Prevents shadows from breaking across columns, but breaks internal columns...
|
||||||
|
position : relative;
|
||||||
|
padding : 0px;
|
||||||
|
margin-bottom : 1em;
|
||||||
|
|
||||||
|
p{
|
||||||
|
margin-bottom : 0.3cm;
|
||||||
|
}
|
||||||
|
p+p {
|
||||||
|
margin-top : 0; //May not be needed
|
||||||
|
text-indent : 0;
|
||||||
|
}
|
||||||
|
p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Headers
|
||||||
h2{
|
h2{
|
||||||
margin-top : -8px;
|
font-size : 0.62cm;
|
||||||
margin-bottom : 0px;
|
line-height : 1em;
|
||||||
&+p{
|
margin : 0;
|
||||||
padding-bottom : 0px;
|
&+p {
|
||||||
|
font-size : 0.304cm; //Monster size and type subtext
|
||||||
|
margin-bottom : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h3{
|
h3{
|
||||||
font-family : ScalySansRemake;
|
font-family : ScalySansRemake;
|
||||||
font-weight : 400;
|
font-weight : 800;
|
||||||
border-bottom : 1px solid @headerText;
|
font-variant : small-caps;
|
||||||
}
|
border-bottom : 2px solid @headerText;
|
||||||
hr+ul{
|
margin-top : 0.05cm;
|
||||||
color : @headerText;
|
padding-bottom : 0.05cm;
|
||||||
}
|
|
||||||
ul{
|
|
||||||
.useSansSerif();
|
|
||||||
padding-left : 1em;
|
|
||||||
font-size : 0.352cm;
|
|
||||||
}
|
|
||||||
// Monster Ability table
|
|
||||||
hr+table{
|
|
||||||
margin : 0;
|
|
||||||
column-span : 1;
|
|
||||||
background-color : transparent;
|
|
||||||
border-style : none;
|
|
||||||
border-image : none;
|
|
||||||
-webkit-column-span : 1;
|
|
||||||
tbody{
|
|
||||||
tr:nth-child(odd), tr:nth-child(even){
|
|
||||||
background-color : transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
table{
|
|
||||||
color : @headerText;
|
|
||||||
}
|
|
||||||
p+p{
|
|
||||||
margin-top : 0em;
|
|
||||||
padding-bottom : 0.5em;
|
|
||||||
text-indent : 0em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Triangle dividers
|
//Triangle dividers
|
||||||
hr{
|
hr{
|
||||||
visibility : visible;
|
visibility : visible;
|
||||||
height : 6px;
|
height : 6px;
|
||||||
margin : 4px 0px;
|
margin : 0.12cm 0cm;
|
||||||
background-image : @redTriangleImage;
|
background-image : @redTriangleImage;
|
||||||
background-size : 100% 100%;
|
background-size : 100% 100%;
|
||||||
border : none;
|
border : none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Attribute Lists
|
||||||
|
dl {
|
||||||
|
.useSansSerif();
|
||||||
|
color : @headerText;
|
||||||
|
padding-left :1.3em;
|
||||||
|
text-indent :-1.3em;
|
||||||
}
|
}
|
||||||
|
dd {
|
||||||
|
text-indent : 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monster Ability table
|
||||||
|
hr + table:first-of-type{
|
||||||
|
margin : 0;
|
||||||
|
column-span : 1;
|
||||||
|
color : @headerText;
|
||||||
|
background-color : transparent;
|
||||||
|
border-style : none;
|
||||||
|
border-image : none;
|
||||||
|
-webkit-column-span : 1;
|
||||||
|
tr {
|
||||||
|
background-color : transparent;
|
||||||
|
}
|
||||||
|
td,th {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Full Width
|
//Full Width
|
||||||
|
.monster.wide{
|
||||||
|
.useColumns(0.96);
|
||||||
|
}
|
||||||
|
|
||||||
hr+hr+blockquote{
|
hr+hr+blockquote{
|
||||||
.useColumns(0.96);
|
.useColumns(0.96);
|
||||||
}
|
}
|
||||||
@@ -485,9 +525,15 @@ body {
|
|||||||
// * MUSTACHE DIVS/SPANS
|
// * MUSTACHE DIVS/SPANS
|
||||||
// *****************************/
|
// *****************************/
|
||||||
.phb3 {
|
.phb3 {
|
||||||
|
.block {
|
||||||
|
break-inside : avoid;
|
||||||
|
}
|
||||||
.inline-block {
|
.inline-block {
|
||||||
display : block;
|
display : block;
|
||||||
}
|
}
|
||||||
|
div {
|
||||||
|
column-gap : 0.5cm; //Default spacing if a div uses multicolumns
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//*****************************
|
//*****************************
|
||||||
@@ -512,6 +558,9 @@ body {
|
|||||||
// *****************************/
|
// *****************************/
|
||||||
.phb3 {
|
.phb3 {
|
||||||
.blank {
|
.blank {
|
||||||
height: 0.8em;
|
height: 0.75em;
|
||||||
|
}
|
||||||
|
p + .blank {
|
||||||
|
margin-top: -1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ body {
|
|||||||
background-color : @monsterStatBackground;
|
background-color : @monsterStatBackground;
|
||||||
border-style : solid;
|
border-style : solid;
|
||||||
border-width : 10px;
|
border-width : 10px;
|
||||||
border-image : @monsterBorderImage 10;
|
border-image : @monsterBorderImageLegacy 10;
|
||||||
h2{
|
h2{
|
||||||
margin-top : -8px;
|
margin-top : -8px;
|
||||||
margin-bottom : 0px;
|
margin-bottom : 0px;
|
||||||
|
|||||||
2358
package-lock.json
generated
32
package.json
@@ -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.11.1",
|
"version": "2.11.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "14.15.x"
|
"node": "14.15.x"
|
||||||
},
|
},
|
||||||
@@ -40,40 +40,42 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.13.10",
|
"@babel/core": "^7.14.0",
|
||||||
"@babel/preset-env": "^7.13.10",
|
"@babel/preset-env": "^7.14.0",
|
||||||
"@babel/preset-react": "^7.12.13",
|
"@babel/preset-react": "^7.13.13",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.3.1",
|
||||||
"codemirror": "^5.59.4",
|
"codemirror": "^5.61.0",
|
||||||
"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",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
|
"express-async-handler": "^1.1.4",
|
||||||
"express-static-gzip": "2.1.1",
|
"express-static-gzip": "2.1.1",
|
||||||
"fs-extra": "9.1.0",
|
"fs-extra": "9.1.0",
|
||||||
"googleapis": "67.1.1",
|
"googleapis": "73.0.0",
|
||||||
"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",
|
||||||
"moment": "^2.29.1",
|
"marked": "2.0.3",
|
||||||
"mongoose": "^5.12.0",
|
|
||||||
"nanoid": "3.1.21",
|
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"marked": "2.0.1",
|
"moment": "^2.29.1",
|
||||||
|
"mongoose": "^5.12.7",
|
||||||
|
"nanoid": "3.1.22",
|
||||||
"nconf": "^0.11.2",
|
"nconf": "^0.11.2",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "15.7.2",
|
||||||
"query-string": "6.14.1",
|
"query-string": "7.0.0",
|
||||||
"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.0",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^6.1.0",
|
"superagent": "^6.1.0",
|
||||||
"vitreum": "github:calculuschild/vitreum#21a8e1c9421f1d3a3b474c12f480feb2fbd28c5b"
|
"vitreum": "git+https://github.com/calculuschild/vitreum.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^7.21.0",
|
"eslint": "^7.25.0",
|
||||||
"eslint-plugin-react": "^7.22.0",
|
"eslint-plugin-react": "^7.23.2",
|
||||||
"pico-check": "^2.0.3"
|
"pico-check": "^2.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
232
server.js
@@ -8,6 +8,23 @@ const homebrewApi = require('./server/homebrew.api.js');
|
|||||||
const GoogleActions = require('./server/googleActions.js');
|
const GoogleActions = require('./server/googleActions.js');
|
||||||
const serveCompressedStaticAssets = require('./server/static-assets.mv.js');
|
const serveCompressedStaticAssets = require('./server/static-assets.mv.js');
|
||||||
const sanitizeFilename = require('sanitize-filename');
|
const sanitizeFilename = require('sanitize-filename');
|
||||||
|
const asyncHandler = require('express-async-handler');
|
||||||
|
|
||||||
|
//Get the brew object from the HB database or Google Drive
|
||||||
|
const getBrewFromId = asyncHandler(async (id, accessType)=>{
|
||||||
|
if(accessType !== 'edit' && accessType !== 'share')
|
||||||
|
throw ('Invalid Access Type when getting brew');
|
||||||
|
let brew;
|
||||||
|
if(id.length > 12) {
|
||||||
|
const googleId = id.slice(0, -12);
|
||||||
|
id = id.slice(-12);
|
||||||
|
brew = await GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, id, accessType);
|
||||||
|
} else {
|
||||||
|
brew = await HomebrewModel.get(accessType == 'edit' ? { editId: id } : { shareId: id });
|
||||||
|
brew.sanatize(true);
|
||||||
|
}
|
||||||
|
return brew;
|
||||||
|
});
|
||||||
|
|
||||||
app.use('/', serveCompressedStaticAssets(`${__dirname}/build`));
|
app.use('/', serveCompressedStaticAssets(`${__dirname}/build`));
|
||||||
|
|
||||||
@@ -65,14 +82,10 @@ app.get('/robots.txt', (req, res)=>{
|
|||||||
return res.sendFile(`${__dirname}/robots.txt`);
|
return res.sendFile(`${__dirname}/robots.txt`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//Source page
|
//Source page
|
||||||
app.get('/source/:id', (req, res)=>{
|
app.get('/source/:id', asyncHandler(async (req, res)=>{
|
||||||
if(req.params.id.length > 12) {
|
const brew = await getBrewFromId(req.params.id, 'share');
|
||||||
const googleId = req.params.id.slice(0, -12);
|
|
||||||
const shareId = req.params.id.slice(-12);
|
|
||||||
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
|
||||||
.then((brew)=>{
|
|
||||||
const replaceStrings = { '&': '&', '<': '<', '>': '>' };
|
const replaceStrings = { '&': '&', '<': '<', '>': '>' };
|
||||||
let text = brew.text;
|
let text = brew.text;
|
||||||
for (const replaceStr in replaceStrings) {
|
for (const replaceStr in replaceStrings) {
|
||||||
@@ -80,38 +93,13 @@ app.get('/source/:id', (req, res)=>{
|
|||||||
}
|
}
|
||||||
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
|
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
|
||||||
res.status(200).send(text);
|
res.status(200).send(text);
|
||||||
})
|
}));
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send('Can\'t get brew from Google');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
|
||||||
.then((brew)=>{
|
|
||||||
const replaceStrings = { '&': '&', '<': '<', '>': '>' };
|
|
||||||
let text = brew.text;
|
|
||||||
for (const replaceStr in replaceStrings) {
|
|
||||||
text = text.replaceAll(replaceStr, replaceStrings[replaceStr]);
|
|
||||||
}
|
|
||||||
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
|
|
||||||
res.status(200).send(text);
|
|
||||||
})
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(404).send('Could not find Homebrew with that id');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Download brew source page
|
//Download brew source page
|
||||||
app.get('/download/:id', (req, res)=>{
|
app.get('/download/:id', asyncHandler(async (req, res)=>{
|
||||||
|
const brew = await getBrewFromId(req.params.id, 'share');
|
||||||
const prefix = 'HB - ';
|
const prefix = 'HB - ';
|
||||||
|
|
||||||
if(req.params.id.length > 12) {
|
|
||||||
const googleId = req.params.id.slice(0, -12);
|
|
||||||
const shareId = req.params.id.slice(-12);
|
|
||||||
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
|
||||||
.then((brew)=>{
|
|
||||||
let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', '');
|
let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', '');
|
||||||
if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; };
|
if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; };
|
||||||
res.set({
|
res.set({
|
||||||
@@ -120,29 +108,7 @@ app.get('/download/:id', (req, res)=>{
|
|||||||
'Content-Disposition' : `attachment; filename="${fileName}.txt"`
|
'Content-Disposition' : `attachment; filename="${fileName}.txt"`
|
||||||
});
|
});
|
||||||
res.status(200).send(brew.text);
|
res.status(200).send(brew.text);
|
||||||
})
|
}));
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send('Can\'t get brew from Google');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
|
||||||
.then((brew)=>{
|
|
||||||
let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', '');
|
|
||||||
if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; };
|
|
||||||
res.set({
|
|
||||||
'Cache-Control' : 'no-cache',
|
|
||||||
'Content-Type' : 'text/plain',
|
|
||||||
'Content-Disposition' : `attachment; filename="${fileName}.txt"`
|
|
||||||
});
|
|
||||||
res.status(200).send(brew.text);
|
|
||||||
})
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(404).send('Could not find Homebrew with that id');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//User Page
|
//User Page
|
||||||
app.get('/user/:username', async (req, res, next)=>{
|
app.get('/user/:username', async (req, res, next)=>{
|
||||||
@@ -170,123 +136,45 @@ app.get('/user/:username', async (req, res, next)=>{
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Edit Page
|
//Edit Page
|
||||||
app.get('/edit/:id', (req, res, next)=>{
|
app.get('/edit/:id', asyncHandler(async (req, res, next)=>{
|
||||||
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
|
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
|
||||||
if(req.params.id.length > 12) {
|
const brew = await getBrewFromId(req.params.id, 'edit');
|
||||||
const googleId = req.params.id.slice(0, -12);
|
|
||||||
const editId = req.params.id.slice(-12);
|
|
||||||
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, editId, 'edit')
|
|
||||||
.then((brew)=>{
|
|
||||||
req.brew = brew; //TODO Need to sanitize later
|
|
||||||
return next();
|
|
||||||
})
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send('Can\'t get brew from Google');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
HomebrewModel.get({ editId: req.params.id })
|
|
||||||
.then((brew)=>{
|
|
||||||
req.brew = brew.sanatize();
|
|
||||||
return next();
|
|
||||||
})
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send(`Can't get that`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//New Page
|
|
||||||
app.get('/new/:id', (req, res, next)=>{
|
|
||||||
if(req.params.id.length > 12) {
|
|
||||||
const googleId = req.params.id.slice(0, -12);
|
|
||||||
const shareId = req.params.id.slice(-12);
|
|
||||||
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
|
||||||
.then((brew)=>{
|
|
||||||
req.brew = brew; //TODO Need to sanitize later
|
|
||||||
return next();
|
|
||||||
})
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send('Can\'t get brew from Google');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
|
||||||
.then((brew)=>{
|
|
||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
return next();
|
return next();
|
||||||
})
|
}));
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
//New Page
|
||||||
return res.status(400).send(`Can't get that`);
|
app.get('/new/:id', asyncHandler(async (req, res, next)=>{
|
||||||
});
|
const brew = await getBrewFromId(req.params.id, 'share');
|
||||||
}
|
req.brew = brew;
|
||||||
});
|
return next();
|
||||||
|
}));
|
||||||
|
|
||||||
//Share Page
|
//Share Page
|
||||||
app.get('/share/:id', (req, res, next)=>{
|
app.get('/share/:id', asyncHandler(async (req, res, next)=>{
|
||||||
|
const brew = await getBrewFromId(req.params.id, 'share');
|
||||||
|
|
||||||
if(req.params.id.length > 12) {
|
if(req.params.id.length > 12) {
|
||||||
const googleId = req.params.id.slice(0, -12);
|
const googleId = req.params.id.slice(0, -12);
|
||||||
const shareId = req.params.id.slice(-12);
|
const shareId = req.params.id.slice(-12);
|
||||||
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
await GoogleActions.increaseView(googleId, shareId, 'share', brew)
|
||||||
.then((brew)=>{
|
.catch((err)=>{next(err);});
|
||||||
GoogleActions.increaseView(googleId, shareId, 'share', brew);
|
|
||||||
return brew;
|
|
||||||
})
|
|
||||||
.then((brew)=>{
|
|
||||||
req.brew = brew; //TODO Need to sanitize later
|
|
||||||
return next();
|
|
||||||
})
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send('Can\'t get brew from Google');
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
await brew.increaseView();
|
||||||
.then((brew)=>{
|
|
||||||
return brew.increaseView();
|
|
||||||
})
|
|
||||||
.then((brew)=>{
|
|
||||||
req.brew = brew.sanatize(true);
|
|
||||||
return next();
|
|
||||||
})
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send(`Can't get that`);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
req.brew = brew;
|
||||||
|
return next();
|
||||||
|
}));
|
||||||
|
|
||||||
//Print Page
|
//Print Page
|
||||||
app.get('/print/:id', (req, res, next)=>{
|
app.get('/print/:id', asyncHandler(async (req, res, next)=>{
|
||||||
if(req.params.id.length > 12) {
|
const brew = await getBrewFromId(req.params.id, 'share');
|
||||||
const googleId = req.params.id.slice(0, -12);
|
req.brew = brew;
|
||||||
const shareId = req.params.id.slice(-12);
|
|
||||||
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
|
||||||
.then((brew)=>{
|
|
||||||
req.brew = brew; //TODO Need to sanitize later
|
|
||||||
return next();
|
return next();
|
||||||
})
|
}));
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send('Can\'t get brew from Google');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
|
||||||
.then((brew)=>{
|
|
||||||
req.brew = brew.sanatize(true);
|
|
||||||
return next();
|
|
||||||
})
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
return res.status(400).send(`Can't get that`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Render the page
|
//Render the page
|
||||||
//const render = require('.build/render');
|
|
||||||
const templateFn = require('./client/template.js');
|
const templateFn = require('./client/template.js');
|
||||||
app.use((req, res)=>{
|
app.use((req, res)=>{
|
||||||
const props = {
|
const props = {
|
||||||
@@ -303,11 +191,35 @@ app.use((req, res)=>{
|
|||||||
templateFn('homebrew', title = req.brew ? req.brew.title : '', props)
|
templateFn('homebrew', title = req.brew ? req.brew.title : '', props)
|
||||||
.then((page)=>{ res.send(page); })
|
.then((page)=>{ res.send(page); })
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
|
console.log('TEMPLATE ERROR');
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return res.sendStatus(500);
|
return res.sendStatus(500);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//v=====----- Error-Handling Middleware -----=====v//
|
||||||
|
//Format Errors so all fields will be sent
|
||||||
|
const replaceErrors = (key, value)=>{
|
||||||
|
if(value instanceof Error) {
|
||||||
|
const error = {};
|
||||||
|
Object.getOwnPropertyNames(value).forEach(function (key) {
|
||||||
|
error[key] = value[key];
|
||||||
|
});
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPureError = (error)=>{
|
||||||
|
return JSON.parse(JSON.stringify(error, replaceErrors));
|
||||||
|
};
|
||||||
|
|
||||||
|
app.use((err, req, res, next)=>{
|
||||||
|
const status = err.status || 500;
|
||||||
|
console.error(err);
|
||||||
|
res.status(status).send(getPureError(err));
|
||||||
|
});
|
||||||
|
//^=====--------------------------------------=====^//
|
||||||
|
|
||||||
const PORT = process.env.PORT || config.get('web_port') || 8000;
|
const PORT = process.env.PORT || config.get('web_port') || 8000;
|
||||||
app.listen(PORT);
|
app.listen(PORT);
|
||||||
|
|||||||
@@ -240,6 +240,7 @@ GoogleActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
readFileMetadata : async (auth, id, accessId, accessType)=>{
|
readFileMetadata : async (auth, id, accessId, accessType)=>{
|
||||||
|
|
||||||
const drive = google.drive({ version: 'v3', auth: auth });
|
const drive = google.drive({ version: 'v3', auth: auth });
|
||||||
|
|
||||||
const obj = await drive.files.get({
|
const obj = await drive.files.get({
|
||||||
@@ -248,7 +249,7 @@ GoogleActions = {
|
|||||||
})
|
})
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log('Error loading from Google');
|
console.log('Error loading from Google');
|
||||||
console.error(err);
|
throw (err);
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -345,7 +346,10 @@ GoogleActions = {
|
|||||||
increaseView : async (id, accessId, accessType, brew)=>{
|
increaseView : async (id, accessId, accessType, brew)=>{
|
||||||
//service account because this is modifying another user's file properties
|
//service account because this is modifying another user's file properties
|
||||||
//so we need extended scope
|
//so we need extended scope
|
||||||
const keys = JSON.parse(config.get('service_account'));
|
const keys = typeof(config.get('service_account')) == 'string' ?
|
||||||
|
JSON.parse(config.get('service_account')) :
|
||||||
|
config.get('service_account');
|
||||||
|
|
||||||
const auth = google.auth.fromJSON(keys);
|
const auth = google.auth.fromJSON(keys);
|
||||||
auth.scopes = ['https://www.googleapis.com/auth/drive'];
|
auth.scopes = ['https://www.googleapis.com/auth/drive'];
|
||||||
|
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ module.exports = {
|
|||||||
.replace(/(<dt>.*<\/dt><dd>.*<\/dd>\n?)+/gm, `<dl>$1</dl>\n\n`)
|
.replace(/(<dt>.*<\/dt><dd>.*<\/dd>\n?)+/gm, `<dl>$1</dl>\n\n`)
|
||||||
.replace(/^}}/gm, '\n}}')
|
.replace(/^}}/gm, '\n}}')
|
||||||
.replace(/^({{[^\n]*)$/gm, '$1\n');
|
.replace(/^({{[^\n]*)$/gm, '$1\n');
|
||||||
console.log(rawBrewText);
|
|
||||||
return Markdown(
|
return Markdown(
|
||||||
sanatizeScriptTags(rawBrewText),
|
sanatizeScriptTags(rawBrewText),
|
||||||
{ renderer: renderer }
|
{ renderer: renderer }
|
||||||
|
|||||||