0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 20:42:43 +00:00

Merge branch 'htmlValidate'

This commit is contained in:
Scott Tolksdorf
2016-11-07 19:10:53 -05:00
12 changed files with 291 additions and 28 deletions

View File

@@ -1,17 +1,16 @@
**If you are suggestting a feature or improvement**, delete everything here and just write your idea.
If you are reporting a bug, please fill out the details. If you don't, I will most likely just close the issue asking for more detail.
---
**Browser Type/Version**: FILL OUT
**Issue Description**: FILL OUT
**Brew code to reproduce**: <details><summary>Click to expand code</summary>
```
PUT YOUR BREW CODE HERE
```
</details>
### Additional Details
**Related Images** : IF APPLICABLE
**Share Link** :
or
**Brew code to reproduce** : <details><summary>Click to expand</summary><code><pre>
PASTE BREW CODE HERE
</pre></code></details>

View File

@@ -1,5 +1,12 @@
# changelog
### 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.
### Friday, 09/09/2016 - v2.4.0
- Adding in a HTML validator that will display warnings whenever you save. This should stop a lot of the issues generated with pages not showing up.
### Saturday, 20/08/2016 - v2.3.0
- Added in a license file
- Updated the welcome text

View File

@@ -3,20 +3,24 @@ var _ = require('lodash');
var cx = require('classnames');
var Markdown = require('naturalcrit/markdown.js');
var ErrorBar = require('./errorBar/errorBar.jsx');
var PAGE_HEIGHT = 1056 + 30;
var BrewRenderer = React.createClass({
getDefaultProps: function() {
return {
text : ''
text : '',
errors : []
};
},
getInitialState: function() {
return {
viewablePageNumber: 0,
height : 0,
isMounted : false
isMounted : false,
errors : []
};
},
totalPages : 0,
@@ -95,6 +99,8 @@ var BrewRenderer = React.createClass({
ref='main'
style={{height : this.state.height}}>
<ErrorBar errors={this.props.errors} />
<div className='pages'>
{this.renderPages()}
</div>

View File

@@ -0,0 +1,73 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var ErrorBar = React.createClass({
getDefaultProps: function() {
return {
errors : []
};
},
hasOpenError : false,
hasCloseError : false,
hasMatchError : false,
renderErrors : function(){
this.hasOpenError = false;
this.hasCloseError = false;
this.hasMatchError = false;
var errors = _.map(this.props.errors, (err, idx) => {
if(err.id == 'OPEN') this.hasOpenError = true;
if(err.id == 'CLOSE') this.hasCloseError = true;
if(err.id == 'MISMATCH') this.hasMatchError = true;
return <li key={idx}>
Line {err.line} : {err.text}, '{err.type}' tag
</li>
});
return <ul>{errors}</ul>
},
renderProtip : function(){
var msg = [];
if(this.hasOpenError){
msg.push(<div>
An unmatched opening tag means there's an opened tag that isn't closed, you need to close a tag, like this {'</div>'}. Make sure to match types!
</div>);
}
if(this.hasCloseError){
msg.push(<div>
An unmatched closing tag means you closed a tag without opening it. Either remove it, you check to where you think you opened it.
</div>);
}
if(this.hasMatchError){
msg.push(<div>
A type mismatch means you closed a tag, but the last open tag was a different type.
</div>);
}
return <div className='protips'>
<h4>Protips!</h4>
{msg}
</div>
},
render : function(){
if(!this.props.errors.length) return null;
return <div className='errorBar'>
<i className='fa fa-exclamation-triangle' />
<h3> There are HTML errors in your markup</h3>
<small>If these aren't fixed your brew will not render properly when you print it to PDF or share it</small>
{this.renderErrors()}
<hr />
{this.renderProtip()}
</div>
}
});
module.exports = ErrorBar;

View File

@@ -0,0 +1,60 @@
.errorBar{
position : absolute;
z-index : 10000;
box-sizing : border-box;
width : 100%;
margin-right : 13px;
padding : 20px;
padding-bottom : 10px;
padding-left : 100px;
background-color : @red;
color : white;
i{
position : absolute;
left : 30px;
opacity : 0.8;
font-size : 3em;
}
h3{
font-size : 1.1em;
font-weight : 800;
}
ul{
margin-top : 15px;
font-size : 0.8em;
list-style-position : inside;
list-style-type : disc;
li{
line-height : 1.6em;
}
}
hr{
box-sizing : border-box;
height : 2px;
width : 150%;
margin-top : 25px;
margin-bottom : 15px;
margin-left : -100px;
background-color : darken(@red, 8%);
border : none;
}
small{
font-size: 0.6em;
opacity: 0.7;
}
.protips{
margin-left : -80px;
font-size : 0.6em;
&>div{
margin-bottom : 10px;
line-height : 1.2em;
}
h4{
opacity : 0.8;
font-weight : 800;
line-height : 1.5em;
text-transform : uppercase;
}
}
}

View File

@@ -17,7 +17,7 @@ var Editor = require('../../editor/editor.jsx');
var BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
var HijackPrint = require('../hijackPrint.js');
var Markdown = require('naturalcrit/markdown.js');
const SAVE_TIMEOUT = 3000;
@@ -47,6 +47,7 @@ var EditPage = React.createClass({
isSaving : false,
isPending : false,
errors : null,
htmlErrors : [],
lastUpdated : this.props.brew.updatedAt
};
},
@@ -60,6 +61,10 @@ var EditPage = React.createClass({
}
};
this.setState({
htmlErrors : Markdown.validate(this.state.text)
})
document.onkeydown = HijackPrint(this.props.brew.shareId);
},
componentWillUnmount: function() {
@@ -81,9 +86,15 @@ var EditPage = React.createClass({
},
handleTextChange : function(text){
//If there are errors, run the validator on everychange to give quick feedback
var htmlErrors = this.state.htmlErrors;
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
this.setState({
text : text,
isPending : true
isPending : true,
htmlErrors : htmlErrors
});
(this.hasChanges() ? this.debounceSave() : this.debounceSave.cancel());
@@ -115,7 +126,8 @@ var EditPage = React.createClass({
this.debounceSave.cancel();
this.setState({
isSaving : true,
errors : null
errors : null,
htmlErrors : Markdown.validate(this.state.text)
});
request
@@ -196,7 +208,7 @@ var EditPage = React.createClass({
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor value={this.state.text} onChange={this.handleTextChange} ref='editor'/>
<BrewRenderer text={this.state.text} />
<BrewRenderer text={this.state.text} errors={this.state.htmlErrors} />
</SplitPane>
</div>
</div>

View File

@@ -80,7 +80,7 @@ ___
### Images
Images can be included 'inline' with the text using Markdown-style images. However for background images more control is needed.
Images must be hosted online somewhere, like imgur. You use the address to that image to reference it in your brew. Images can be included 'inline' with the text using Markdown-style images. However for background images more control is needed.
Background images should be included as HTML-style img tags. Using inline CSS you can precisely position your image where you'd like it to be. I have added both a inflow image snippet and a background image snippet to give you exmaples of how to do it.

View File

@@ -3,12 +3,13 @@ var _ = require('lodash');
var cx = require('classnames');
var request = require("superagent");
var Markdown = require('naturalcrit/markdown.js');
var Nav = require('naturalcrit/nav/nav.jsx');
var Navbar = require('../../navbar/navbar.jsx');
var EditTitle = require('../../navbar/editTitle.navitem.jsx');
var IssueNavItem = require('../../navbar/issue.navitem.jsx');
var SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
var Editor = require('../../editor/editor.jsx');
var BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
@@ -22,7 +23,8 @@ var NewPage = React.createClass({
ver : '0.0.0',
title : '',
text: '',
isSaving : false
isSaving : false,
errors : []
};
},
@@ -57,7 +59,8 @@ var NewPage = React.createClass({
handleTextChange : function(text){
this.setState({
text : text
text : text,
errors : Markdown.validate(text)
});
localStorage.setItem(KEY, text);
},
@@ -66,6 +69,7 @@ var NewPage = React.createClass({
this.setState({
isSaving : true
});
request.post('/api')
.send({
title : this.state.title,
@@ -114,11 +118,10 @@ var NewPage = React.createClass({
render : function(){
return <div className='newPage page'>
{this.renderNavbar()}
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor value={this.state.text} onChange={this.handleTextChange} ref='editor'/>
<BrewRenderer text={this.state.text} />
<BrewRenderer text={this.state.text} errors={this.state.errors} />
</SplitPane>
</div>
</div>

View File

@@ -1,7 +1,7 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
"version": "2.3.0",
"version": "2.4.0",
"scripts": {
"build": "node_modules/.bin/gulp prod",
"watch": "node_modules/.bin/gulp",
@@ -33,4 +33,4 @@
"superagent": "^1.6.1",
"vitreum": "^3.2.1"
}
}
}

View File

@@ -13,9 +13,70 @@ renderer.html = function (html) {
return html;
};
const tagTypes = ['div', 'span', 'a'];
const tagRegex = new RegExp('(' +
_.map(tagTypes, (type)=>{
return `\\<${type}|\\</${type}>`;
}).join('|') + ')', 'g');
module.exports = {
render : (rawText)=>{
return Markdown(rawText, {renderer : renderer})
marked : Markdown,
render : (rawBrewText)=>{
return Markdown(rawBrewText, {renderer : renderer})
},
validate : (rawBrewText) => {
var errors = [];
var leftovers = _.reduce(rawBrewText.split('\n'), (acc, line, _lineNumber) => {
var lineNumber = _lineNumber + 1;
var matches = line.match(tagRegex);
if(!matches || !matches.length) return acc;
_.each(matches, (match)=>{
_.each(tagTypes, (type)=>{
if(match == `<${type}`){
acc.push({
type : type,
line : lineNumber
});
}
if(match === `</${type}>`){
if(!acc.length){
errors.push({
line : lineNumber,
type : type,
text : 'Unmatched closing tag',
id : 'CLOSE'
});
}else if(_.last(acc).type == type){
acc.pop();
}else{
errors.push({
line : _.last(acc).line + ' to ' + lineNumber,
type : type,
text : 'Type mismatch on closing tag',
id : 'MISMATCH'
});
acc.pop();
}
}
});
});
return acc;
}, []);
_.each(leftovers, (unmatched)=>{
errors.push({
line : unmatched.line,
type : unmatched.type,
text : "Unmatched opening tag",
id : 'OPEN'
})
});
return errors;
},
marked : Markdown
};

View File

@@ -0,0 +1,30 @@
var test =`
<div class >
<a>
</div>
</a>
<span> </span>
<div> Hellow !!! </div></div></div></div></div></div>
</a>
`;
var markdown = require('./markdown.js');
console.log(markdown.validate(test));

12
todo.md
View File

@@ -1,5 +1,17 @@
# The Homebrewery
### v2.4.0 todo
- [ ] Match style of snippet bar to TPK
- [ ] Add 'Snippets' tag to bar
- [ ] Add an FAQ page
- How to add images
- Pages not appearing
- Rendering is weird
- How to print to PDF
- [ ] Remove ver and each page
- [ ] Use pass-through express routes (like in tpk)
## v2.0.0 todo
X make a proper staging environment
X make database backups