mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-24 20:42:43 +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
|
||||
|
||||
### 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
|
||||
|
||||
- Fixed the issue with new brews not saving!
|
||||
@@ -44,6 +57,9 @@ myStyle {color: black}
|
||||
##### G-Ambatte :
|
||||
- Snippet to remove drop caps (fancy first letter after title)
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
### Saturday, 13/3/2021 - v2.11.0
|
||||
|
||||
- Many background things for upcoming v3. Get pumped.
|
||||
@@ -87,6 +103,8 @@ myStyle {color: black}
|
||||
- Fixed issue with users unable to create new brews
|
||||
- Fixing brews being lost when loaded via back button
|
||||
|
||||
\page
|
||||
|
||||
### 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!
|
||||
|
||||
@@ -100,13 +118,6 @@ myStyle {color: black}
|
||||
### Wednesday, 20/05/2020 - v2.9.0
|
||||
- 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
|
||||
- Fixed delete button removing everyone's copy for brews with multiple authors
|
||||
- Compressed homebrew text in database
|
||||
@@ -180,6 +191,8 @@ myStyle {color: black}
|
||||
- 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.
|
||||
|
||||
\page
|
||||
|
||||
### Wednesday, 23/11/2016 - v2.5.0
|
||||
- Metadata can now be added to brews
|
||||
- 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
|
||||
- Added the ability to use ctrl+p and ctrl+s to print and save respectively.
|
||||
|
||||
\page
|
||||
|
||||
### Monday, 07/11/2016
|
||||
- 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.
|
||||
@@ -253,6 +264,8 @@ myStyle {color: black}
|
||||
### Wednesday, 25/05/2016 -v2.0.5
|
||||
- The class table generators have the proper ability score improvement progression.
|
||||
|
||||
\page
|
||||
|
||||
### Tuesday, 24/05/2016 - v2.0.4
|
||||
- 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)
|
||||
@@ -263,8 +276,6 @@ myStyle {color: black}
|
||||
- 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.
|
||||
|
||||
\page
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -122,6 +122,11 @@ const BrewRenderer = createClass({
|
||||
</div>;
|
||||
},
|
||||
|
||||
renderStyle : function() {
|
||||
if(!this.props.style) return;
|
||||
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${this.props.style} </style>` }} />;
|
||||
},
|
||||
|
||||
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} />;
|
||||
@@ -191,14 +196,14 @@ const BrewRenderer = createClass({
|
||||
</div>
|
||||
|
||||
<div className='pages' ref='pages'>
|
||||
{/* Apply CSS from Style tab */}
|
||||
|
||||
<div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${this.props.style} </style>` }} />
|
||||
|
||||
{/* Render pages from Markdown tab */}
|
||||
{/* Apply CSS from Style tab and render pages from Markdown tab */}
|
||||
{this.state.isMounted
|
||||
? this.renderPages()
|
||||
: null}
|
||||
&&
|
||||
<>
|
||||
{this.renderStyle()}
|
||||
{this.renderPages()}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Frame>
|
||||
|
||||
@@ -131,7 +131,7 @@ const Editor = createClass({
|
||||
|
||||
// Highlight inline spans {{content}}
|
||||
if(line.includes('{{') && line.includes('}}')){
|
||||
const regex = /{{(?:="[\w,\-. ]*"|[^"'\s])*\s*|}}/g;
|
||||
const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g;
|
||||
let match;
|
||||
let blockCount = 0;
|
||||
while ((match = regex.exec(line)) != null) {
|
||||
@@ -150,7 +150,7 @@ const Editor = createClass({
|
||||
// Highlight block divs {{\n Content \n}}
|
||||
let endCh = line.length+1;
|
||||
|
||||
const match = line.match(/^ *{{(?:="[\w,\-. ]*"|[^"'\s])*$|^ *}}$/);
|
||||
const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/);
|
||||
if(match)
|
||||
endCh = match.index+match[0].length;
|
||||
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
|
||||
update : function(){
|
||||
this.refs.codeEditor.updateSize();
|
||||
this.refs.codeEditor?.updateSize();
|
||||
},
|
||||
|
||||
renderEditor : function(){
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
font-weight : 800;
|
||||
line-height : 1.8em;
|
||||
text-transform : uppercase;
|
||||
flex-grow : 0;
|
||||
flex : 0 0 auto;
|
||||
}
|
||||
&>.value{
|
||||
flex-grow : 1;
|
||||
flex : 1 1 auto;
|
||||
min-width : 200px;
|
||||
}
|
||||
}
|
||||
.description.field textarea.value{
|
||||
@@ -38,15 +39,22 @@
|
||||
font-size : 0.7em;
|
||||
font-weight : 800;
|
||||
user-select : none;
|
||||
white-space : nowrap;
|
||||
display : inline-flex;
|
||||
align-items : center;
|
||||
}
|
||||
input{
|
||||
vertical-align : middle;
|
||||
cursor : pointer;
|
||||
margin : 3px;
|
||||
}
|
||||
}
|
||||
.publish.field .value{
|
||||
position : relative;
|
||||
margin-bottom: 15px;
|
||||
button{
|
||||
width:100%;
|
||||
}
|
||||
button.publish{
|
||||
.button(@blueLight);
|
||||
}
|
||||
@@ -76,4 +84,4 @@
|
||||
font-size: 0.8em;
|
||||
line-height : 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ module.exports = [
|
||||
{
|
||||
name : 'Horizontal Spacing',
|
||||
icon : 'fas fa-arrows-alt-h',
|
||||
gen : ' {{width="100px"}} '
|
||||
gen : ' {{width:100px}} '
|
||||
},
|
||||
{
|
||||
name : 'Wide Block',
|
||||
@@ -51,35 +51,34 @@ module.exports = [
|
||||
name : 'Image',
|
||||
icon : 'fas fa-image',
|
||||
gen : dedent`
|
||||
 {width="325px"}
|
||||
 {width:325px}
|
||||
Credit: Kyounghwan Kim`
|
||||
},
|
||||
{
|
||||
name : 'Background Image',
|
||||
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',
|
||||
icon : 'fas fa-qrcode',
|
||||
gen : (brew)=>{
|
||||
return `<img ` +
|
||||
`src='https://api.qrserver.com/v1/create-qr-code/?data=` +
|
||||
return `![]` +
|
||||
`(https://api.qrserver.com/v1/create-qr-code/?data=` +
|
||||
`https://homebrewery.naturalcrit.com/share/${brew.shareId}` +
|
||||
`&size=100x100' ` +
|
||||
`style='width:100px;mix-blend-mode:multiply'/>`;
|
||||
`&size=100x100) {width:100px;mix-blend-mode:multiply}`;
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
name : 'Page Number',
|
||||
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',
|
||||
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',
|
||||
@@ -257,7 +256,7 @@ module.exports = [
|
||||
gen : function(){
|
||||
return dedent`
|
||||
##### Typical Difficulty Classes
|
||||
{{column-count="2"
|
||||
{{column-count:2
|
||||
| Task Difficulty | DC |
|
||||
|:----------------|:--:|
|
||||
| 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='/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' 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='/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='/changelog' exact component={()=><SharePage brew={{ title: 'Changelog', text: this.props.changelog }} />}/>
|
||||
<Route path='/' component={()=><HomePage welcomeText={this.props.welcomeText}/>}/>
|
||||
<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='/changelog' exact component={()=><SharePage brew={this.props.brew} />}/>
|
||||
<Route path='/' component={()=><HomePage brew={this.props.brew} />}/>
|
||||
</Switch>
|
||||
</div>
|
||||
</Router>
|
||||
|
||||
@@ -23,19 +23,15 @@ const HomePage = createClass({
|
||||
getDefaultProps : function() {
|
||||
return {
|
||||
brew : {
|
||||
text : ''
|
||||
text : '',
|
||||
},
|
||||
welcomeText : '',
|
||||
ver : '0.0.0'
|
||||
ver : '0.0.0'
|
||||
};
|
||||
|
||||
|
||||
},
|
||||
getInitialState : function() {
|
||||
return {
|
||||
brew : {
|
||||
text : this.props.welcomeText
|
||||
}
|
||||
brew : this.props.brew,
|
||||
welcomeText : this.props.brew.text
|
||||
};
|
||||
},
|
||||
handleSave : function(){
|
||||
@@ -89,7 +85,7 @@ const HomePage = createClass({
|
||||
</SplitPane>
|
||||
</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' />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -69,16 +69,15 @@ const NewPage = createClass({
|
||||
const brewStorage = localStorage.getItem(BREWKEY);
|
||||
const styleStorage = localStorage.getItem(STYLEKEY);
|
||||
|
||||
const brew = this.state.brew;
|
||||
|
||||
if(!this.props.brew.text || !this.props.brew.style){
|
||||
this.setState({
|
||||
brew : {
|
||||
text : this.props.brew.text || (brewStorage ?? ''),
|
||||
style : this.props.brew.style || (styleStorage ?? undefined)
|
||||
}
|
||||
});
|
||||
brew.text = this.props.brew.text || (brewStorage ?? '');
|
||||
brew.style = this.props.brew.style || (styleStorage ?? undefined);
|
||||
}
|
||||
|
||||
this.setState((prevState)=>({
|
||||
brew : brew,
|
||||
htmlErrors : Markdown.validate(prevState.brew.text)
|
||||
}));
|
||||
|
||||
|
||||
20
package-lock.json
generated
20
package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "homebrewery",
|
||||
"version": "2.13.1",
|
||||
"version": "2.13.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "2.13.1",
|
||||
"version": "2.13.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"marked": "2.1.3",
|
||||
"markedLegacy": "npm:marked@^0.3.19",
|
||||
"moment": "^2.29.1",
|
||||
"mongoose": "^5.13.3",
|
||||
"mongoose": "^5.13.4",
|
||||
"nanoid": "3.1.23",
|
||||
"nconf": "^0.11.3",
|
||||
"prop-types": "15.7.2",
|
||||
@@ -6376,9 +6376,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "5.13.3",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.3.tgz",
|
||||
"integrity": "sha512-q+zX6kqHAvwxf5speMWhq6qF4vdj+x6/kfD5RSKdZKNm52yGmaUygN+zgrtQjBZPFEzG0B3vF6GP0PoAGadE+w==",
|
||||
"version": "5.13.4",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.4.tgz",
|
||||
"integrity": "sha512-D1yVHAOa+G8iQZsC/nNzZe+CI1FCYu6Qk384s1vSyaSfKCu/alKeyL78BA73SsxeRKT9zmswSIueLbGBURjrKg==",
|
||||
"dependencies": {
|
||||
"@types/mongodb": "^3.5.27",
|
||||
"@types/node": "14.x || 15.x",
|
||||
@@ -6389,6 +6389,7 @@
|
||||
"mpath": "0.8.3",
|
||||
"mquery": "3.2.5",
|
||||
"ms": "2.1.2",
|
||||
"optional-require": "1.0.x",
|
||||
"regexp-clone": "1.0.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"sift": "13.5.2",
|
||||
@@ -14455,9 +14456,9 @@
|
||||
}
|
||||
},
|
||||
"mongoose": {
|
||||
"version": "5.13.3",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.3.tgz",
|
||||
"integrity": "sha512-q+zX6kqHAvwxf5speMWhq6qF4vdj+x6/kfD5RSKdZKNm52yGmaUygN+zgrtQjBZPFEzG0B3vF6GP0PoAGadE+w==",
|
||||
"version": "5.13.4",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.4.tgz",
|
||||
"integrity": "sha512-D1yVHAOa+G8iQZsC/nNzZe+CI1FCYu6Qk384s1vSyaSfKCu/alKeyL78BA73SsxeRKT9zmswSIueLbGBURjrKg==",
|
||||
"requires": {
|
||||
"@types/mongodb": "^3.5.27",
|
||||
"@types/node": "14.x || 15.x",
|
||||
@@ -14468,6 +14469,7 @@
|
||||
"mpath": "0.8.3",
|
||||
"mquery": "3.2.5",
|
||||
"ms": "2.1.2",
|
||||
"optional-require": "1.0.x",
|
||||
"regexp-clone": "1.0.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"sift": "13.5.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "homebrewery",
|
||||
"description": "Create authentic looking D&D homebrews using only markdown",
|
||||
"version": "2.13.1",
|
||||
"version": "2.13.2",
|
||||
"engines": {
|
||||
"node": "14.15.x"
|
||||
},
|
||||
@@ -61,7 +61,7 @@
|
||||
"marked": "2.1.3",
|
||||
"markedLegacy": "npm:marked@^0.3.19",
|
||||
"moment": "^2.29.1",
|
||||
"mongoose": "^5.13.3",
|
||||
"mongoose": "^5.13.4",
|
||||
"nanoid": "3.1.23",
|
||||
"nconf": "^0.11.3",
|
||||
"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`);
|
||||
});
|
||||
|
||||
//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
|
||||
app.get('/source/:id', asyncHandler(async (req, res)=>{
|
||||
const brew = await getBrewFromId(req.params.id, 'raw');
|
||||
@@ -170,6 +189,7 @@ app.get('/edit/:id', asyncHandler(async (req, res, next)=>{
|
||||
//New Page
|
||||
app.get('/new/:id', asyncHandler(async (req, res, next)=>{
|
||||
const brew = await getBrewFromId(req.params.id, 'share');
|
||||
brew.title = `CLONE - ${brew.title}`;
|
||||
req.brew = brew;
|
||||
return next();
|
||||
}));
|
||||
@@ -204,8 +224,6 @@ app.use((req, res)=>{
|
||||
const props = {
|
||||
version : require('./package.json').version,
|
||||
url : req.originalUrl,
|
||||
welcomeText : welcomeText,
|
||||
changelog : changelogText,
|
||||
brew : req.brew,
|
||||
brews : req.brews,
|
||||
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
|
||||
tokenizer(src, tokens) {
|
||||
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);
|
||||
if(match) {
|
||||
//Find closing delimiter
|
||||
let blockCount = 0;
|
||||
let tags = '';
|
||||
let endIndex = 0;
|
||||
let endTags = 0;
|
||||
let endToken = 0;
|
||||
let delim;
|
||||
while (delim = inlineRegex.exec(match[0])) {
|
||||
if(!tags) {
|
||||
tags = ` ${processStyleTags(delim[0].substring(2))}`;
|
||||
endTags = delim[0].length;
|
||||
}
|
||||
if(delim[0].startsWith('{{')) {
|
||||
tags = tags || ` ${processStyleTags(delim[0].substring(2))}`;
|
||||
blockCount++;
|
||||
} else if(delim[0] == '}}' && blockCount !== 0) {
|
||||
blockCount--;
|
||||
if(blockCount == 0) {
|
||||
endIndex = inlineRegex.lastIndex;
|
||||
endToken = inlineRegex.lastIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(endIndex) {
|
||||
const raw = src.slice(0, endIndex);
|
||||
const text = raw.slice((raw.indexOf(' ')+1) || -2, -2);
|
||||
if(endToken) {
|
||||
const raw = src.slice(0, endToken);
|
||||
const text = raw.slice(endTags || -2, -2);
|
||||
|
||||
return { // Token to generate
|
||||
type : 'mustacheSpans', // Should match "name" above
|
||||
@@ -74,33 +78,37 @@ const mustacheSpans = {
|
||||
const mustacheDivs = {
|
||||
name : 'mustacheDivs',
|
||||
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) {
|
||||
const completeBlock = /^ *{{.*\n *}}/s; // Regex for the complete token
|
||||
const blockRegex = /^ *{{(?:="[\w,\-(). ]*"|[^"'{}\s])*$|^ *}}$/gm;
|
||||
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
|
||||
const blockRegex = /^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/gm;
|
||||
const match = completeBlock.exec(src);
|
||||
if(match) {
|
||||
//Find closing delimiter
|
||||
let blockCount = 0;
|
||||
let tags = '';
|
||||
let endIndex = 0;
|
||||
let endTags = 0;
|
||||
let endToken = 0;
|
||||
let delim;
|
||||
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
|
||||
if(!tags) {
|
||||
tags = ` ${processStyleTags(delim.substring(2))}`;
|
||||
endTags = delim.length;
|
||||
}
|
||||
if(delim.startsWith('{{')) {
|
||||
tags = tags || ` ${processStyleTags(delim.substring(2))}`;
|
||||
blockCount++;
|
||||
} else if(delim == '}}' && blockCount !== 0) {
|
||||
blockCount--;
|
||||
if(blockCount == 0) {
|
||||
endIndex = blockRegex.lastIndex;
|
||||
endToken = blockRegex.lastIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(endIndex) {
|
||||
const raw = src.slice(0, endIndex);
|
||||
const text = raw.slice((raw.indexOf('\n')+1) || -2, -2);
|
||||
if(endToken) {
|
||||
const raw = src.slice(0, endToken);
|
||||
const text = raw.slice(endTags || -2, -2);
|
||||
return { // Token to generate
|
||||
type : 'mustacheDivs', // Should match "name" above
|
||||
raw : raw, // Text to consume from the source
|
||||
@@ -121,7 +129,7 @@ const mustacheInjectInline = {
|
||||
level : 'inline',
|
||||
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const inlineRegex = /^ *{((?:="[\w,\-(). ]*"|[^"'{}\s])*)}/g;
|
||||
const inlineRegex = /^ *{((?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*)}/g;
|
||||
const match = inlineRegex.exec(src);
|
||||
if(match) {
|
||||
const lastToken = tokens[tokens.length - 1];
|
||||
@@ -154,9 +162,9 @@ const mustacheInjectBlock = {
|
||||
extensions : [{
|
||||
name : 'mustacheInjectBlock',
|
||||
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) {
|
||||
const inlineRegex = /^ *{((?:="[\w,\-(). ]*"|[^"'{}\s])*)}/ym;
|
||||
const inlineRegex = /^ *{((?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*)}/ym;
|
||||
const match = inlineRegex.exec(src);
|
||||
if(match) {
|
||||
const lastToken = tokens[tokens.length - 1];
|
||||
@@ -313,13 +321,15 @@ const tagRegex = new RegExp(`(${
|
||||
}).join('|')})`, 'g');
|
||||
|
||||
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 '"';
|
||||
|
||||
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0];
|
||||
const classes = _.remove(tags, (tag)=>!tag.includes('"'));
|
||||
const styles = tags.map((tag)=>tag.replace(/="(.*)"/g, ':$1;'));
|
||||
const classes = _.remove(tags, (tag)=>!tag.includes(':'));
|
||||
const styles = tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;'));
|
||||
return `${classes.join(' ')}" ${id ? `id="${id}"` : ''} ${styles.length ? `style="${styles.join(' ')}"` : ''}`;
|
||||
};
|
||||
|
||||
|
||||
@@ -385,13 +385,14 @@ body {
|
||||
}
|
||||
}
|
||||
.pageNumber{
|
||||
position : absolute;
|
||||
right : 2px;
|
||||
bottom : 22px;
|
||||
width : 50px;
|
||||
font-size : 0.9em;
|
||||
color : #c9ad6a;
|
||||
text-align : center;
|
||||
position : absolute;
|
||||
right : 2px;
|
||||
bottom : 22px;
|
||||
width : 50px;
|
||||
font-size : 0.9em;
|
||||
color : #c9ad6a;
|
||||
text-align : center;
|
||||
text-indent : 0;
|
||||
&.auto::after {
|
||||
content : counter(phb-page-numbers);
|
||||
}
|
||||
@@ -602,9 +603,10 @@ body {
|
||||
break-inside : avoid;
|
||||
-webkit-transform : translateZ(0); //Prevents shadows from breaking across columns
|
||||
}
|
||||
.inline {
|
||||
.inline-block {
|
||||
display : inline-block;
|
||||
text-indent : initial;
|
||||
line-height : 1.3em;
|
||||
}
|
||||
div {
|
||||
column-gap : 0.5cm; //Default spacing if a div uses multicolumns
|
||||
|
||||
Reference in New Issue
Block a user