diff --git a/.github/issue_template.md b/.github/issue_template.md index 656278d72..9abe053ce 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -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**:
Click to expand code -``` -PUT YOUR BREW CODE HERE -``` -
+### Additional Details -**Related Images** : IF APPLICABLE +**Share Link** : + +or + +**Brew code to reproduce** :
Click to expand
+
+PASTE BREW CODE HERE
+
+
\ No newline at end of file diff --git a/changelog.md b/changelog.md index 544cf3896..fd5400322 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 34de1f416..b81bbb3ba 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -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}}> + +
{this.renderPages()}
diff --git a/client/homebrew/brewRenderer/errorBar/errorBar.jsx b/client/homebrew/brewRenderer/errorBar/errorBar.jsx new file mode 100644 index 000000000..e9ff189c9 --- /dev/null +++ b/client/homebrew/brewRenderer/errorBar/errorBar.jsx @@ -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
  • + Line {err.line} : {err.text}, '{err.type}' tag +
  • + }); + + return + }, + + renderProtip : function(){ + var msg = []; + if(this.hasOpenError){ + msg.push(
    + An unmatched opening tag means there's an opened tag that isn't closed, you need to close a tag, like this {'
    '}. Make sure to match types! + ); + } + + if(this.hasCloseError){ + msg.push(
    + An unmatched closing tag means you closed a tag without opening it. Either remove it, you check to where you think you opened it. +
    ); + } + + if(this.hasMatchError){ + msg.push(
    + A type mismatch means you closed a tag, but the last open tag was a different type. +
    ); + } + return
    +

    Protips!

    + {msg} +
    + }, + + render : function(){ + if(!this.props.errors.length) return null; + + return
    + +

    There are HTML errors in your markup

    + If these aren't fixed your brew will not render properly when you print it to PDF or share it + {this.renderErrors()} +
    + {this.renderProtip()} +
    + } +}); + +module.exports = ErrorBar; diff --git a/client/homebrew/brewRenderer/errorBar/errorBar.less b/client/homebrew/brewRenderer/errorBar/errorBar.less new file mode 100644 index 000000000..f3f2dbaae --- /dev/null +++ b/client/homebrew/brewRenderer/errorBar/errorBar.less @@ -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; + } + } +} \ No newline at end of file diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 4b923b50f..de1278dba 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -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({
    - +
    diff --git a/client/homebrew/pages/homePage/welcome_msg.md b/client/homebrew/pages/homePage/welcome_msg.md index 04751e4db..dbe614bd6 100644 --- a/client/homebrew/pages/homePage/welcome_msg.md +++ b/client/homebrew/pages/homePage/welcome_msg.md @@ -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. diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index d664f88dc..e50db1823 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -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
    {this.renderNavbar()} -
    - +
    diff --git a/package.json b/package.json index ce2c4821d..3bb003cf5 100644 --- a/package.json +++ b/package.json @@ -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" } -} +} \ No newline at end of file diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index ba64ad7dc..fcb8d6ef2 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -13,9 +13,70 @@ renderer.html = function (html) { return html; }; + +const tagTypes = ['div', 'span', 'a']; +const tagRegex = new RegExp('(' + + _.map(tagTypes, (type)=>{ + return `\\<${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 === ``){ + 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 }; + diff --git a/shared/naturalcrit/validate_test.js b/shared/naturalcrit/validate_test.js new file mode 100644 index 000000000..60e240040 --- /dev/null +++ b/shared/naturalcrit/validate_test.js @@ -0,0 +1,30 @@ +var test =` + +
    + + + + + + +
    + + + + + + + +
    Hellow !!!
    + + + + + + + +`; + +var markdown = require('./markdown.js'); + +console.log(markdown.validate(test)); \ No newline at end of file diff --git a/todo.md b/todo.md index 266dad93a..98a5a1ab0 100644 --- a/todo.md +++ b/todo.md @@ -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