mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-05 12:22:44 +00:00
Merge branch 'master' of github.com:naturalcrit/homebrewery
This commit is contained in:
177
changelog.md
177
changelog.md
@@ -75,11 +75,188 @@ pre {
|
|||||||
.page {
|
.page {
|
||||||
padding-bottom: 1.5cm;
|
padding-bottom: 1.5cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.varSyntaxTable th:first-of-type {
|
||||||
|
width:6cm;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## changelog
|
## changelog
|
||||||
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
|
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
|
||||||
|
|
||||||
|
### Wednesday 21/2/2024 - v3.11.0
|
||||||
|
{{taskList
|
||||||
|
|
||||||
|
##### Gazook89
|
||||||
|
|
||||||
|
* [x] Brew view count no longer increases when viewed by owner
|
||||||
|
|
||||||
|
Fixes issue [#3037](https://github.com/naturalcrit/homebrewery/issues/3037)
|
||||||
|
|
||||||
|
* [x] Small tweak to PHB H3 sizing
|
||||||
|
|
||||||
|
Fixes issue [#2989](https://github.com/naturalcrit/homebrewery/issues/2989)
|
||||||
|
|
||||||
|
* [x] Add **Fold/Unfold All** {{fas,fa-compress-alt}} / {{fas,fa-expand-alt}} buttons to editor bar
|
||||||
|
|
||||||
|
Fixes issue [#2965](https://github.com/naturalcrit/homebrewery/issues/2965)
|
||||||
|
|
||||||
|
|
||||||
|
##### G-Ambatte
|
||||||
|
|
||||||
|
* [x] Share link added to Editor Access error page
|
||||||
|
|
||||||
|
Fixes issue [#3086](https://github.com/naturalcrit/homebrewery/issues/3086)
|
||||||
|
|
||||||
|
* [x] Add Darkbrewery theme to Editor theme selector {{fas,fa-palette}}
|
||||||
|
|
||||||
|
Fixes issue [#3034](https://github.com/naturalcrit/homebrewery/issues/3034)
|
||||||
|
|
||||||
|
* [x] Fix Firefox prints with alternating blank pages
|
||||||
|
|
||||||
|
Fixes issue [#3115](https://github.com/naturalcrit/homebrewery/issues/3115)
|
||||||
|
|
||||||
|
* [x] Admin page working again
|
||||||
|
|
||||||
|
Fixes issue [#2657](https://github.com/naturalcrit/homebrewery/issues/2657)
|
||||||
|
|
||||||
|
|
||||||
|
##### 5e-Cleric
|
||||||
|
|
||||||
|
* [x] Fix indenting issue with Monster Blocks and italics in Class Feature
|
||||||
|
|
||||||
|
Fixes issues [#527](https://github.com/naturalcrit/homebrewery/issues/527),
|
||||||
|
[#3247](https://github.com/naturalcrit/homebrewery/issues/3247)
|
||||||
|
|
||||||
|
* [x] Allow CSS vars in curly syntax to be formatted as strings using single quotes
|
||||||
|
|
||||||
|
`{{--customVar:"'a string'"}}`
|
||||||
|
|
||||||
|
Fixes issue [#3066](https://github.com/naturalcrit/homebrewery/issues/3066)
|
||||||
|
|
||||||
|
* [x] Add *Elderberry Inn* icons {{ei,action}} `{{ei,icon-name}}`
|
||||||
|
|
||||||
|
Fixes issue [#3171](https://github.com/naturalcrit/homebrewery/issues/3171)
|
||||||
|
|
||||||
|
* [x] New {{openSans **{{fas,fa-keyboard}} FONTS** }} snippets!
|
||||||
|
|
||||||
|
Fixes issue [#3171](https://github.com/naturalcrit/homebrewery/issues/3171)
|
||||||
|
|
||||||
|
* [x] New page now opens in a new tab
|
||||||
|
|
||||||
|
|
||||||
|
##### abquintic (new contributor!)
|
||||||
|
|
||||||
|
* [x] Add ^super^ `^abc^` and ^^sub^^ `^^abc^^` syntax.
|
||||||
|
|
||||||
|
Fixes issue [#2171](https://github.com/naturalcrit/homebrewery/issues/2171)
|
||||||
|
|
||||||
|
* [x] Add HTML tag assignment to curly syntax `{{tag=value}}`
|
||||||
|
|
||||||
|
Fixes issue [1488](https://github.com/naturalcrit/homebrewery/issues/1488)
|
||||||
|
|
||||||
|
* [x] {{openSans **Brew → Clone to New**}} now clones tags
|
||||||
|
|
||||||
|
Fixes issue [1488](https://github.com/naturalcrit/homebrewery/issues/1488)
|
||||||
|
|
||||||
|
##### calculuschild
|
||||||
|
|
||||||
|
* [x] Better error messages for "Out of Google Drive Storage" and "Not logged in to edit"
|
||||||
|
|
||||||
|
Fixes issues [2510](https://github.com/naturalcrit/homebrewery/issues/2510),
|
||||||
|
[2975](https://github.com/naturalcrit/homebrewery/issues/2975)
|
||||||
|
|
||||||
|
* [x] New Variables syntax. See below for details.
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide
|
||||||
|
|
||||||
|
### Brew Variable Syntax
|
||||||
|
|
||||||
|
You may already be familiar with `[link](url)` and `` syntax. We have expanded this to include a third `$[variable](text)` syntax. All three of these syntaxes now share a common set of features:
|
||||||
|
|
||||||
|
{{varSyntaxTable
|
||||||
|
| syntax | description |
|
||||||
|
|:-------|-------------|
|
||||||
|
| `[var]:content` | Assigns a variable (must start on a line by itself, and ends at the next blank line) |
|
||||||
|
| `[var](content)` | Assigns a variable and outputs it (can be inline) |
|
||||||
|
| `[var]` | Outputs the variable contents as a link, if formatted as a valid link |
|
||||||
|
| `![var]` | Outputs as an image, if formatted as a valid image |
|
||||||
|
| `$[var]` | Outputs as Markdown |
|
||||||
|
| `$[var1 + var2 - 2 * var3]` | Performs math operations and outputs result if all variables are valid numbers |
|
||||||
|
}}
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide,margin-top:0,margin-bottom:0
|
||||||
|
### Examples
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide,columns:2,margin-top:0,margin-bottom:0
|
||||||
|
|
||||||
|
```
|
||||||
|
[first]: Bob
|
||||||
|
|
||||||
|
[last]: Jones
|
||||||
|
|
||||||
|
My name is $[first] $[last].
|
||||||
|
```
|
||||||
|
|
||||||
|
\column
|
||||||
|
|
||||||
|
[first]: Bob
|
||||||
|
|
||||||
|
[last]: Jones
|
||||||
|
|
||||||
|
My name is $[first] $[last].
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide,columns:2,margin-top:0,margin-bottom:0
|
||||||
|
|
||||||
|
```
|
||||||
|
[myTable]:
|
||||||
|
| h1 | h2 |
|
||||||
|
|----|----|
|
||||||
|
| c1 | c2 |
|
||||||
|
|
||||||
|
Here is my table:
|
||||||
|
$[myTable]
|
||||||
|
```
|
||||||
|
|
||||||
|
\column
|
||||||
|
|
||||||
|
[myTable]:
|
||||||
|
| h1 | h2 |
|
||||||
|
|----|----|
|
||||||
|
| c1 | c2 |
|
||||||
|
|
||||||
|
Here is my table:
|
||||||
|
$[myTable]
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{wide,columns:2,margin-top:0,margin-bottom:0
|
||||||
|
|
||||||
|
```
|
||||||
|
There are $[TableNum] tables total.
|
||||||
|
|
||||||
|
#### Table $[TableNum](1): Horses
|
||||||
|
|
||||||
|
#### Table $[TableNum]($[TableNum + 1]): Cows
|
||||||
|
```
|
||||||
|
|
||||||
|
\column
|
||||||
|
|
||||||
|
There are $[TableNum] tables in this document. *(note: final value of `$[TableNum]` gets hoisted up if available)*
|
||||||
|
|
||||||
|
|
||||||
|
#### Table $[TableNum](1): Horses
|
||||||
|
|
||||||
|
#### Table $[TableNum]($[TableNum + 1]): Cows
|
||||||
|
}}
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
### Friday 13/10/2023 - v3.10.0
|
### Friday 13/10/2023 - v3.10.0
|
||||||
{{taskList
|
{{taskList
|
||||||
|
|
||||||
|
|||||||
@@ -89,22 +89,24 @@ const BrewRenderer = (props)=>{
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const shouldRender = (index)=>{
|
const isInView = (index)=>{
|
||||||
if(!state.isMounted) return false;
|
if(!state.isMounted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(index == props.currentEditorPage) //Already rendered before this step
|
||||||
|
return false;
|
||||||
|
|
||||||
if(Math.abs(index - state.viewablePageNumber) <= 3)
|
if(Math.abs(index - state.viewablePageNumber) <= 3)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(index + 1 == props.currentEditorPage)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const sanitizeScriptTags = (content)=>{
|
const sanitizeScriptTags = (content)=>{
|
||||||
return content
|
return content
|
||||||
.replace(/<script/ig, '<script')
|
?.replace(/<script/ig, '<script')
|
||||||
.replace(/<\/script>/ig, '</script>');
|
.replace(/<\/script>/ig, '</script>')
|
||||||
|
|| '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPageInfo = ()=>{
|
const renderPageInfo = ()=>{
|
||||||
@@ -138,7 +140,7 @@ const BrewRenderer = (props)=>{
|
|||||||
return <BrewPage className='page phb' index={index} key={index} contents={html} />;
|
return <BrewPage className='page phb' index={index} key={index} contents={html} />;
|
||||||
} else {
|
} else {
|
||||||
cleanPageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
|
cleanPageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
|
||||||
const html = Markdown.render(cleanPageText);
|
const html = Markdown.render(cleanPageText, index);
|
||||||
return <BrewPage className='page' index={index} key={index} contents={html} />;
|
return <BrewPage className='page' index={index} key={index} contents={html} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -150,8 +152,11 @@ const BrewRenderer = (props)=>{
|
|||||||
if(rawPages.length != renderedPages.length) // Re-render all pages when page count changes
|
if(rawPages.length != renderedPages.length) // Re-render all pages when page count changes
|
||||||
renderedPages.length = 0;
|
renderedPages.length = 0;
|
||||||
|
|
||||||
|
// Render currently-edited page first so cross-page effects (variables, links) can propagate out first
|
||||||
|
renderedPages[props.currentEditorPage] = renderPage(rawPages[props.currentEditorPage], props.currentEditorPage);
|
||||||
|
|
||||||
_.forEach(rawPages, (page, index)=>{
|
_.forEach(rawPages, (page, index)=>{
|
||||||
if((shouldRender(index) || !renderedPages[index]) && typeof window !== 'undefined'){
|
if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){
|
||||||
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
|
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,13 +25,10 @@ const NotificationPopup = createClass({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<li key='psa'>
|
<li key='psa'>
|
||||||
<em>Broken default logo on <b>CoverPage</b> </em> <br />
|
<em>Don't store IMAGES in Google Drive</em><br />
|
||||||
If you have used the Cover Page snippet and notice the Naturalcrit
|
Google Drive is not an image service, and will block images from being used
|
||||||
logo is showing as a broken image, this is due to some small tweaks
|
in brews if they get more views than expected. Google has confirmed they won't fix
|
||||||
of this BETA feature. To fix the logo in your cover page, rename
|
this, so we recommend you look for another image hosting service such as imgur, ImgBB or Google Photos.
|
||||||
the image link <b>"/assets/naturalCritLogoRed.svg"</b>. Remember
|
|
||||||
that any snippet marked "BETA" may have a similar change in the
|
|
||||||
future as we encounter any bugs or reworks.
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li key='googleDriveFolder'>
|
<li key='googleDriveFolder'>
|
||||||
|
|||||||
@@ -135,7 +135,11 @@ const BrewItem = createClass({
|
|||||||
</> : <></>
|
</> : <></>
|
||||||
}
|
}
|
||||||
<span title={`Authors:\n${brew.authors?.join('\n')}`}>
|
<span title={`Authors:\n${brew.authors?.join('\n')}`}>
|
||||||
<i className='fas fa-user'/> {brew.authors.map((item) => <a href={`/user/${item}`}>{item}</a>)}
|
<i className='fas fa-user'/> {brew.authors?.map((author, index)=>(
|
||||||
|
<>
|
||||||
|
<a key={index} href={`/user/${author}`}>{author}</a>
|
||||||
|
{index < brew.authors.length - 1 && ', '}
|
||||||
|
</>))}
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>
|
<span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ const EditPage = createClass({
|
|||||||
brew : { ...prevState.brew, text: text },
|
brew : { ...prevState.brew, text: text },
|
||||||
isPending : true,
|
isPending : true,
|
||||||
htmlErrors : htmlErrors,
|
htmlErrors : htmlErrors,
|
||||||
currentEditorPage : this.refs.editor.getCurrentPage()
|
currentEditorPage : this.refs.editor.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||||
}), ()=>{if(this.state.autoSave) this.trySave();});
|
}), ()=>{if(this.state.autoSave) this.trySave();});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ const HomePage = createClass({
|
|||||||
},
|
},
|
||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
return {
|
return {
|
||||||
brew : this.props.brew,
|
brew : this.props.brew,
|
||||||
welcomeText : this.props.brew.text,
|
welcomeText : this.props.brew.text,
|
||||||
error : undefined
|
error : undefined,
|
||||||
|
currentEditorPage : 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSave : function(){
|
handleSave : function(){
|
||||||
@@ -53,7 +54,8 @@ const HomePage = createClass({
|
|||||||
},
|
},
|
||||||
handleTextChange : function(text){
|
handleTextChange : function(text){
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
brew : { ...prevState.brew, text: text }
|
brew : { ...prevState.brew, text: text },
|
||||||
|
currentEditorPage : this.refs.editor.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
renderNavbar : function(){
|
renderNavbar : function(){
|
||||||
@@ -85,7 +87,12 @@ const HomePage = createClass({
|
|||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
showEditButtons={false}
|
showEditButtons={false}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer}/>
|
<BrewRenderer
|
||||||
|
text={this.state.brew.text}
|
||||||
|
style={this.state.brew.style}
|
||||||
|
renderer={this.state.brew.renderer}
|
||||||
|
currentEditorPage={this.state.currentEditorPage}
|
||||||
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ Much nicer than `<br><br><br><br><br>`
|
|||||||
### Column Breaks
|
### Column Breaks
|
||||||
Column and page breaks with `\column` and `\page`.
|
Column and page breaks with `\column` and `\page`.
|
||||||
|
|
||||||
|
\column
|
||||||
### Tables
|
### Tables
|
||||||
Tables now allow column & row spanning between cells. This is included in some updated snippets, but a simplified example is given below.
|
Tables now allow column & row spanning between cells. This is included in some updated snippets, but a simplified example is given below.
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,12 @@ const NewPage = createClass({
|
|||||||
const brew = this.props.brew;
|
const brew = this.props.brew;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
brew : brew,
|
brew : brew,
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
saveGoogle : (global.account && global.account.googleId ? true : false),
|
saveGoogle : (global.account && global.account.googleId ? true : false),
|
||||||
error : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(brew.text)
|
htmlErrors : Markdown.validate(brew.text),
|
||||||
|
currentEditorPage : 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -104,8 +105,9 @@ const NewPage = createClass({
|
|||||||
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
|
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
|
||||||
|
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
brew : { ...prevState.brew, text: text },
|
brew : { ...prevState.brew, text: text },
|
||||||
htmlErrors : htmlErrors
|
htmlErrors : htmlErrors,
|
||||||
|
currentEditorPage : this.refs.editor.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||||
}));
|
}));
|
||||||
localStorage.setItem(BREWKEY, text);
|
localStorage.setItem(BREWKEY, text);
|
||||||
},
|
},
|
||||||
@@ -220,7 +222,15 @@ const NewPage = createClass({
|
|||||||
onMetaChange={this.handleMetaChange}
|
onMetaChange={this.handleMetaChange}
|
||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} lang={this.state.brew.lang} errors={this.state.htmlErrors}/>
|
<BrewRenderer
|
||||||
|
text={this.state.brew.text}
|
||||||
|
style={this.state.brew.style}
|
||||||
|
renderer={this.state.brew.renderer}
|
||||||
|
theme={this.state.brew.theme}
|
||||||
|
errors={this.state.htmlErrors}
|
||||||
|
lang={this.state.brew.lang}
|
||||||
|
currentEditorPage={this.state.currentEditorPage}
|
||||||
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ const PrintPage = createClass({
|
|||||||
brew : {
|
brew : {
|
||||||
text : '',
|
text : '',
|
||||||
style : '',
|
style : '',
|
||||||
renderer : 'legacy'
|
renderer : 'legacy',
|
||||||
|
lang : ''
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -32,7 +33,8 @@ const PrintPage = createClass({
|
|||||||
text : this.props.brew.text || '',
|
text : this.props.brew.text || '',
|
||||||
style : this.props.brew.style || undefined,
|
style : this.props.brew.style || undefined,
|
||||||
renderer : this.props.brew.renderer || 'legacy',
|
renderer : this.props.brew.renderer || 'legacy',
|
||||||
theme : this.props.brew.theme || '5ePHB'
|
theme : this.props.brew.theme || '5ePHB',
|
||||||
|
lang : this.props.brew.lang || 'en'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -49,7 +51,8 @@ const PrintPage = createClass({
|
|||||||
text : brewStorage,
|
text : brewStorage,
|
||||||
style : styleStorage,
|
style : styleStorage,
|
||||||
renderer : metaStorage?.renderer || 'legacy',
|
renderer : metaStorage?.renderer || 'legacy',
|
||||||
theme : metaStorage?.theme || '5ePHB'
|
theme : metaStorage?.theme || '5ePHB',
|
||||||
|
lang : metaStorage?.lang || 'en'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -100,7 +103,7 @@ const PrintPage = createClass({
|
|||||||
<link href={`/themes/${rendererPath}/${themePath}/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/${themePath}/style.css`} rel='stylesheet'/>
|
||||||
{/* Apply CSS from Style tab */}
|
{/* Apply CSS from Style tab */}
|
||||||
{this.renderStyle()}
|
{this.renderStyle()}
|
||||||
<div className='pages' ref='pages'>
|
<div className='pages' ref='pages' lang={this.state.brew.lang}>
|
||||||
{this.renderPages()}
|
{this.renderPages()}
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
80
package-lock.json
generated
80
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "homebrewery",
|
"name": "homebrewery",
|
||||||
"version": "3.10.0",
|
"version": "3.11.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "homebrewery",
|
"name": "homebrewery",
|
||||||
"version": "3.10.0",
|
"version": "3.11.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -14,13 +14,14 @@
|
|||||||
"@babel/plugin-transform-runtime": "^7.23.9",
|
"@babel/plugin-transform-runtime": "^7.23.9",
|
||||||
"@babel/preset-env": "^7.23.9",
|
"@babel/preset-env": "^7.23.9",
|
||||||
"@babel/preset-react": "^7.23.3",
|
"@babel/preset-react": "^7.23.3",
|
||||||
"@googleapis/drive": "^8.6.0",
|
"@googleapis/drive": "^8.7.0",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"codemirror": "^5.65.6",
|
"codemirror": "^5.65.6",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"create-react-class": "^15.7.0",
|
"create-react-class": "^15.7.0",
|
||||||
"dedent-tabs": "^0.10.3",
|
"dedent-tabs": "^0.10.3",
|
||||||
|
"expr-eval": "^2.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-async-handler": "^1.2.0",
|
"express-async-handler": "^1.2.0",
|
||||||
"express-static-gzip": "2.1.7",
|
"express-static-gzip": "2.1.7",
|
||||||
@@ -31,30 +32,30 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "11.2.0",
|
"marked": "11.2.0",
|
||||||
"marked-extended-tables": "^1.0.8",
|
"marked-extended-tables": "^1.0.8",
|
||||||
"marked-gfm-heading-id": "^3.1.2",
|
"marked-gfm-heading-id": "^3.1.3",
|
||||||
"marked-smartypants-lite": "^1.0.2",
|
"marked-smartypants-lite": "^1.0.2",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"mongoose": "^8.1.1",
|
"mongoose": "^8.2.0",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"nconf": "^0.12.1",
|
"nconf": "^0.12.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-frame-component": "^4.1.3",
|
"react-frame-component": "^4.1.3",
|
||||||
"react-router-dom": "6.21.3",
|
"react-router-dom": "6.22.1",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^8.1.2",
|
"superagent": "^8.1.2",
|
||||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-plugin-jest": "^27.6.3",
|
"eslint-plugin-jest": "^27.9.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
"stylelint": "^15.11.0",
|
"stylelint": "^15.11.0",
|
||||||
"stylelint-config-recess-order": "^4.4.0",
|
"stylelint-config-recess-order": "^4.6.0",
|
||||||
"stylelint-config-recommended": "^13.0.0",
|
"stylelint-config-recommended": "^13.0.0",
|
||||||
"stylelint-stylistic": "^0.4.3",
|
"stylelint-stylistic": "^0.4.3",
|
||||||
"supertest": "^6.3.4"
|
"supertest": "^6.3.4"
|
||||||
@@ -1966,9 +1967,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@googleapis/drive": {
|
"node_modules/@googleapis/drive": {
|
||||||
"version": "8.6.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.7.0.tgz",
|
||||||
"integrity": "sha512-Af3/5i6h7gbjHnwFuO9zMTpYOy2yhhfZlNciUEjb14L3ZdT1WNIDM038viIAb9ovFzkrIDqLSfUbFCgh1pywkw==",
|
"integrity": "sha512-XAi6kfySIU4H3ivX2DpzTDce5UhNke5NxEWCL6tySEdcVqx+cmXJmkMqwfOAHJalEB5s9PPfdLBU29Xd5XlLSQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"googleapis-common": "^7.0.0"
|
"googleapis-common": "^7.0.0"
|
||||||
},
|
},
|
||||||
@@ -2837,9 +2838,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@remix-run/router": {
|
"node_modules/@remix-run/router": {
|
||||||
"version": "1.14.2",
|
"version": "1.15.1",
|
||||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.1.tgz",
|
||||||
"integrity": "sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==",
|
"integrity": "sha512-zcU0gM3z+3iqj8UX45AmWY810l3oUmXM7uH4dt5xtzvMhRtYVhKGOmgOd1877dOPPepfCjUv57w+syamWIYe7w==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
@@ -5642,9 +5643,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-jest": {
|
"node_modules/eslint-plugin-jest": {
|
||||||
"version": "27.6.3",
|
"version": "27.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz",
|
||||||
"integrity": "sha512-+YsJFVH6R+tOiO3gCJon5oqn4KWc+mDq2leudk8mrp8RFubLOo9CVyi3cib4L7XMpxExmkmBZQTPDYVBzgpgOA==",
|
"integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/utils": "^5.10.0"
|
"@typescript-eslint/utils": "^5.10.0"
|
||||||
@@ -5653,7 +5654,7 @@
|
|||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||||
"eslint": "^7.0.0 || ^8.0.0",
|
"eslint": "^7.0.0 || ^8.0.0",
|
||||||
"jest": "*"
|
"jest": "*"
|
||||||
},
|
},
|
||||||
@@ -6036,6 +6037,11 @@
|
|||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expr-eval": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg=="
|
||||||
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.18.2",
|
"version": "4.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||||
@@ -10060,14 +10066,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/marked-gfm-heading-id": {
|
"node_modules/marked-gfm-heading-id": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-3.1.3.tgz",
|
||||||
"integrity": "sha512-SdIZvhNxDgndFkDa2WRcFP4ahYm6k6hoHdTCN+fD7HRiI/R3Eimcw/Yl7ikQ+0KUuDpi75NnYQiThZnZsNr9Dg==",
|
"integrity": "sha512-A0cRU4PCueX/5m8VE4mT8uTQ36l3xMYRojz3Eqnk4BmUFZ0T+9Xhn2KvHcANP4qbhfOeuMrWJCTQbASIBR5xeg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"github-slugger": "^2.0.0"
|
"github-slugger": "^2.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"marked": ">=4 <12"
|
"marked": ">=4 <13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/marked-smartypants-lite": {
|
"node_modules/marked-smartypants-lite": {
|
||||||
@@ -10453,9 +10459,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mongoose": {
|
"node_modules/mongoose": {
|
||||||
"version": "8.1.1",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.0.tgz",
|
||||||
"integrity": "sha512-DbLb0NsiEXmaqLOpEz+AtAsgwhRw6f25gwa1dF5R7jj6lS1D8X6uTdhBSC8GDVtOwe5Tfw2EL7nTn6hiJT3Bgg==",
|
"integrity": "sha512-la93n6zCYRbPS+c5N9oTDAktvREy5OT9OCljp1Tah0y3+p8UPMTAoabWaLZMdzYruOtF9/9GRf6MasaZjiZP1A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bson": "^6.2.0",
|
"bson": "^6.2.0",
|
||||||
"kareem": "2.5.1",
|
"kareem": "2.5.1",
|
||||||
@@ -11873,11 +11879,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "6.21.3",
|
"version": "6.22.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.1.tgz",
|
||||||
"integrity": "sha512-a0H638ZXULv1OdkmiK6s6itNhoy33ywxmUFT/xtSoVyf9VnC7n7+VT4LjVzdIHSaF5TIh9ylUgxMXksHTgGrKg==",
|
"integrity": "sha512-0pdoRGwLtemnJqn1K0XHUbnKiX0S4X8CgvVVmHGOWmofESj31msHo/1YiqcJWK7Wxfq2a4uvvtS01KAQyWK/CQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@remix-run/router": "1.14.2"
|
"@remix-run/router": "1.15.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
@@ -11887,12 +11893,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "6.21.3",
|
"version": "6.22.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.1.tgz",
|
||||||
"integrity": "sha512-kNzubk7n4YHSrErzjLK72j0B5i969GsuCGazRl3G6j1zqZBLjuSlYBdVdkDOgzGdPIffUOc9nmgiadTEVoq91g==",
|
"integrity": "sha512-iwMyyyrbL7zkKY7MRjOVRy+TMnS/OPusaFVxM2P11x9dzSzGmLsebkCvYirGq0DWB9K9hOspHYYtDz33gE5Duw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@remix-run/router": "1.14.2",
|
"@remix-run/router": "1.15.1",
|
||||||
"react-router": "6.21.3"
|
"react-router": "6.22.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
@@ -13327,9 +13333,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint-config-recess-order": {
|
"node_modules/stylelint-config-recess-order": {
|
||||||
"version": "4.4.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-4.6.0.tgz",
|
||||||
"integrity": "sha512-Q99kvZyIM/aoPEV4dRDkzD3fZLzH0LXi+pawCf1r700uUeF/PHQ5PZXjwFUuGrWhOzd1N+cuVm+OUGsY2fRN5A==",
|
"integrity": "sha512-V76fhv3YtcNXh/hyAuAdSzi5FmcrG54Mp2AThJ3D/PTMTSYzUPd7GIhP6z9mTqnRhmkk6YTfcu/JWB8h+Yrcaw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"stylelint-order": "6.x"
|
"stylelint-order": "6.x"
|
||||||
|
|||||||
16
package.json
16
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": "3.10.0",
|
"version": "3.11.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"npm": "^10.2.x",
|
"npm": "^10.2.x",
|
||||||
"node": "^20.8.x"
|
"node": "^20.8.x"
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"test:coverage": "jest --coverage --silent --runInBand",
|
"test:coverage": "jest --coverage --silent --runInBand",
|
||||||
"test:dev": "jest --verbose --watch",
|
"test:dev": "jest --verbose --watch",
|
||||||
"test:basic": "jest tests/markdown/basic.test.js --verbose",
|
"test:basic": "jest tests/markdown/basic.test.js --verbose",
|
||||||
|
"test:variables": "jest tests/markdown/variables.test.js --verbose",
|
||||||
"test:mustache-syntax": "jest '.*(mustache-syntax).*' --verbose --noStackTrace",
|
"test:mustache-syntax": "jest '.*(mustache-syntax).*' --verbose --noStackTrace",
|
||||||
"test:mustache-syntax:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --verbose --noStackTrace",
|
"test:mustache-syntax:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --verbose --noStackTrace",
|
||||||
"test:mustache-syntax:block": "jest '.*(mustache-syntax).*' -t '^Block:.*' --verbose --noStackTrace",
|
"test:mustache-syntax:block": "jest '.*(mustache-syntax).*' -t '^Block:.*' --verbose --noStackTrace",
|
||||||
@@ -83,13 +84,14 @@
|
|||||||
"@babel/plugin-transform-runtime": "^7.23.9",
|
"@babel/plugin-transform-runtime": "^7.23.9",
|
||||||
"@babel/preset-env": "^7.23.9",
|
"@babel/preset-env": "^7.23.9",
|
||||||
"@babel/preset-react": "^7.23.3",
|
"@babel/preset-react": "^7.23.3",
|
||||||
"@googleapis/drive": "^8.6.0",
|
"@googleapis/drive": "^8.7.0",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"codemirror": "^5.65.6",
|
"codemirror": "^5.65.6",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"create-react-class": "^15.7.0",
|
"create-react-class": "^15.7.0",
|
||||||
"dedent-tabs": "^0.10.3",
|
"dedent-tabs": "^0.10.3",
|
||||||
|
"expr-eval": "^2.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-async-handler": "^1.2.0",
|
"express-async-handler": "^1.2.0",
|
||||||
"express-static-gzip": "2.1.7",
|
"express-static-gzip": "2.1.7",
|
||||||
@@ -100,30 +102,30 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "11.2.0",
|
"marked": "11.2.0",
|
||||||
"marked-extended-tables": "^1.0.8",
|
"marked-extended-tables": "^1.0.8",
|
||||||
"marked-gfm-heading-id": "^3.1.2",
|
"marked-gfm-heading-id": "^3.1.3",
|
||||||
"marked-smartypants-lite": "^1.0.2",
|
"marked-smartypants-lite": "^1.0.2",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"mongoose": "^8.1.1",
|
"mongoose": "^8.2.0",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"nconf": "^0.12.1",
|
"nconf": "^0.12.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-frame-component": "^4.1.3",
|
"react-frame-component": "^4.1.3",
|
||||||
"react-router-dom": "6.21.3",
|
"react-router-dom": "6.22.1",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^8.1.2",
|
"superagent": "^8.1.2",
|
||||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-plugin-jest": "^27.6.3",
|
"eslint-plugin-jest": "^27.9.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
"stylelint": "^15.11.0",
|
"stylelint": "^15.11.0",
|
||||||
"stylelint-config-recess-order": "^4.4.0",
|
"stylelint-config-recess-order": "^4.6.0",
|
||||||
"stylelint-config-recommended": "^13.0.0",
|
"stylelint-config-recommended": "^13.0.0",
|
||||||
"stylelint-stylistic": "^0.4.3",
|
"stylelint-stylistic": "^0.4.3",
|
||||||
"supertest": "^6.3.4"
|
"supertest": "^6.3.4"
|
||||||
|
|||||||
@@ -4,7 +4,40 @@ const Marked = require('marked');
|
|||||||
const MarkedExtendedTables = require('marked-extended-tables');
|
const MarkedExtendedTables = require('marked-extended-tables');
|
||||||
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite');
|
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite');
|
||||||
const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id');
|
const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id');
|
||||||
|
const MathParser = require('expr-eval').Parser;
|
||||||
const renderer = new Marked.Renderer();
|
const renderer = new Marked.Renderer();
|
||||||
|
const tokenizer = new Marked.Tokenizer();
|
||||||
|
|
||||||
|
//Limit math features to simple items
|
||||||
|
const mathParser = new MathParser({
|
||||||
|
operators : {
|
||||||
|
// These default to true, but are included to be explicit
|
||||||
|
add : true,
|
||||||
|
subtract : true,
|
||||||
|
multiply : true,
|
||||||
|
divide : true,
|
||||||
|
power : true,
|
||||||
|
round : true,
|
||||||
|
floor : true,
|
||||||
|
ceil : true,
|
||||||
|
|
||||||
|
sin : false, cos : false, tan : false, asin : false, acos : false,
|
||||||
|
atan : false, sinh : false, cosh : false, tanh : false, asinh : false,
|
||||||
|
acosh : false, atanh : false, sqrt : false, cbrt : false, log : false,
|
||||||
|
log2 : false, ln : false, lg : false, log10 : false, expm1 : false,
|
||||||
|
log1p : false, abs : false, trunc : false, join : false, sum : false,
|
||||||
|
'-' : false, '+' : false, exp : false, not : false, length : false,
|
||||||
|
'!' : false, sign : false, random : false, fac : false, min : false,
|
||||||
|
max : false, hypot : false, pyt : false, pow : false, atan2 : false,
|
||||||
|
'if' : false, gamma : false, roundTo : false, map : false, fold : false,
|
||||||
|
filter : false, indexOf : false,
|
||||||
|
|
||||||
|
remainder : false, factorial : false,
|
||||||
|
comparison : false, concatenate : false,
|
||||||
|
logical : false, assignment : false,
|
||||||
|
array : false, fndef : false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//Processes the markdown within an HTML block if it's just a class-wrapper
|
//Processes the markdown within an HTML block if it's just a class-wrapper
|
||||||
renderer.html = function (html) {
|
renderer.html = function (html) {
|
||||||
@@ -50,6 +83,11 @@ renderer.link = function (href, title, text) {
|
|||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Disable default reflink behavior, as it steps on our variables extension
|
||||||
|
tokenizer.def = function () {
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const mustacheSpans = {
|
const mustacheSpans = {
|
||||||
name : 'mustacheSpans',
|
name : 'mustacheSpans',
|
||||||
level : 'inline', // Is this a block-level or inline-level tokenizer?
|
level : 'inline', // Is this a block-level or inline-level tokenizer?
|
||||||
@@ -288,9 +326,255 @@ const definitionLists = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
|
||||||
|
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
|
||||||
|
const regex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g;
|
||||||
|
const match = regex.exec(input);
|
||||||
|
|
||||||
|
const prefix = match[1];
|
||||||
|
const label = match[2];
|
||||||
|
|
||||||
|
//v=====--------------------< HANDLE MATH >-------------------=====v//
|
||||||
|
const mathRegex = /[a-z]+\(|[+\-*/^()]/g;
|
||||||
|
const matches = label.split(mathRegex);
|
||||||
|
const mathVars = matches.filter((match)=>isNaN(match))?.map((s)=>s.trim()); // Capture any variable names
|
||||||
|
|
||||||
|
let replacedLabel = label;
|
||||||
|
|
||||||
|
if(prefix[0] == '$' && mathVars?.[0] !== label.trim()) {// If there was mathy stuff not captured, let's do math!
|
||||||
|
mathVars?.forEach((variable)=>{
|
||||||
|
const foundVar = lookupVar(variable, globalPageNumber, hoist);
|
||||||
|
if(foundVar && foundVar.resolved && foundVar.content && !isNaN(foundVar.content)) // Only subsitute math values if fully resolved, not empty strings, and numbers
|
||||||
|
replacedLabel = replacedLabel.replaceAll(variable, foundVar.content);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
return mathParser.evaluate(replacedLabel);
|
||||||
|
} catch (error) {
|
||||||
|
return undefined; // Return undefined if invalid math result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//^=====--------------------< HANDLE MATH >-------------------=====^//
|
||||||
|
|
||||||
|
const foundVar = lookupVar(label, globalPageNumber, hoist);
|
||||||
|
|
||||||
|
if(!foundVar || (!foundVar.resolved && !allowUnresolved))
|
||||||
|
return undefined; // Return undefined if not found, or parially-resolved vars are not allowed
|
||||||
|
|
||||||
|
// url or <url> "title" or 'title' or (title)
|
||||||
|
const linkRegex = /^([^<\s][^\s]*|<.*?>)(?: ("(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\((?:\\\(|\\\)|[^()])*\)))?$/m;
|
||||||
|
const linkMatch = linkRegex.exec(foundVar.content);
|
||||||
|
|
||||||
|
const href = linkMatch ? linkMatch[1] : null; //TODO: TRIM OFF < > IF PRESENT
|
||||||
|
const title = linkMatch ? linkMatch[2]?.slice(1, -1) : null;
|
||||||
|
|
||||||
|
if(!prefix[0] && href) // Link
|
||||||
|
return `[${label}](${href}${title ? ` "${title}"` : ''})`;
|
||||||
|
|
||||||
|
if(prefix[0] == '!' && href) // Image
|
||||||
|
return ``;
|
||||||
|
|
||||||
|
if(prefix[0] == '$') // Variable
|
||||||
|
return foundVar.content;
|
||||||
|
};
|
||||||
|
|
||||||
|
const lookupVar = function(label, index, hoist=false) {
|
||||||
|
while (index >= 0) {
|
||||||
|
if(globalVarsList[index]?.[label] !== undefined)
|
||||||
|
return globalVarsList[index][label];
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hoist) { //If normal lookup failed, attempt hoisting
|
||||||
|
index = Object.keys(globalVarsList).length; // Move index to start from last page
|
||||||
|
while (index >= 0) {
|
||||||
|
if(globalVarsList[index]?.[label] !== undefined)
|
||||||
|
return globalVarsList[index][label];
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const processVariableQueue = function() {
|
||||||
|
let resolvedOne = true;
|
||||||
|
let finalLoop = false;
|
||||||
|
while (resolvedOne || finalLoop) { // Loop through queue until no more variable calls can be resolved
|
||||||
|
resolvedOne = false;
|
||||||
|
for (const item of varsQueue) {
|
||||||
|
if(item.type == 'text')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(item.type == 'varDefBlock') {
|
||||||
|
const regex = /[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g;
|
||||||
|
let match;
|
||||||
|
let resolved = true;
|
||||||
|
let tempContent = item.content;
|
||||||
|
while (match = regex.exec(item.content)) { // regex to find variable calls
|
||||||
|
const value = replaceVar(match[0], true);
|
||||||
|
|
||||||
|
if(value == undefined)
|
||||||
|
resolved = false;
|
||||||
|
else
|
||||||
|
tempContent = tempContent.replaceAll(match[0], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resolved == true || item.content != tempContent) {
|
||||||
|
resolvedOne = true;
|
||||||
|
item.content = tempContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalVarsList[globalPageNumber][item.varName] = {
|
||||||
|
content : item.content,
|
||||||
|
resolved : resolved
|
||||||
|
};
|
||||||
|
|
||||||
|
if(resolved)
|
||||||
|
item.type = 'resolved';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(item.type == 'varCallBlock' || item.type == 'varCallInline') {
|
||||||
|
const value = replaceVar(item.content, true, finalLoop); // final loop will just use the best value so far
|
||||||
|
|
||||||
|
if(value == undefined)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
resolvedOne = true;
|
||||||
|
item.content = value;
|
||||||
|
item.type = 'text';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
varsQueue = varsQueue.filter((item)=>item.type !== 'resolved'); // Remove any fully-resolved variable definitions
|
||||||
|
|
||||||
|
if(finalLoop)
|
||||||
|
break;
|
||||||
|
if(!resolvedOne)
|
||||||
|
finalLoop = true;
|
||||||
|
}
|
||||||
|
varsQueue = varsQueue.filter((item)=>item.type !== 'varDefBlock');
|
||||||
|
};
|
||||||
|
|
||||||
|
function MarkedVariables() {
|
||||||
|
return {
|
||||||
|
hooks : {
|
||||||
|
preprocess(src) {
|
||||||
|
const codeBlockSkip = /^(?: {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+|^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})(?:[^\n]*)(?:\n|$)(?:|(?:[\s\S]*?)(?:\n|$))(?: {0,3}\2[~`]* *(?=\n|$))|`[^`]*?`/;
|
||||||
|
const blockDefRegex = /^[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]:(?!\() *((?:\n? *[^\s].*)+)(?=\n+|$)/; //Matches 3, [4]:5
|
||||||
|
const blockCallRegex = /^[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\](?=\n|$)/; //Matches 6, [7]
|
||||||
|
const inlineDefRegex = /([!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\])\(([^\n]+)\)/; //Matches 8, 9[10](11)
|
||||||
|
const inlineCallRegex = /[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\](?!\()/; //Matches 12, [13]
|
||||||
|
|
||||||
|
// Combine regexes and wrap in parens like so: (regex1)|(regex2)|(regex3)|(regex4)
|
||||||
|
const combinedRegex = new RegExp([codeBlockSkip, blockDefRegex, blockCallRegex, inlineDefRegex, inlineCallRegex].map((s)=>`(${s.source})`).join('|'), 'gm');
|
||||||
|
|
||||||
|
let lastIndex = 0;
|
||||||
|
let match;
|
||||||
|
while ((match = combinedRegex.exec(src)) !== null) {
|
||||||
|
// Format any matches into tokens and store
|
||||||
|
if(match.index > lastIndex) { // Any non-variable stuff
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'text',
|
||||||
|
varName : null,
|
||||||
|
content : src.slice(lastIndex, match.index)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[1]) {
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'text',
|
||||||
|
varName : null,
|
||||||
|
content : match[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[3]) { // Block Definition
|
||||||
|
const label = match[4] ? match[4].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
const content = match[5] ? match[5].trim().replace(/[ \t]+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varDefBlock',
|
||||||
|
varName : label,
|
||||||
|
content : content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[6]) { // Block Call
|
||||||
|
const label = match[7] ? match[7].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varCallBlock',
|
||||||
|
varName : label,
|
||||||
|
content : match[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[8]) { // Inline Definition
|
||||||
|
const label = match[10] ? match[10].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
let content = match[11] ? match[11].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
|
||||||
|
// In case of nested (), find the correct matching end )
|
||||||
|
let level = 0;
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < content.length; i++) {
|
||||||
|
if(content[i] === '\\') {
|
||||||
|
i++;
|
||||||
|
} else if(content[i] === '(') {
|
||||||
|
level++;
|
||||||
|
} else if(content[i] === ')') {
|
||||||
|
level--;
|
||||||
|
if(level < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i > -1) {
|
||||||
|
combinedRegex.lastIndex = combinedRegex.lastIndex - (content.length - i);
|
||||||
|
content = content.slice(0, i).trim().replace(/\s+/g, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varDefBlock',
|
||||||
|
varName : label,
|
||||||
|
content : content
|
||||||
|
});
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varCallInline',
|
||||||
|
varName : label,
|
||||||
|
content : match[9]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[12]) { // Inline Call
|
||||||
|
const label = match[13] ? match[13].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space
|
||||||
|
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'varCallInline',
|
||||||
|
varName : label,
|
||||||
|
content : match[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
lastIndex = combinedRegex.lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastIndex < src.length) {
|
||||||
|
varsQueue.push(
|
||||||
|
{ type : 'text',
|
||||||
|
varName : null,
|
||||||
|
content : src.slice(lastIndex)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
processVariableQueue();
|
||||||
|
|
||||||
|
const output = varsQueue.map((item)=>item.content).join('');
|
||||||
|
varsQueue = []; // Must clear varsQueue because custom HTML renderer uses Marked.parse which will preprocess again without clearing the array
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
//^=====--------------------< Variable Handling >-------------------=====^//
|
||||||
|
|
||||||
|
Marked.use(MarkedVariables());
|
||||||
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists, superSubScripts] });
|
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists, superSubScripts] });
|
||||||
Marked.use(mustacheInjectBlock);
|
Marked.use(mustacheInjectBlock);
|
||||||
Marked.use({ renderer: renderer, mangle: false });
|
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
||||||
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());
|
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());
|
||||||
|
|
||||||
const nonWordAndColonTest = /[^\w:]/g;
|
const nonWordAndColonTest = /[^\w:]/g;
|
||||||
@@ -369,12 +653,28 @@ const processStyleTags = (string)=>{
|
|||||||
`${attributes?.length ? ` ${attributes.join(' ')}` : ''}`;
|
`${attributes?.length ? ` ${attributes.join(' ')}` : ''}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const globalVarsList = {};
|
||||||
|
let varsQueue = [];
|
||||||
|
let globalPageNumber = 0;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
marked : Marked,
|
marked : Marked,
|
||||||
render : (rawBrewText)=>{
|
render : (rawBrewText, pageNumber=1)=>{
|
||||||
|
globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
|
||||||
|
varsQueue = []; //Could move into MarkedVariables()
|
||||||
|
globalPageNumber = pageNumber;
|
||||||
|
|
||||||
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
||||||
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
||||||
return Marked.parse(rawBrewText);
|
const opts = Marked.defaults;
|
||||||
|
|
||||||
|
rawBrewText = opts.hooks.preprocess(rawBrewText);
|
||||||
|
const tokens = Marked.lexer(rawBrewText, opts);
|
||||||
|
|
||||||
|
Marked.walkTokens(tokens, opts.walkTokens);
|
||||||
|
|
||||||
|
const html = Marked.parser(tokens, opts);
|
||||||
|
return opts.hooks.postprocess(html);
|
||||||
},
|
},
|
||||||
|
|
||||||
validate : (rawBrewText)=>{
|
validate : (rawBrewText)=>{
|
||||||
|
|||||||
373
tests/markdown/variables.test.js
Normal file
373
tests/markdown/variables.test.js
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
|
const dedent = require('dedent-tabs').default;
|
||||||
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
|
// Marked.js adds line returns after closing tags on some default tokens.
|
||||||
|
// This removes those line returns for comparison sake.
|
||||||
|
String.prototype.trimReturns = function(){
|
||||||
|
return this.replace(/\r?\n|\r/g, '').trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
renderAllPages = function(pages){
|
||||||
|
const outputs = [];
|
||||||
|
pages.forEach((page, index)=>{
|
||||||
|
const output = Markdown.render(page, index);
|
||||||
|
outputs.push(output);
|
||||||
|
});
|
||||||
|
|
||||||
|
return outputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adding `.failing()` method to `describe` or `it` will make failing tests "pass" as long as they continue to fail.
|
||||||
|
// Remove the `.failing()` method once you have fixed the issue.
|
||||||
|
|
||||||
|
describe('Block-level variables', ()=>{
|
||||||
|
it('Handles variable assignment and recall with simple text', function() {
|
||||||
|
const source = dedent`
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable assignment and recall with multiline string', function() {
|
||||||
|
const source = dedent`
|
||||||
|
[var]: string
|
||||||
|
across multiple
|
||||||
|
lines
|
||||||
|
|
||||||
|
$[var]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>string across multiple lines</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable assignment and recall with tables', function() {
|
||||||
|
const source = dedent`
|
||||||
|
[var]:
|
||||||
|
##### Title
|
||||||
|
| H1 | H2 |
|
||||||
|
|:---|:--:|
|
||||||
|
| A | B |
|
||||||
|
| C | D |
|
||||||
|
|
||||||
|
$[var]`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<h5 id="title">Title</h5>
|
||||||
|
<table><thead><tr><th align=left>H1</th>
|
||||||
|
<th align=center>H2</th>
|
||||||
|
</tr></thead><tbody><tr><td align=left>A</td>
|
||||||
|
<td align=center>B</td>
|
||||||
|
</tr><tr><td align=left>C</td>
|
||||||
|
<td align=center>D</td>
|
||||||
|
</tr></tbody></table>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Hoists undefined variables', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: string`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Hoists last instance of variable', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
[var]: new string`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>new string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles complex hoisting', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[titleAndName]: $[title] $[fullName]
|
||||||
|
|
||||||
|
$[title]: Mr.
|
||||||
|
|
||||||
|
$[fullName]: $[firstName] $[lastName]
|
||||||
|
|
||||||
|
[firstName]: Bob
|
||||||
|
|
||||||
|
Welcome, $[titleAndName]!
|
||||||
|
|
||||||
|
[lastName]: Jacob
|
||||||
|
|
||||||
|
[lastName]: $[lastName]son
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>Welcome, Mr. Bob Jacobson!</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable reassignment', function() {
|
||||||
|
const source = dedent`
|
||||||
|
[var]: one
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: two
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>one</p><p>two</p>'.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable reassignment with hoisting', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: one
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
[var]: two
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>two</p><p>one</p><p>two</p>'.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ignores undefined variables that can\'t be hoisted', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var](My name is $[first] $[last])
|
||||||
|
|
||||||
|
$[last]: Jones
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>My name is $[first] Jones</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Inline-level variables', ()=>{
|
||||||
|
it('Handles variable assignment and recall with simple text', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var](string)
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>string</p><p>string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Hoists undefined variables when possible', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var](My name is $[name] Jones)
|
||||||
|
|
||||||
|
[name]: Bob`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>My name is Bob Jones</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Hoists last instance of variable', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var](My name is $[name] Jones)
|
||||||
|
|
||||||
|
$[name](Bob)
|
||||||
|
|
||||||
|
[name]: Bill`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>My name is Bill Jones</p> <p>Bob</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Only captures nested parens if balanced', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[var1](A variable (with nested parens) inside)
|
||||||
|
|
||||||
|
$[var1]
|
||||||
|
|
||||||
|
$[var2](A variable ) with unbalanced parens)
|
||||||
|
|
||||||
|
$[var2]`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>A variable (with nested parens) inside</p>
|
||||||
|
<p>A variable (with nested parens) inside</p>
|
||||||
|
<p>A variable with unbalanced parens)</p>
|
||||||
|
<p>A variable</p>
|
||||||
|
`.trimReturns());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Math', ()=>{
|
||||||
|
it('Handles simple math using numbers only', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[1 + 3 * 5 - (1 / 4)]
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>15.75</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles round function', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[round(1/4)]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>0</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles floor function', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[floor(0.6)]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>0</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles ceil function', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[ceil(0.2)]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>1</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles nested functions', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[ceil(floor(round(0.6)))]`;
|
||||||
|
const rendered = Markdown.render(source).replace(/\s/g, ' ').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>1</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles simple math with variables', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[num1]: 5
|
||||||
|
|
||||||
|
$[num2]: 4
|
||||||
|
|
||||||
|
Answer is $[answer]($[1 + 3 * num1 - (1 / num2)]).
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>Answer is 15.75.</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles variable incrementing', function() {
|
||||||
|
const source = dedent`
|
||||||
|
$[num1]: 5
|
||||||
|
|
||||||
|
Increment num1 to get $[num1]($[num1 + 1]) and again to $[num1]($[num1 + 1]).
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p>Increment num1 to get 6 and again to 7.</p>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Code blocks', ()=>{
|
||||||
|
it('Ignores all variables in fenced code blocks', function() {
|
||||||
|
const source = dedent`
|
||||||
|
\`\`\`
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
$[var](new string)
|
||||||
|
\`\`\`
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<pre><code>
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
$[var](new string)
|
||||||
|
</code></pre>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ignores all variables in indented code blocks', function() {
|
||||||
|
const source = dedent`
|
||||||
|
test
|
||||||
|
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
$[var](new string)
|
||||||
|
`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>test</p>
|
||||||
|
|
||||||
|
<pre><code>
|
||||||
|
[var]: string
|
||||||
|
|
||||||
|
$[var]
|
||||||
|
|
||||||
|
$[var](new string)
|
||||||
|
</code></pre>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ignores all variables in inline code blocks', function() {
|
||||||
|
const source = '[var](Hello) `[link](url)`. This `[var] does not work`';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p><a href="Hello">var</a> <code>[link](url)</code>. This <code>[var] does not work</code></p>`.trimReturns());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Normal Links and Images', ()=>{
|
||||||
|
it('Renders normal images', function() {
|
||||||
|
const source = ``;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p><img src="url" alt="alt text"></p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders normal images with a title', function() {
|
||||||
|
const source = 'An image !';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>An image <img src="url" alt="alt text" title="and title">!</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Applies curly injectors to images', function() {
|
||||||
|
const source = `{width:100px}`;
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p><img class="" style="width:100px;" src="url" alt="alt text"></p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders normal links', function() {
|
||||||
|
const source = 'A Link to my [website](url)!';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>A Link to my <a href="url">website</a>!</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders normal links with a title', function() {
|
||||||
|
const source = 'A Link to my [website](url "and title")!';
|
||||||
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
|
<p>A Link to my <a href="url" title="and title">website</a>!</p>`.trimReturns());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Cross-page variables', ()=>{
|
||||||
|
it('Handles variable assignment and recall across pages', function() {
|
||||||
|
const source0 = `[var]: string`;
|
||||||
|
const source1 = `$[var]`;
|
||||||
|
const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('\\page<p>string</p>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles hoisting across pages', function() {
|
||||||
|
const source0 = `$[var]`;
|
||||||
|
const source1 = `[var]: string`;
|
||||||
|
renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up
|
||||||
|
const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('<p>string</p>\\page');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Handles reassignment and hoisting across pages', function() {
|
||||||
|
const source0 = `$[var]\n\n[var]: one\n\n$[var]`;
|
||||||
|
const source1 = `[var]: two\n\n$[var]`;
|
||||||
|
renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up
|
||||||
|
const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns();
|
||||||
|
expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('<p>two</p><p>one</p>\\page<p>two</p>');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -307,7 +307,7 @@
|
|||||||
margin-left : -0.16cm;
|
margin-left : -0.16cm;
|
||||||
background-color : var(--HB_Color_MonsterStatBackground);
|
background-color : var(--HB_Color_MonsterStatBackground);
|
||||||
background-image : @monsterBlockBackground;
|
background-image : @monsterBlockBackground;
|
||||||
background-attachment : fixed;
|
background-attachment : unset;
|
||||||
background-blend-mode : overlay;
|
background-blend-mode : overlay;
|
||||||
border-style : solid;
|
border-style : solid;
|
||||||
border-width : 7px 6px;
|
border-width : 7px 6px;
|
||||||
|
|||||||
121
themes/codeMirror/customThemes/darkvision.css
Normal file
121
themes/codeMirror/customThemes/darkvision.css
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
.CodeMirror {
|
||||||
|
background: #0C0C0C;
|
||||||
|
color: #B9BDB6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Brew BG */
|
||||||
|
.brewRenderer {
|
||||||
|
background-color: #0C0C0C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-darkvision {
|
||||||
|
/* Blinking cursor and selection */
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
border-left: 1px solid #B9BDB6;
|
||||||
|
}
|
||||||
|
.CodeMirror-selected {
|
||||||
|
background: #E0E8FF40;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Line number stuff */
|
||||||
|
.CodeMirror-gutter-elt {
|
||||||
|
color: #81969A;
|
||||||
|
}
|
||||||
|
.CodeMirror-linenumber {
|
||||||
|
background-color: #0C0C0C;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter {
|
||||||
|
background-color: #0C0C0C;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* column splits */
|
||||||
|
.editor .codeEditor .columnSplit {
|
||||||
|
font-style: italic;
|
||||||
|
color: inherit;
|
||||||
|
background-color:#1F5763;
|
||||||
|
border-bottom: #299 solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* # headings */
|
||||||
|
.cm-header {
|
||||||
|
color: #C51B1B;
|
||||||
|
-webkit-text-stroke-width: 0.1px;
|
||||||
|
}
|
||||||
|
/* bold points */
|
||||||
|
.cm-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #309DD2;
|
||||||
|
}
|
||||||
|
/* Link headings */
|
||||||
|
.cm-link {
|
||||||
|
color: #DD6300;
|
||||||
|
}
|
||||||
|
/* links */
|
||||||
|
.cm-string {
|
||||||
|
color: #5CE638;
|
||||||
|
}
|
||||||
|
/*@import*/
|
||||||
|
.cm-def {
|
||||||
|
color: #2986CC;
|
||||||
|
}
|
||||||
|
/* Bullets and such */
|
||||||
|
.cm-variable-2 {
|
||||||
|
color: #3CBF30;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tags (divs) */
|
||||||
|
.cm-tag {
|
||||||
|
color: #E3FF00;
|
||||||
|
}
|
||||||
|
.cm-attribute {
|
||||||
|
color: #E3FF00;
|
||||||
|
}
|
||||||
|
.cm-atom {
|
||||||
|
color: #CF7EA9;
|
||||||
|
}
|
||||||
|
.cm-qualifier {
|
||||||
|
color: #EE1919;
|
||||||
|
}
|
||||||
|
.cm-comment {
|
||||||
|
color: #BBC700;
|
||||||
|
}
|
||||||
|
.cm-keyword {
|
||||||
|
color: #CC66FF;
|
||||||
|
}
|
||||||
|
.cm-property {
|
||||||
|
color: aqua;
|
||||||
|
}
|
||||||
|
.cm-error {
|
||||||
|
color: #C50202;
|
||||||
|
}
|
||||||
|
.CodeMirror-foldmarker {
|
||||||
|
color: #F0FF00;
|
||||||
|
}
|
||||||
|
/* New page */
|
||||||
|
.cm-builtin {
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor .codeEditor {
|
||||||
|
/* blocks */
|
||||||
|
.block:not(.cm-comment) {
|
||||||
|
color: magenta;
|
||||||
|
}
|
||||||
|
/* definition lists */
|
||||||
|
.define.definition {
|
||||||
|
color: #FFAA3E;
|
||||||
|
}
|
||||||
|
.define.term {
|
||||||
|
color: #7290d9;
|
||||||
|
}
|
||||||
|
.define:not(.term):not(.definition) {
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
/* New page */
|
||||||
|
.pageLine {
|
||||||
|
background: #000;
|
||||||
|
color: #000;
|
||||||
|
border-bottom: 1px solid #FFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
"colorforth",
|
"colorforth",
|
||||||
"darcula",
|
"darcula",
|
||||||
"darkbrewery-v301",
|
"darkbrewery-v301",
|
||||||
|
"darkvision",
|
||||||
"dracula",
|
"dracula",
|
||||||
"duotone-dark",
|
"duotone-dark",
|
||||||
"duotone-light",
|
"duotone-light",
|
||||||
|
|||||||
118
themes/fonts/5e/dicefont.less
Normal file
118
themes/fonts/5e/dicefont.less
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
Icon Font: dicefont
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DiceFont';
|
||||||
|
src: url('../../../fonts/5e/dicefont.woff2') format('woff2'),
|
||||||
|
url('../../../fonts/5e/dicefont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.df {
|
||||||
|
display: inline-block;
|
||||||
|
font-family: 'DiceFont';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
line-height: 1;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
text-transform: none;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-smooth: antialiased;
|
||||||
|
&.F:before { content: '\f190'; }
|
||||||
|
&.F-minus:before { content: '\f191'; }
|
||||||
|
&.F-plus:before { content: '\f192'; }
|
||||||
|
&.F-zero:before { content: '\f193'; }
|
||||||
|
&.d10:before { content: '\f194'; }
|
||||||
|
&.d10-0:before { content: '\f100'; }
|
||||||
|
&.d10-1:before { content: '\f101'; }
|
||||||
|
&.d10-10:before { content: '\f102'; }
|
||||||
|
&.d10-2:before { content: '\f103'; }
|
||||||
|
&.d10-3:before { content: '\f104'; }
|
||||||
|
&.d10-4:before { content: '\f105'; }
|
||||||
|
&.d10-5:before { content: '\f106'; }
|
||||||
|
&.d10-6:before { content: '\f107'; }
|
||||||
|
&.d10-7:before { content: '\f108'; }
|
||||||
|
&.d10-8:before { content: '\f109'; }
|
||||||
|
&.d10-9:before { content: '\f10a'; }
|
||||||
|
&.d12:before { content: '\f195'; }
|
||||||
|
&.d12-1:before { content: '\f10b'; }
|
||||||
|
&.d12-10:before { content: '\f10c'; }
|
||||||
|
&.d12-11:before { content: '\f10d'; }
|
||||||
|
&.d12-12:before { content: '\f10e'; }
|
||||||
|
&.d12-2:before { content: '\f10f'; }
|
||||||
|
&.d12-3:before { content: '\f110'; }
|
||||||
|
&.d12-4:before { content: '\f111'; }
|
||||||
|
&.d12-5:before { content: '\f112'; }
|
||||||
|
&.d12-6:before { content: '\f113'; }
|
||||||
|
&.d12-7:before { content: '\f114'; }
|
||||||
|
&.d12-8:before { content: '\f115'; }
|
||||||
|
&.d12-9:before { content: '\f116'; }
|
||||||
|
&.d2:before { content: '\f196'; }
|
||||||
|
&.d2-1:before { content: '\f117'; }
|
||||||
|
&.d2-2:before { content: '\f118'; }
|
||||||
|
&.d20:before { content: '\f197'; }
|
||||||
|
&.d20-1:before { content: '\f119'; }
|
||||||
|
&.d20-10:before { content: '\f11a'; }
|
||||||
|
&.d20-11:before { content: '\f11b'; }
|
||||||
|
&.d20-12:before { content: '\f11c'; }
|
||||||
|
&.d20-13:before { content: '\f11d'; }
|
||||||
|
&.d20-14:before { content: '\f11e'; }
|
||||||
|
&.d20-15:before { content: '\f11f'; }
|
||||||
|
&.d20-16:before { content: '\f120'; }
|
||||||
|
&.d20-17:before { content: '\f121'; }
|
||||||
|
&.d20-18:before { content: '\f122'; }
|
||||||
|
&.d20-19:before { content: '\f123'; }
|
||||||
|
&.d20-2:before { content: '\f124'; }
|
||||||
|
&.d20-20:before { content: '\f125'; }
|
||||||
|
&.d20-3:before { content: '\f126'; }
|
||||||
|
&.d20-4:before { content: '\f127'; }
|
||||||
|
&.d20-5:before { content: '\f128'; }
|
||||||
|
&.d20-6:before { content: '\f129'; }
|
||||||
|
&.d20-7:before { content: '\f12a'; }
|
||||||
|
&.d20-8:before { content: '\f12b'; }
|
||||||
|
&.d20-9:before { content: '\f12c'; }
|
||||||
|
&.d4:before { content: '\f198'; }
|
||||||
|
&.d4-1:before { content: '\f12d'; }
|
||||||
|
&.d4-2:before { content: '\f12e'; }
|
||||||
|
&.d4-3:before { content: '\f12f'; }
|
||||||
|
&.d4-4:before { content: '\f130'; }
|
||||||
|
&.d6:before { content: '\f199'; }
|
||||||
|
&.d6-1:before { content: '\f131'; }
|
||||||
|
&.d6-2:before { content: '\f132'; }
|
||||||
|
&.d6-3:before { content: '\f133'; }
|
||||||
|
&.d6-4:before { content: '\f134'; }
|
||||||
|
&.d6-5:before { content: '\f135'; }
|
||||||
|
&.d6-6:before { content: '\f136'; }
|
||||||
|
&.d8:before { content: '\f19a'; }
|
||||||
|
&.d8-1:before { content: '\f137'; }
|
||||||
|
&.d8-2:before { content: '\f138'; }
|
||||||
|
&.d8-3:before { content: '\f139'; }
|
||||||
|
&.d8-4:before { content: '\f13a'; }
|
||||||
|
&.d8-5:before { content: '\f13b'; }
|
||||||
|
&.d8-6:before { content: '\f13c'; }
|
||||||
|
&.d8-7:before { content: '\f13d'; }
|
||||||
|
&.d8-8:before { content: '\f13e'; }
|
||||||
|
&.dot-d6:before { content: '\f19b'; }
|
||||||
|
&.dot-d6-1:before { content: '\f13f'; }
|
||||||
|
&.dot-d6-2:before { content: '\f140'; }
|
||||||
|
&.dot-d6-3:before { content: '\f141'; }
|
||||||
|
&.dot-d6-4:before { content: '\f142'; }
|
||||||
|
&.dot-d6-5:before { content: '\f143'; }
|
||||||
|
&.dot-d6-6:before { content: '\f18f'; }
|
||||||
|
&.small-dot-d6-1:before { content: '\f183'; }
|
||||||
|
&.small-dot-d6-2:before { content: '\f184'; }
|
||||||
|
&.small-dot-d6-3:before { content: '\f185'; }
|
||||||
|
&.small-dot-d6-4:before { content: '\f186'; }
|
||||||
|
&.small-dot-d6-5:before { content: '\f187'; }
|
||||||
|
&.small-dot-d6-6:before { content: '\f188'; }
|
||||||
|
&.solid-small-dot-d6-1:before { content: '\f189'; }
|
||||||
|
&.solid-small-dot-d6-2:before { content: '\f18a'; }
|
||||||
|
&.solid-small-dot-d6-3:before { content: '\f18b'; }
|
||||||
|
&.solid-small-dot-d6-4:before { content: '\f18c'; }
|
||||||
|
&.solid-small-dot-d6-5:before { content: '\f18d'; }
|
||||||
|
&.solid-small-dot-d6-6:before { content: '\f18e'; }
|
||||||
|
}
|
||||||
BIN
themes/fonts/5e/dicefont.woff
Normal file
BIN
themes/fonts/5e/dicefont.woff
Normal file
Binary file not shown.
BIN
themes/fonts/5e/dicefont.woff2
Normal file
BIN
themes/fonts/5e/dicefont.woff2
Normal file
Binary file not shown.
18
themes/fonts/5e/dicefont_license.md
Normal file
18
themes/fonts/5e/dicefont_license.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# License
|
||||||
|
|
||||||
|
DiceFont is open source. You can use it for commercial projects, personal
|
||||||
|
projects or open source projects.
|
||||||
|
|
||||||
|
## Font License
|
||||||
|
|
||||||
|
Applies to all desktop and webfont files: [License: SIL OFL 1.1](http://scripts.sil.org/OFL)
|
||||||
|
|
||||||
|
## Code License
|
||||||
|
|
||||||
|
Applies to all CSS and LESS files: [License: MIT License](http://opensource.org/licenses/mit-license.html)
|
||||||
|
|
||||||
|
## Documentation License
|
||||||
|
|
||||||
|
Applies to all other files [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)
|
||||||
|
|
||||||
|
Copyright [Franco Ponticelli](https://github.com/fponticelli).
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@import url('./dicefont.less');
|
||||||
|
|
||||||
/* Main Font, serif */
|
/* Main Font, serif */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: BookInsanityRemake;
|
font-family: BookInsanityRemake;
|
||||||
|
|||||||
Reference in New Issue
Block a user