mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-07 18:32:40 +00:00
Merge branch 'naturalcrit:master' into Legacy-SpellList-Update
This commit is contained in:
33
changelog.md
33
changelog.md
@@ -6,6 +6,19 @@ h5 {
|
|||||||
|
|
||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
|
### Friday, 30/07/2021 - v2.13.2
|
||||||
|
|
||||||
|
- Background work to allow new themes in the future
|
||||||
|
- Fixed cursor getting stuck when resizing divider bar
|
||||||
|
|
||||||
|
##### G-Ambatte :
|
||||||
|
- Fix Style tab not copying when Cloned To New
|
||||||
|
- Basic brew sorting on User page
|
||||||
|
- Reduced data sent on each request from server
|
||||||
|
|
||||||
|
##### Gazook89 :
|
||||||
|
- Cleaned up styling on menus
|
||||||
|
|
||||||
### Saturday, 28/6/2021 - v2.13.1
|
### Saturday, 28/6/2021 - v2.13.1
|
||||||
|
|
||||||
- Fixed the issue with new brews not saving!
|
- Fixed the issue with new brews not saving!
|
||||||
@@ -44,6 +57,9 @@ myStyle {color: black}
|
|||||||
##### G-Ambatte :
|
##### G-Ambatte :
|
||||||
- Snippet to remove drop caps (fancy first letter after title)
|
- Snippet to remove drop caps (fancy first letter after title)
|
||||||
|
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
### Saturday, 13/3/2021 - v2.11.0
|
### Saturday, 13/3/2021 - v2.11.0
|
||||||
|
|
||||||
- Many background things for upcoming v3. Get pumped.
|
- Many background things for upcoming v3. Get pumped.
|
||||||
@@ -87,6 +103,8 @@ myStyle {color: black}
|
|||||||
- 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
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
### Wednesday, 07/10/2020 - v2.10.0
|
### Wednesday, 07/10/2020 - v2.10.0
|
||||||
- Google Drive integration -- Sign in with your Google account to link it with your Homebrewery profile. A new button in the Edit page will let you transfer your file to your personal Google Drive storage, and Google will keep a backup of each version! No more lost work surprises!
|
- Google Drive integration -- Sign in with your Google account to link it with your Homebrewery profile. A new button in the Edit page will let you transfer your file to your personal Google Drive storage, and Google will keep a backup of each version! No more lost work surprises!
|
||||||
|
|
||||||
@@ -100,13 +118,6 @@ myStyle {color: black}
|
|||||||
### 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
|
||||||
@@ -180,6 +191,8 @@ myStyle {color: black}
|
|||||||
- Added a hover tooltip to fully read the brew description
|
- Added a hover tooltip to fully read the brew description
|
||||||
- Made the brew items take up only 25% allowing you to view more per row.
|
- Made the brew items take up only 25% allowing you to view more per row.
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
### Wednesday, 23/11/2016 - v2.5.0
|
### Wednesday, 23/11/2016 - v2.5.0
|
||||||
- Metadata can now be added to brews
|
- Metadata can now be added to brews
|
||||||
- Added a metadata editor onto the edit and new pages
|
- Added a metadata editor onto the edit and new pages
|
||||||
@@ -195,8 +208,6 @@ myStyle {color: black}
|
|||||||
- 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.
|
||||||
@@ -253,6 +264,8 @@ myStyle {color: black}
|
|||||||
### Wednesday, 25/05/2016 -v2.0.5
|
### Wednesday, 25/05/2016 -v2.0.5
|
||||||
- The class table generators have the proper ability score improvement progression.
|
- The class table generators have the proper ability score improvement progression.
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
### Tuesday, 24/05/2016 - v2.0.4
|
### Tuesday, 24/05/2016 - v2.0.4
|
||||||
- Fixed extra wide monster stat blocks sometimes only being one column
|
- Fixed extra wide monster stat blocks sometimes only being one column
|
||||||
- The class table generators now follow the proper progression from the PHB (thakns u/IrishBandit)
|
- The class table generators now follow the proper progression from the PHB (thakns u/IrishBandit)
|
||||||
@@ -263,8 +276,6 @@ myStyle {color: black}
|
|||||||
- Bumped up the allowed entity size for extra-large brew (Thanks for reporting it dickboner93)
|
- Bumped up the allowed entity size for extra-large brew (Thanks for reporting it dickboner93)
|
||||||
- Added a little error box when a save fails with a custom link to reporting the issue on github.
|
- Added a little error box when a save fails with a custom link to reporting the issue on github.
|
||||||
|
|
||||||
\page
|
|
||||||
|
|
||||||
### Saturday, 14/05/2016 - v2.0.0 (finally!)
|
### Saturday, 14/05/2016 - v2.0.0 (finally!)
|
||||||
|
|
||||||
I've been working on v2 for a *very* long time. I want to thank you guys for being paitent.
|
I've been working on v2 for a *very* long time. I want to thank you guys for being paitent.
|
||||||
|
|||||||
@@ -122,6 +122,11 @@ const BrewRenderer = createClass({
|
|||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderStyle : function() {
|
||||||
|
if(!this.props.style) return;
|
||||||
|
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${this.props.style} </style>` }} />;
|
||||||
|
},
|
||||||
|
|
||||||
renderPage : function(pageText, index){
|
renderPage : function(pageText, index){
|
||||||
if(this.props.renderer == 'legacy')
|
if(this.props.renderer == 'legacy')
|
||||||
return <div className='phb page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(pageText) }} key={index} />;
|
return <div className='phb page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(pageText) }} key={index} />;
|
||||||
@@ -191,14 +196,14 @@ const BrewRenderer = createClass({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='pages' ref='pages'>
|
<div className='pages' ref='pages'>
|
||||||
{/* Apply CSS from Style tab */}
|
{/* Apply CSS from Style tab and render pages from Markdown tab */}
|
||||||
|
|
||||||
<div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${this.props.style} </style>` }} />
|
|
||||||
|
|
||||||
{/* Render pages from Markdown tab */}
|
|
||||||
{this.state.isMounted
|
{this.state.isMounted
|
||||||
? this.renderPages()
|
&&
|
||||||
: null}
|
<>
|
||||||
|
{this.renderStyle()}
|
||||||
|
{this.renderPages()}
|
||||||
|
</>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Frame>
|
</Frame>
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ const Editor = createClass({
|
|||||||
|
|
||||||
// Highlight inline spans {{content}}
|
// Highlight inline spans {{content}}
|
||||||
if(line.includes('{{') && line.includes('}}')){
|
if(line.includes('{{') && line.includes('}}')){
|
||||||
const regex = /{{(?:="[\w,\-. ]*"|[^"'\s])*\s*|}}/g;
|
const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g;
|
||||||
let match;
|
let match;
|
||||||
let blockCount = 0;
|
let blockCount = 0;
|
||||||
while ((match = regex.exec(line)) != null) {
|
while ((match = regex.exec(line)) != null) {
|
||||||
@@ -150,7 +150,7 @@ const Editor = createClass({
|
|||||||
// Highlight block divs {{\n Content \n}}
|
// Highlight block divs {{\n Content \n}}
|
||||||
let endCh = line.length+1;
|
let endCh = line.length+1;
|
||||||
|
|
||||||
const match = line.match(/^ *{{(?:="[\w,\-. ]*"|[^"'\s])*$|^ *}}$/);
|
const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/);
|
||||||
if(match)
|
if(match)
|
||||||
endCh = match.index+match[0].length;
|
endCh = match.index+match[0].length;
|
||||||
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
|
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
|
||||||
@@ -170,7 +170,7 @@ const Editor = createClass({
|
|||||||
|
|
||||||
//Called when there are changes to the editor's dimensions
|
//Called when there are changes to the editor's dimensions
|
||||||
update : function(){
|
update : function(){
|
||||||
this.refs.codeEditor.updateSize();
|
this.refs.codeEditor?.updateSize();
|
||||||
},
|
},
|
||||||
|
|
||||||
renderEditor : function(){
|
renderEditor : function(){
|
||||||
|
|||||||
@@ -18,10 +18,11 @@
|
|||||||
font-weight : 800;
|
font-weight : 800;
|
||||||
line-height : 1.8em;
|
line-height : 1.8em;
|
||||||
text-transform : uppercase;
|
text-transform : uppercase;
|
||||||
flex-grow : 0;
|
flex : 0 0 auto;
|
||||||
}
|
}
|
||||||
&>.value{
|
&>.value{
|
||||||
flex-grow : 1;
|
flex : 1 1 auto;
|
||||||
|
min-width : 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.description.field textarea.value{
|
.description.field textarea.value{
|
||||||
@@ -38,15 +39,22 @@
|
|||||||
font-size : 0.7em;
|
font-size : 0.7em;
|
||||||
font-weight : 800;
|
font-weight : 800;
|
||||||
user-select : none;
|
user-select : none;
|
||||||
|
white-space : nowrap;
|
||||||
|
display : inline-flex;
|
||||||
|
align-items : center;
|
||||||
}
|
}
|
||||||
input{
|
input{
|
||||||
vertical-align : middle;
|
vertical-align : middle;
|
||||||
cursor : pointer;
|
cursor : pointer;
|
||||||
|
margin : 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.publish.field .value{
|
.publish.field .value{
|
||||||
position : relative;
|
position : relative;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
button{
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
button.publish{
|
button.publish{
|
||||||
.button(@blueLight);
|
.button(@blueLight);
|
||||||
}
|
}
|
||||||
@@ -76,4 +84,4 @@
|
|||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
line-height : 1.5em;
|
line-height : 1.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
name : 'Horizontal Spacing',
|
name : 'Horizontal Spacing',
|
||||||
icon : 'fas fa-arrows-alt-h',
|
icon : 'fas fa-arrows-alt-h',
|
||||||
gen : ' {{width="100px"}} '
|
gen : ' {{width:100px}} '
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Wide Block',
|
name : 'Wide Block',
|
||||||
@@ -51,35 +51,34 @@ module.exports = [
|
|||||||
name : 'Image',
|
name : 'Image',
|
||||||
icon : 'fas fa-image',
|
icon : 'fas fa-image',
|
||||||
gen : dedent`
|
gen : dedent`
|
||||||
 {width="325px"}
|
 {width:325px}
|
||||||
Credit: Kyounghwan Kim`
|
Credit: Kyounghwan Kim`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Background Image',
|
name : 'Background Image',
|
||||||
icon : 'fas fa-tree',
|
icon : 'fas fa-tree',
|
||||||
gen : ` {position="absolute",top="50px",right="30px",width="280px"}`
|
gen : ` {position:absolute,top:50px,right:30px,width:280px}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'QR Code',
|
name : 'QR Code',
|
||||||
icon : 'fas fa-qrcode',
|
icon : 'fas fa-qrcode',
|
||||||
gen : (brew)=>{
|
gen : (brew)=>{
|
||||||
return `<img ` +
|
return `![]` +
|
||||||
`src='https://api.qrserver.com/v1/create-qr-code/?data=` +
|
`(https://api.qrserver.com/v1/create-qr-code/?data=` +
|
||||||
`https://homebrewery.naturalcrit.com/share/${brew.shareId}` +
|
`https://homebrewery.naturalcrit.com/share/${brew.shareId}` +
|
||||||
`&size=100x100' ` +
|
`&size=100x100) {width:100px;mix-blend-mode:multiply}`;
|
||||||
`style='width:100px;mix-blend-mode:multiply'/>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Page Number',
|
name : 'Page Number',
|
||||||
icon : 'fas fa-bookmark',
|
icon : 'fas fa-bookmark',
|
||||||
gen : '{{pageNumber\n1\n}}\n{{footnote\nPART 1 | FANCINESS\n}}\n\n'
|
gen : '{{pageNumber 1}}\n{{footnote PART 1 | SECTION NAME}}\n\n'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Auto-incrementing Page Number',
|
name : 'Auto-incrementing Page Number',
|
||||||
icon : 'fas fa-sort-numeric-down',
|
icon : 'fas fa-sort-numeric-down',
|
||||||
gen : '{{pageNumber,auto\n}}\n\n'
|
gen : '{{pageNumber,auto}}\n{{footnote PART 1 | SECTION NAME}}\n\n'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'Link to page',
|
name : 'Link to page',
|
||||||
@@ -257,7 +256,7 @@ module.exports = [
|
|||||||
gen : function(){
|
gen : function(){
|
||||||
return dedent`
|
return dedent`
|
||||||
##### Typical Difficulty Classes
|
##### Typical Difficulty Classes
|
||||||
{{column-count="2"
|
{{column-count:2
|
||||||
| Task Difficulty | DC |
|
| Task Difficulty | DC |
|
||||||
|:----------------|:--:|
|
|:----------------|:--:|
|
||||||
| Very easy | 5 |
|
| Very easy | 5 |
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ const Homebrew = createClass({
|
|||||||
<Route path='/edit/:id' component={(routeProps)=><EditPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
|
<Route path='/edit/:id' component={(routeProps)=><EditPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
|
||||||
<Route path='/share/:id' component={(routeProps)=><SharePage id={routeProps.match.params.id} brew={this.props.brew} />}/>
|
<Route path='/share/:id' component={(routeProps)=><SharePage id={routeProps.match.params.id} brew={this.props.brew} />}/>
|
||||||
<Route path='/new/:id' component={(routeProps)=><NewPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
|
<Route path='/new/:id' component={(routeProps)=><NewPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
|
||||||
<Route path='/new' exact component={(routeProps)=><NewPage/>}/>
|
<Route path='/new' exact component={(routeProps)=><NewPage />}/>
|
||||||
<Route path='/user/:username' component={(routeProps)=><UserPage username={routeProps.match.params.username} brews={this.props.brews} />}/>
|
<Route path='/user/:username' component={(routeProps)=><UserPage username={routeProps.match.params.username} brews={this.props.brews} />}/>
|
||||||
<Route path='/print/:id' component={(routeProps)=><PrintPage brew={this.props.brew} query={queryString.parse(routeProps.location.search)} /> } />
|
<Route path='/print/:id' component={(routeProps)=><PrintPage brew={this.props.brew} query={queryString.parse(routeProps.location.search)} />}/>
|
||||||
<Route path='/print' exact component={(routeProps)=><PrintPage query={queryString.parse(routeProps.location.search)} /> } />
|
<Route path='/print' exact component={(routeProps)=><PrintPage query={queryString.parse(routeProps.location.search)} />}/>
|
||||||
<Route path='/changelog' exact component={()=><SharePage brew={{ title: 'Changelog', text: this.props.changelog }} />}/>
|
<Route path='/changelog' exact component={()=><SharePage brew={this.props.brew} />}/>
|
||||||
<Route path='/' component={()=><HomePage welcomeText={this.props.welcomeText}/>}/>
|
<Route path='/' component={()=><HomePage brew={this.props.brew} />}/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -23,19 +23,15 @@ const HomePage = createClass({
|
|||||||
getDefaultProps : function() {
|
getDefaultProps : function() {
|
||||||
return {
|
return {
|
||||||
brew : {
|
brew : {
|
||||||
text : ''
|
text : '',
|
||||||
},
|
},
|
||||||
welcomeText : '',
|
ver : '0.0.0'
|
||||||
ver : '0.0.0'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
return {
|
return {
|
||||||
brew : {
|
brew : this.props.brew,
|
||||||
text : this.props.welcomeText
|
welcomeText : this.props.brew.text
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSave : function(){
|
handleSave : function(){
|
||||||
@@ -89,7 +85,7 @@ const HomePage = createClass({
|
|||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx('floatingSaveButton', { show: this.props.welcomeText != this.state.brew.text })} onClick={this.handleSave}>
|
<div className={cx('floatingSaveButton', { show: this.state.welcomeText != this.state.brew.text })} onClick={this.handleSave}>
|
||||||
Save current <i className='fas fa-save' />
|
Save current <i className='fas fa-save' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -69,16 +69,15 @@ const NewPage = createClass({
|
|||||||
const brewStorage = localStorage.getItem(BREWKEY);
|
const brewStorage = localStorage.getItem(BREWKEY);
|
||||||
const styleStorage = localStorage.getItem(STYLEKEY);
|
const styleStorage = localStorage.getItem(STYLEKEY);
|
||||||
|
|
||||||
|
const brew = this.state.brew;
|
||||||
|
|
||||||
if(!this.props.brew.text || !this.props.brew.style){
|
if(!this.props.brew.text || !this.props.brew.style){
|
||||||
this.setState({
|
brew.text = this.props.brew.text || (brewStorage ?? '');
|
||||||
brew : {
|
brew.style = this.props.brew.style || (styleStorage ?? undefined);
|
||||||
text : this.props.brew.text || (brewStorage ?? ''),
|
|
||||||
style : this.props.brew.style || (styleStorage ?? undefined)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
|
brew : brew,
|
||||||
htmlErrors : Markdown.validate(prevState.brew.text)
|
htmlErrors : Markdown.validate(prevState.brew.text)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
20
package-lock.json
generated
20
package-lock.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "homebrewery",
|
"name": "homebrewery",
|
||||||
"version": "2.13.1",
|
"version": "2.13.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"version": "2.13.1",
|
"version": "2.13.2",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"marked": "2.1.3",
|
"marked": "2.1.3",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"mongoose": "^5.13.3",
|
"mongoose": "^5.13.4",
|
||||||
"nanoid": "3.1.23",
|
"nanoid": "3.1.23",
|
||||||
"nconf": "^0.11.3",
|
"nconf": "^0.11.3",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "15.7.2",
|
||||||
@@ -6376,9 +6376,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mongoose": {
|
"node_modules/mongoose": {
|
||||||
"version": "5.13.3",
|
"version": "5.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.4.tgz",
|
||||||
"integrity": "sha512-q+zX6kqHAvwxf5speMWhq6qF4vdj+x6/kfD5RSKdZKNm52yGmaUygN+zgrtQjBZPFEzG0B3vF6GP0PoAGadE+w==",
|
"integrity": "sha512-D1yVHAOa+G8iQZsC/nNzZe+CI1FCYu6Qk384s1vSyaSfKCu/alKeyL78BA73SsxeRKT9zmswSIueLbGBURjrKg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/mongodb": "^3.5.27",
|
"@types/mongodb": "^3.5.27",
|
||||||
"@types/node": "14.x || 15.x",
|
"@types/node": "14.x || 15.x",
|
||||||
@@ -6389,6 +6389,7 @@
|
|||||||
"mpath": "0.8.3",
|
"mpath": "0.8.3",
|
||||||
"mquery": "3.2.5",
|
"mquery": "3.2.5",
|
||||||
"ms": "2.1.2",
|
"ms": "2.1.2",
|
||||||
|
"optional-require": "1.0.x",
|
||||||
"regexp-clone": "1.0.0",
|
"regexp-clone": "1.0.0",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"sift": "13.5.2",
|
"sift": "13.5.2",
|
||||||
@@ -14455,9 +14456,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mongoose": {
|
"mongoose": {
|
||||||
"version": "5.13.3",
|
"version": "5.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.4.tgz",
|
||||||
"integrity": "sha512-q+zX6kqHAvwxf5speMWhq6qF4vdj+x6/kfD5RSKdZKNm52yGmaUygN+zgrtQjBZPFEzG0B3vF6GP0PoAGadE+w==",
|
"integrity": "sha512-D1yVHAOa+G8iQZsC/nNzZe+CI1FCYu6Qk384s1vSyaSfKCu/alKeyL78BA73SsxeRKT9zmswSIueLbGBURjrKg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/mongodb": "^3.5.27",
|
"@types/mongodb": "^3.5.27",
|
||||||
"@types/node": "14.x || 15.x",
|
"@types/node": "14.x || 15.x",
|
||||||
@@ -14468,6 +14469,7 @@
|
|||||||
"mpath": "0.8.3",
|
"mpath": "0.8.3",
|
||||||
"mquery": "3.2.5",
|
"mquery": "3.2.5",
|
||||||
"ms": "2.1.2",
|
"ms": "2.1.2",
|
||||||
|
"optional-require": "1.0.x",
|
||||||
"regexp-clone": "1.0.0",
|
"regexp-clone": "1.0.0",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"sift": "13.5.2",
|
"sift": "13.5.2",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "homebrewery",
|
"name": "homebrewery",
|
||||||
"description": "Create authentic looking D&D homebrews using only markdown",
|
"description": "Create authentic looking D&D homebrews using only markdown",
|
||||||
"version": "2.13.1",
|
"version": "2.13.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "14.15.x"
|
"node": "14.15.x"
|
||||||
},
|
},
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"marked": "2.1.3",
|
"marked": "2.1.3",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"mongoose": "^5.13.3",
|
"mongoose": "^5.13.4",
|
||||||
"nanoid": "3.1.23",
|
"nanoid": "3.1.23",
|
||||||
"nconf": "^0.11.3",
|
"nconf": "^0.11.3",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "15.7.2",
|
||||||
|
|||||||
22
server.js
22
server.js
@@ -105,6 +105,25 @@ app.get('/robots.txt', (req, res)=>{
|
|||||||
return res.sendFile(`${__dirname}/robots.txt`);
|
return res.sendFile(`${__dirname}/robots.txt`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Home page
|
||||||
|
app.get('/', async (req, res, next)=>{
|
||||||
|
const brew = {
|
||||||
|
text : welcomeText
|
||||||
|
};
|
||||||
|
req.brew = brew;
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Changelog page
|
||||||
|
app.get('/changelog', async (req, res, next)=>{
|
||||||
|
const brew = {
|
||||||
|
title : 'Changelog',
|
||||||
|
text : changelogText
|
||||||
|
};
|
||||||
|
req.brew = brew;
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
|
||||||
//Source page
|
//Source page
|
||||||
app.get('/source/:id', asyncHandler(async (req, res)=>{
|
app.get('/source/:id', asyncHandler(async (req, res)=>{
|
||||||
const brew = await getBrewFromId(req.params.id, 'raw');
|
const brew = await getBrewFromId(req.params.id, 'raw');
|
||||||
@@ -170,6 +189,7 @@ app.get('/edit/:id', asyncHandler(async (req, res, next)=>{
|
|||||||
//New Page
|
//New Page
|
||||||
app.get('/new/:id', asyncHandler(async (req, res, next)=>{
|
app.get('/new/:id', asyncHandler(async (req, res, next)=>{
|
||||||
const brew = await getBrewFromId(req.params.id, 'share');
|
const brew = await getBrewFromId(req.params.id, 'share');
|
||||||
|
brew.title = `CLONE - ${brew.title}`;
|
||||||
req.brew = brew;
|
req.brew = brew;
|
||||||
return next();
|
return next();
|
||||||
}));
|
}));
|
||||||
@@ -204,8 +224,6 @@ app.use((req, res)=>{
|
|||||||
const props = {
|
const props = {
|
||||||
version : require('./package.json').version,
|
version : require('./package.json').version,
|
||||||
url : req.originalUrl,
|
url : req.originalUrl,
|
||||||
welcomeText : welcomeText,
|
|
||||||
changelog : changelogText,
|
|
||||||
brew : req.brew,
|
brew : req.brew,
|
||||||
brews : req.brews,
|
brews : req.brews,
|
||||||
googleBrews : req.googleBrews,
|
googleBrews : req.googleBrews,
|
||||||
|
|||||||
@@ -31,30 +31,34 @@ const mustacheSpans = {
|
|||||||
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
|
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
|
||||||
const inlineRegex = /{{(?:="[\w,\-(). ]*"|[^"'{}\s])*\s*|}}/g;
|
const inlineRegex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g;
|
||||||
const match = completeSpan.exec(src);
|
const match = completeSpan.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
//Find closing delimiter
|
//Find closing delimiter
|
||||||
let blockCount = 0;
|
let blockCount = 0;
|
||||||
let tags = '';
|
let tags = '';
|
||||||
let endIndex = 0;
|
let endTags = 0;
|
||||||
|
let endToken = 0;
|
||||||
let delim;
|
let delim;
|
||||||
while (delim = inlineRegex.exec(match[0])) {
|
while (delim = inlineRegex.exec(match[0])) {
|
||||||
|
if(!tags) {
|
||||||
|
tags = ` ${processStyleTags(delim[0].substring(2))}`;
|
||||||
|
endTags = delim[0].length;
|
||||||
|
}
|
||||||
if(delim[0].startsWith('{{')) {
|
if(delim[0].startsWith('{{')) {
|
||||||
tags = tags || ` ${processStyleTags(delim[0].substring(2))}`;
|
|
||||||
blockCount++;
|
blockCount++;
|
||||||
} else if(delim[0] == '}}' && blockCount !== 0) {
|
} else if(delim[0] == '}}' && blockCount !== 0) {
|
||||||
blockCount--;
|
blockCount--;
|
||||||
if(blockCount == 0) {
|
if(blockCount == 0) {
|
||||||
endIndex = inlineRegex.lastIndex;
|
endToken = inlineRegex.lastIndex;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(endIndex) {
|
if(endToken) {
|
||||||
const raw = src.slice(0, endIndex);
|
const raw = src.slice(0, endToken);
|
||||||
const text = raw.slice((raw.indexOf(' ')+1) || -2, -2);
|
const text = raw.slice(endTags || -2, -2);
|
||||||
|
|
||||||
return { // Token to generate
|
return { // Token to generate
|
||||||
type : 'mustacheSpans', // Should match "name" above
|
type : 'mustacheSpans', // Should match "name" above
|
||||||
@@ -74,33 +78,37 @@ const mustacheSpans = {
|
|||||||
const mustacheDivs = {
|
const mustacheDivs = {
|
||||||
name : 'mustacheDivs',
|
name : 'mustacheDivs',
|
||||||
level : 'block',
|
level : 'block',
|
||||||
start(src) { return src.match(/^ *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const completeBlock = /^ *{{.*\n *}}/s; // Regex for the complete token
|
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
|
||||||
const blockRegex = /^ *{{(?:="[\w,\-(). ]*"|[^"'{}\s])*$|^ *}}$/gm;
|
const blockRegex = /^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/gm;
|
||||||
const match = completeBlock.exec(src);
|
const match = completeBlock.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
//Find closing delimiter
|
//Find closing delimiter
|
||||||
let blockCount = 0;
|
let blockCount = 0;
|
||||||
let tags = '';
|
let tags = '';
|
||||||
let endIndex = 0;
|
let endTags = 0;
|
||||||
|
let endToken = 0;
|
||||||
let delim;
|
let delim;
|
||||||
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
|
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
|
||||||
|
if(!tags) {
|
||||||
|
tags = ` ${processStyleTags(delim.substring(2))}`;
|
||||||
|
endTags = delim.length;
|
||||||
|
}
|
||||||
if(delim.startsWith('{{')) {
|
if(delim.startsWith('{{')) {
|
||||||
tags = tags || ` ${processStyleTags(delim.substring(2))}`;
|
|
||||||
blockCount++;
|
blockCount++;
|
||||||
} else if(delim == '}}' && blockCount !== 0) {
|
} else if(delim == '}}' && blockCount !== 0) {
|
||||||
blockCount--;
|
blockCount--;
|
||||||
if(blockCount == 0) {
|
if(blockCount == 0) {
|
||||||
endIndex = blockRegex.lastIndex;
|
endToken = blockRegex.lastIndex;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(endIndex) {
|
if(endToken) {
|
||||||
const raw = src.slice(0, endIndex);
|
const raw = src.slice(0, endToken);
|
||||||
const text = raw.slice((raw.indexOf('\n')+1) || -2, -2);
|
const text = raw.slice(endTags || -2, -2);
|
||||||
return { // Token to generate
|
return { // Token to generate
|
||||||
type : 'mustacheDivs', // Should match "name" above
|
type : 'mustacheDivs', // Should match "name" above
|
||||||
raw : raw, // Text to consume from the source
|
raw : raw, // Text to consume from the source
|
||||||
@@ -121,7 +129,7 @@ const mustacheInjectInline = {
|
|||||||
level : 'inline',
|
level : 'inline',
|
||||||
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const inlineRegex = /^ *{((?:="[\w,\-(). ]*"|[^"'{}\s])*)}/g;
|
const inlineRegex = /^ *{((?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*)}/g;
|
||||||
const match = inlineRegex.exec(src);
|
const match = inlineRegex.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
const lastToken = tokens[tokens.length - 1];
|
const lastToken = tokens[tokens.length - 1];
|
||||||
@@ -154,9 +162,9 @@ const mustacheInjectBlock = {
|
|||||||
extensions : [{
|
extensions : [{
|
||||||
name : 'mustacheInjectBlock',
|
name : 'mustacheInjectBlock',
|
||||||
level : 'block',
|
level : 'block',
|
||||||
start(src) { return src.match(/^ *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const inlineRegex = /^ *{((?:="[\w,\-(). ]*"|[^"'{}\s])*)}/ym;
|
const inlineRegex = /^ *{((?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*)}/ym;
|
||||||
const match = inlineRegex.exec(src);
|
const match = inlineRegex.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
const lastToken = tokens[tokens.length - 1];
|
const lastToken = tokens[tokens.length - 1];
|
||||||
@@ -313,13 +321,15 @@ const tagRegex = new RegExp(`(${
|
|||||||
}).join('|')})`, 'g');
|
}).join('|')})`, 'g');
|
||||||
|
|
||||||
const processStyleTags = (string)=>{
|
const processStyleTags = (string)=>{
|
||||||
const tags = string.match(/(?:[^, "=]+|="[^"]*")+/g);
|
//split tags up. quotes can only occur right after colons.
|
||||||
|
//TODO: can we simplify to just split on commas?
|
||||||
|
const tags = string.match(/(?:[^, ":]+|:(?:"[^"]*"|))+/g);
|
||||||
|
|
||||||
if(!tags) return '"';
|
if(!tags) return '"';
|
||||||
|
|
||||||
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0];
|
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0];
|
||||||
const classes = _.remove(tags, (tag)=>!tag.includes('"'));
|
const classes = _.remove(tags, (tag)=>!tag.includes(':'));
|
||||||
const styles = tags.map((tag)=>tag.replace(/="(.*)"/g, ':$1;'));
|
const styles = tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;'));
|
||||||
return `${classes.join(' ')}" ${id ? `id="${id}"` : ''} ${styles.length ? `style="${styles.join(' ')}"` : ''}`;
|
return `${classes.join(' ')}" ${id ? `id="${id}"` : ''} ${styles.length ? `style="${styles.join(' ')}"` : ''}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -385,13 +385,14 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pageNumber{
|
.pageNumber{
|
||||||
position : absolute;
|
position : absolute;
|
||||||
right : 2px;
|
right : 2px;
|
||||||
bottom : 22px;
|
bottom : 22px;
|
||||||
width : 50px;
|
width : 50px;
|
||||||
font-size : 0.9em;
|
font-size : 0.9em;
|
||||||
color : #c9ad6a;
|
color : #c9ad6a;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
|
text-indent : 0;
|
||||||
&.auto::after {
|
&.auto::after {
|
||||||
content : counter(phb-page-numbers);
|
content : counter(phb-page-numbers);
|
||||||
}
|
}
|
||||||
@@ -602,9 +603,10 @@ body {
|
|||||||
break-inside : avoid;
|
break-inside : avoid;
|
||||||
-webkit-transform : translateZ(0); //Prevents shadows from breaking across columns
|
-webkit-transform : translateZ(0); //Prevents shadows from breaking across columns
|
||||||
}
|
}
|
||||||
.inline {
|
.inline-block {
|
||||||
display : inline-block;
|
display : inline-block;
|
||||||
text-indent : initial;
|
text-indent : initial;
|
||||||
|
line-height : 1.3em;
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
column-gap : 0.5cm; //Default spacing if a div uses multicolumns
|
column-gap : 0.5cm; //Default spacing if a div uses multicolumns
|
||||||
|
|||||||
Reference in New Issue
Block a user