0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-23 20:53:05 +00:00

Compare commits

..

63 Commits

Author SHA1 Message Date
Scott Tolksdorf
570d86448d Adding some notes 2017-06-04 22:04:28 -04:00
Scott Tolksdorf
18d0b68eb0 Moving code between renderer and view 2017-06-04 22:04:28 -04:00
Scott Tolksdorf
74d17f32a5 Merge branch 'randombrew' into home 2017-06-04 22:03:52 -04:00
Scott Tolksdorf
469fa957aa Populate now generates a bunch of rnadom brews 2017-06-04 22:03:36 -04:00
Scott Tolksdorf
4d0ebcb1d8 Adding a logs task 2017-06-04 16:26:50 -04:00
Scott Tolksdorf
10f9ac14c7 Renaming colors and a bunch of other things 2017-06-04 16:04:34 -04:00
Scott Tolksdorf
705adbe2c6 Adding additional rule for fullPage 2017-06-04 15:18:49 -04:00
Scott Tolksdorf
7e7428504e Removed dep on marked, added in a definition list for list types 2017-06-04 12:39:42 -04:00
Scott Tolksdorf
c28647726a Added spelllist, blendmode, and collumn fill auto 2017-06-04 12:05:30 -04:00
Scott Tolksdorf
a30bd79399 Added dropcap style, making page snippet 2017-06-04 11:39:47 -04:00
Scott Tolksdorf
87a9c46202 adding a note 2017-06-04 10:14:33 -04:00
Scott Tolksdorf
380bbd661f Starting on the brew card 2017-05-29 23:39:59 -04:00
Scott Tolksdorf
fa203047da Creating new home page, added new account route 2017-05-28 13:18:46 -04:00
Scott Tolksdorf
b39f9041c2 Updating DB to handle newer versions of mongo 2017-05-28 11:24:50 -04:00
Scott Tolksdorf
2023ae4f6a Merge branch 'borderShadows' into v3 2017-03-26 15:18:06 -04:00
Scott Tolksdorf
d5f04ca2b6 Shadows now being drawn as a :after element. Beauty 2017-03-26 15:10:36 -04:00
Scott Tolksdorf
f99bcabad0 Updating the build scripts 2017-03-26 12:20:17 -04:00
Scott Tolksdorf
32317bfa6f Merge branch 'collaspeNav' into snippets 2017-03-21 00:15:34 -04:00
Scott Tolksdorf
1946a50ce0 Converted a few nav items over 2017-03-21 00:15:24 -04:00
Scott Tolksdorf
ee1827eab0 Trying to get it working 2017-03-21 00:09:37 -04:00
Scott Tolksdorf
20371a8b3d Adding the brew title to the print page title, so downloads are named properly 2017-03-19 20:23:19 -04:00
Scott Tolksdorf
28a3f31caa Work on the Table of contents snippet 2017-03-19 17:26:55 -04:00
Scott Tolksdorf
c647bdf5ee Added in stlying for blockquotes and clean up logic in footer 2017-03-19 15:07:00 -04:00
Scott Tolksdorf
eb1827cedb Merge branch 'borderTest' into snippets 2017-03-19 13:51:24 -04:00
Scott Tolksdorf
a3251dfa19 Pseudo borders are now working 2017-03-19 13:50:45 -04:00
Scott Tolksdorf
30d3fcf168 Pseudo element borders are working, holy shit 2017-03-19 13:17:33 -04:00
Scott Tolksdorf
393df1b181 Updating the faqw 2017-03-19 12:28:45 -04:00
Scott Tolksdorf
94a3a96960 Adding to faq 2017-03-03 19:20:27 -05:00
Scott Tolksdorf
bfb2cea48e Working on onster block 2017-02-28 23:21:41 -05:00
Scott Tolksdorf
00f2703d0b Moved files into statics, finally fixed the brew editor breaking on resizze 2017-02-28 21:07:37 -05:00
Scott Tolksdorf
4c874149fb added an internal nested div on block elements 2017-02-26 20:18:44 -05:00
Scott Tolksdorf
0705e08381 Added in stlying for code blocks 2017-02-26 19:49:36 -05:00
Scott Tolksdorf
e112808706 Column split now a key word 2017-02-26 19:38:42 -05:00
Scott Tolksdorf
234d216d64 Adding in notes and adding more to blocks 2017-02-26 17:42:21 -05:00
Scott Tolksdorf
ef0265f4fa moving old snippets into the depricated folder 2017-02-26 13:03:12 -05:00
Scott Tolksdorf
fbc18a017c Creating new stlying for the snippet blocks 2017-02-26 13:01:48 -05:00
Scott Tolksdorf
9d4d337bb9 Snippet bar has been replaced and new style of snippets being worked on 2017-02-26 11:53:40 -05:00
Scott Tolksdorf
0d0ce101f3 Starting to set up the snippets 2017-02-24 00:49:21 -05:00
Scott Tolksdorf
540c00cb0c Merge branch 'noHtml' into v3 2017-02-23 10:07:32 -05:00
Scott Tolksdorf
446ae9cbcf Merge branch 'dualRenderer' into noHtml 2017-02-23 10:07:05 -05:00
Scott Tolksdorf
dc486cfba9 Added nested markdown parsering within blocks 2017-02-23 10:03:04 -05:00
Scott Tolksdorf
a6a1f41e77 Merge branch 'newStyle' into dualRenderer 2017-02-23 09:58:38 -05:00
Scott Tolksdorf
fd567352a4 Moved imgs and fonts into the new style folder 2017-02-23 09:58:10 -05:00
Scott Tolksdorf
a33b1d845d Styling is finally split, oh boy 2017-02-23 08:33:13 -05:00
Scott Tolksdorf
b20f4ffb46 PHB style should be fully scoped 2017-02-23 08:11:48 -05:00
Scott Tolksdorf
2f69ef3fe8 Removing the old server files 2017-02-23 07:41:55 -05:00
Scott Tolksdorf
1da1f90a35 Backing up the todo 2017-02-18 14:29:16 -05:00
Scott Tolksdorf
bd08858745 Split off the old stlying in a separate file 2017-02-13 00:45:17 -05:00
Scott Tolksdorf
304cd0ffcd Getting both renderers to play nice 2017-02-12 23:35:19 -05:00
Scott Tolksdorf
b40e5bc4c4 simplifying the issue template, because no one ever actually uses it 2017-02-12 10:21:02 -05:00
Scott Tolksdorf
0663737e1c moved the old parser and renderer into a depreciated folder 2017-02-04 03:27:10 -05:00
Scott Tolksdorf
307dd2d9ba Adding newlines to div injection for blocks 2017-02-01 23:54:20 -05:00
Scott Tolksdorf
95c91b6ba8 Merge branch 'styleEditor' into noHtml 2017-01-30 10:48:34 -05:00
Scott Tolksdorf
c8c46725a2 Making the error looks better 2017-01-30 10:48:05 -05:00
Scott Tolksdorf
7001b71d91 Lots of progress with the new editor 2017-01-28 22:19:14 -05:00
Scott Tolksdorf
cbab4f4959 added todo 2017-01-28 16:38:51 -05:00
Scott Tolksdorf
22d9982888 Added support for title description and thumbnail images 2017-01-28 16:38:51 -05:00
Scott Tolksdorf
76ced9ca49 Added comma parsing to the block code 2017-01-28 16:38:51 -05:00
Scott Tolksdorf
b1db8040a4 Added a todo for generic line styling 2017-01-28 16:38:51 -05:00
Scott Tolksdorf
c8b089f7fb Added new lexer for handling the new block syntax 2017-01-28 16:38:51 -05:00
Scott Tolksdorf
97c0443c76 Fixed bug where new page was storing null brews 2017-01-28 16:38:51 -05:00
Scott Tolksdorf
c470bed591 'Created 2017-01-28 16:38:51 -05:00
Scott Tolksdorf
4593099914 Merge branch 'newAdmin' into v3 2017-01-28 16:36:25 -05:00
148 changed files with 5460 additions and 1064 deletions

View File

@@ -1,16 +1,5 @@
Share link to issue brew: http://homebrewery.naturalcrit.com/share/XXXXXXX
### Additional Details
**Share Link** :
or
**Brew code to reproduce** : <details><summary>Click to expand</summary><code><pre>
PASTE BREW CODE HERE
</pre></code></details>

View File

@@ -53,8 +53,14 @@ const Homebrew = React.createClass({
return <PrintPage query={query}/>;
},
'/new' : <NewPage />,
'/changelog' : <SharePage />,
'*' : <HomePage />,
'/test' : <SharePage />,
'/test_old' : <SharePage />,
'*' : <HomePage brews={this.props.brews}/>,
});
},
render : function(){

View File

@@ -33,7 +33,7 @@ const ContinousSave = React.createClass({
window.onbeforeunload = function(){};
},
actionHandler : function(actionType){
if(actionType == 'UPDATE_BREW_TEXT' || actionType == 'UPDATE_META'){
if(actionType == 'UPDATE_BREW_CODE' || actionType == 'UPDATE_META' || actionType == 'UPDATE_BREW_STYLE'){
Actions.pendingSave();
}
},
@@ -51,6 +51,9 @@ const ContinousSave = React.createClass({
Oops!
<div className='errorContainer'>
Looks like there was a problem saving. <br />
Back up your brew in a text file, just in case.
<br /><br />
Report the issue <a target='_blank' href={'https://github.com/stolksdorf/naturalcrit/issues/new?body='+ encodeURIComponent(errMsg)}>
here
</a>.

View File

@@ -2,7 +2,12 @@ var React = require('react');
var Nav = require('naturalcrit/nav/nav.jsx');
module.exports = function(props){
return <Nav.item newTab={true} href='https://github.com/stolksdorf/homebrewery/issues' color='red' icon='fa-bug'>
return <Nav.item
{...props}
newTab={true}
href='https://github.com/stolksdorf/homebrewery/issues'
color='red'
icon='fa-bug'>
report issue
</Nav.item>
};

View File

@@ -120,7 +120,7 @@
top : 29px;
left : -20px;
z-index : 1000;
width : 120px;
width : 170px;
padding : 8px;
background-color : #333;
a{

View File

@@ -3,6 +3,7 @@ var Nav = require('naturalcrit/nav/nav.jsx');
module.exports = function(props){
return <Nav.item
{...props}
className='patreon'
newTab={true}
href='https://www.patreon.com/stolksdorf'

View File

@@ -8,6 +8,9 @@ var Nav = require('naturalcrit/nav/nav.jsx');
const VIEW_KEY = 'homebrewery-recently-viewed';
const EDIT_KEY = 'homebrewery-recently-edited';
//DEPRICATED
var BaseItem = React.createClass({
getDefaultProps: function() {
return {
@@ -28,6 +31,8 @@ var BaseItem = React.createClass({
},
componentDidMount: function() {
console.log('Recent nav item is depricated');
var brews = JSON.parse(localStorage.getItem(this.props.storageKey) || '[]');
brews = _.filter(brews, (brew)=>{

View File

@@ -48,10 +48,10 @@ const SmartNav = Store.createSmartComponent(React.createClass({
<Nav.section>
<Items.ContinousSave />
<Items.Issue />
<Nav.item newTab={true} href={'/share/' + Store.getBrew().shareId} color='teal' icon='fa-share-alt'>
<Nav.item newTab={true} href={'/share/' + this.props.brew.shareId} color='teal' icon='fa-share-alt'>
Share
</Nav.item>
<Items.Print />
<Items.Print shareId={this.props.brew.shareId} />
<Items.Account />
</Nav.section>
</Navbar>

View File

@@ -1,12 +0,0 @@
//TODO: Depricate
module.exports = function(shareId){
return function(event){
event = event || window.event;
if((event.ctrlKey || event.metaKey) && event.keyCode == 80){
var win = window.open(`/homebrew/print/${shareId}?dialog=true`, '_blank');
win.focus();
event.preventDefault();
}
};
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 KiB

View File

@@ -4,33 +4,31 @@ const cx = require('classnames');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const PatreonNavItem = require('../../navbar/patreon.navitem.jsx');
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx');
const AccountNavItem = require('../../navbar/account.navitem.jsx');
const NavItem = require('../../navbar/navitems.js');
const BrewInterface = require('homebrewery/brewInterface/brewInterface.jsx');
//const BrewInterface = require('homebrewery/brewInterface/brewInterface.jsx');
const BrewCard = require('homebrewery/brewCard/brewCard.jsx');
const Actions = require('homebrewery/brew.actions.js');
//
//const Actions = require('homebrewery/brew.actions.js');
const HomePage = React.createClass({
handleSave : function(){
Actions.saveNew();
getDefaultProps: function() {
return {
topBrews : []
};
},
renderNavbar : function(){
return <Navbar>
<Nav.section>
<PatreonNavItem />
<IssueNavItem />
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-file-text-o'>
Changelog
<NavItem.Patreon collaspe={true} />
<NavItem.Issue collaspe={true} />
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-star' collaspe={true}>
What's new
</Nav.item>
<RecentNavItem.both />
<AccountNavItem />
<NavItem.Recent.both />
<NavItem.Account />
{/*}
<Nav.item href='/new' color='green' icon='fa-external-link'>
New Brew
@@ -40,22 +38,58 @@ const HomePage = React.createClass({
</Navbar>
},
renderNavigation : function(){
return <div className='navigation'>
<p>
Effortlessly create Authnetic looking D&D homebrews with just text
</p>
<div>
<a className='button new' target='_blank' href='/new'>
<i className='fa fa-magic' />
<h3>New</h3>
<p>This is some sample text</p>
</a>
<a className='button search' target='_blank' href='/search'>
<i className='fa fa-search' />
<h3>Search</h3>
<p>This is some sample text</p>
</a>
</div>
<div>
<a className='button docs' target='_blank' href='/docs'>
<i className='fa fa-book' />
<h3>Docs</h3>
<p>This is some sample text</p>
</a>
<a className='button account' target='_blank' href='/account'>
<i className='fa fa-user' />
<h3>Account</h3>
<p>This is some sample text</p>
</a>
</div>
</div>
},
renderTopBrews : function(){
return <div className='topBrews'>
{_.map(this.props.brews, (brew)=><BrewCard brew={brew} key={brew._id}/>)}
</div>
},
render : function(){
return <div className='homePage page'>
{this.renderNavbar()}
<div className='content'>
<BrewInterface />
</div>
<div className='hero'>
hero
<h1>The Homebrewery</h1>
<div className={cx('floatingSaveButton', {
//show : Store.getBrewText() !== this.props.welcomeText
})} onClick={this.handleSave}>
Save current <i className='fa fa-save' />
</div>
{this.renderNavigation()}
{this.renderTopBrews()}
</div>
<a href='/new' className='floatingNewButton'>
Create your own <i className='fa fa-magic' />
</a>
</div>
}
});

View File

@@ -1,43 +1,109 @@
@import 'homebrewery/phb_style/phb.fonts.less';
.homePage{
position : relative;
a.floatingNewButton{
.animate(background-color);
position : absolute;
display : block;
right : 70px;
bottom : 70px;
z-index : 100;
z-index : 5001;
padding : 1em;
background-color : @orange;
font-size : 1.5em;
color : white;
text-decoration : none;
box-shadow : 3px 3px 15px black;
&:hover{
background-color : darken(@orange, 20%);
.hero{
// .backgroundScrollAnimation();
height : 400px;
background-image : url('/assets/homebrew/pages/homePage/fantasy_background.jpg');
background-position : 1% 15%;
h1{
margin-top : 15%;
font-family : BookInsanity;
font-size : 3em;
font-weight : 800;
letter-spacing : 0.3em;
text-align : center;
}
}
.floatingSaveButton{
.animateAll();
position : absolute;
display : block;
right : 200px;
bottom : 90px;
z-index : 100;
z-index : 5000;
padding : 0.8em;
cursor : pointer;
background-color : @blue;
font-size : 0.8em;
color : white;
text-decoration : none;
box-shadow : 3px 3px 15px black;
&:hover{
background-color : darken(@blue, 20%);
.navigation{
padding : 30px;
background-image : url('/assets/homebrew/pages/homePage/dmg_bg.jpg');
text-align : center;
&>div{
//display : flex;
margin : 0 auto;
// justify-content : center;
}
&.show{
right : 350px;
p{
margin-bottom : 20px;
font-size : 0.8em;
}
a.button{
.animate(background-color);
position : relative;
display : block;
height : 60px;
max-width : 280px;
width : 100%;
margin : 15px;
padding : 20px 30px;
padding-left : 80px;
cursor : pointer;
background-color : fade(@red, 30%);
color : black;
text-align : left;
text-decoration : none;
//flex-grow : 0;
display: inline-block;
i{
position : absolute;
top : 15px;
left : 25px;
font-size : 2em;
//transform-style: preserve-3d;
transform: rotateY(0deg);
}
h3{
display : block;
margin-bottom : 3px;
font-size : 1.2em;
font-weight : 600;
letter-spacing : 0.2em;
}
p{
.fadeOutLeft();
.keep();
opacity : 0;
font-size : 0.8em;
}
&:hover{
background-color : fade(@red, 50%);
p{
.fadeInRight();
}
i{
transform: rotateY(360deg);
.animateAll(0.5s);
}
}
}
}
}
.topBrews{
background-image : url('/assets/homebrew/pages/homePage/phb_bg.jpg');
}
}
// .vendor(@property, @value) {
// -webkit-@{property}: @value;
// -khtml-@{property}: @value;
// -moz-@{property}: @value;
// @{property}: @value;
// }
// .backgroundScrollAnimation(){
// @top : -100px;
// .vendor(animation-iteration-count, infinite);
// .createAnimation(backgroundScroll, 60s, linear);
// .backgroundScrollKeyFrames(){
// 0% { background-position: 0 @top}
// 100% { background-position: -1200px @top}
// }
// @-webkit-keyframes backgroundScroll {.backgroundScrollKeyFrames();}
// @-moz-keyframes backgroundScroll {.backgroundScrollKeyFrames();}
// @-ms-keyframes backgroundScroll {.backgroundScrollKeyFrames();}
// @-o-keyframes backgroundScroll {.backgroundScrollKeyFrames();}
// @keyframes backgroundScroll {.backgroundScrollKeyFrames();}
// }

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

View File

@@ -47,7 +47,7 @@ const NewPage = React.createClass({
<Nav.item color='purple' icon='fa-file-pdf-o' onClick={Actions.localPrint}>
get PDF
</Nav.item>
<Items.Issue />
<Items.Issue collaspe={true} />
<Items.Account />
</Nav.section>
</Navbar>

View File

@@ -1,41 +1,70 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const BrewRenderer = require('homebrewery/brewRenderer/brewRenderer.jsx');
const Markdown = require('homebrewery/markdown.js');
const Headtags = require('vitreum/headtags');
const PrintPage = React.createClass({
getDefaultProps: function() {
return {
query : {},
brew : {
text : '',
style : ''
}
};
},
getInitialState: function() {
return {
brewText: this.props.brew.text
brew: this.props.brew
};
},
componentDidMount: function() {
if(this.props.query.local){
this.setState({ brewText : localStorage.getItem(this.props.query.local)});
try{
this.setState({
brew : JSON.parse(
localStorage.getItem(this.props.query.local)
)
});
}catch(e){}
}
if(this.props.query.dialog) window.print();
//if(this.props.query.dialog) window.print();
},
//TODO: Print page shouldn't replicate functionality in brew renderer
renderStyle : function(){
if(!this.state.brew.style) return;
return <style>{this.state.brew.style.replace(/;/g, ' !important;')}</style>
},
renderPages : function(){
return _.map(this.state.brewText.split('\\page'), (page, index) => {
return _.map(this.state.brew.text.split('\\page'), (page, index) => {
return <div
className='phb'
className='phb v2'
id={`p${index + 1}`}
dangerouslySetInnerHTML={{__html:Markdown.render(page)}}
key={index} />;
});
},
renderPrintInstructions : function(){
return <div className='printInstructions'>
Hey, I'm really cool instructions!!!!!
</div>
},
render : function(){
return <div>
{this.renderPages()}
return <div className='printPage'>
<Headtags.title>{this.state.brew.title}</Headtags.title>
{this.renderPrintInstructions()}
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} />
</div>
}
});

View File

@@ -1,3 +1,17 @@
.printPage{
.printPage{
position : relative;
@media print{
.printInstructions{
display : none;
}
}
.printInstructions{
position : absolute;
top : 0px;
right : 0px;
z-index : 100000;
padding : 30px;
background-color : @blue;
}
}

View File

@@ -9,26 +9,15 @@ const ReportIssue = require('../../navbar/issue.navitem.jsx');
//const RecentlyViewed = require('../../navbar/recent.navitem.jsx').viewed;
const Account = require('../../navbar/account.navitem.jsx');
const BrewRenderer = require('homebrewery/brewRenderer/brewRenderer.jsx');
const BrewView = require('homebrewery/BrewView/BrewView.jsx');
const Utils = require('homebrewery/utils.js');
const Actions = require('homebrewery/brew.actions.js');
const Store = require('homebrewery/brew.store.js');
const SharePage = React.createClass({
getDefaultProps: function() {
return {
brew : {
title : '',
text : '',
shareId : null,
createdAt : null,
updatedAt : null,
views : 0
}
};
},
const Headtags = require('vitreum/headtags');
const SharePage = React.createClass({
componentDidMount: function() {
document.addEventListener('keydown', this.handleControlKeys);
},
@@ -39,16 +28,35 @@ const SharePage = React.createClass({
p : Actions.print
}),
renderMetatags : function(brew){
let metatags = [
<Headtags.meta key='site_name' property='og:site_name' content='Homebrewery'/>,
<Headtags.meta key='type' property='og:type' content='article' />
];
if(brew.title){
metatags.push(<Headtags.meta key='title' property='og:title' content={brew.title} />);
}
if(brew.description){
metatags.push(<Headtags.meta key='description' name='description' content={brew.description} />);
}
if(brew.thumbnail){
metatags.push(<Headtags.meta key='image' property='og:image' content={brew.thumbnail} />);
}
return metatags;
},
render : function(){
const brew = Store.getBrew();
return <div className='sharePage page'>
{this.renderMetatags(brew)}
<Navbar>
<Nav.section>
<Nav.item className='brewTitle'>{brew.title}</Nav.item>
</Nav.section>
<Nav.section>
<ReportIssue />
<ReportIssue collaspe={true} />
<PrintLink shareId={brew.shareId} />
<Nav.item href={'/source/' + brew.shareId} color='teal' icon='fa-code'>
source
@@ -57,7 +65,7 @@ const SharePage = React.createClass({
</Nav.section>
</Navbar>
<div className='content'>
<BrewRenderer brewText={brew.text} />
<BrewView brew={brew}/>
</div>
</div>
}

View File

@@ -3,6 +3,8 @@ const _ = require('lodash');
const cx = require('classnames');
const moment = require('moment');
//TODO: Depriacte
const BrewItem = React.createClass({
getDefaultProps: function() {
return {
@@ -24,6 +26,7 @@ const BrewItem = React.createClass({
},
render : function(){
console.log('Brew Item should be depricated');
const brew = this.props.brew;
return <div className='brewItem'>
<h2>{brew.title}</h2>

View File

@@ -9,12 +9,12 @@ const RecentNavItem = require('../../navbar/recent.navitem.jsx');
const Account = require('../../navbar/account.navitem.jsx');
const BrewItem = require('./brewItem/brewItem.jsx');
const brew = {
title : 'SUPER Long title woah now',
authors : []
}
// const brew = {
// title : 'SUPER Long title woah now',
// authors : []
// }
const BREWS = _.times(25, ()=>{ return brew});
// const BREWS = _.times(25, ()=>{ return brew});
const UserPage = React.createClass({
@@ -29,7 +29,6 @@ const UserPage = React.createClass({
if(!brews || !brews.length) return <div className='noBrews'>No Brews.</div>;
const sortedBrews = _.sortBy(brews, (brew)=>{ return brew.title; });
return _.map(sortedBrews, (brew, idx) => {
return <BrewItem brew={brew} key={idx}/>
});
@@ -52,8 +51,6 @@ const UserPage = React.createClass({
render : function(){
const brews = this.getSortedBrews();
console.log('user brews', brews);
return <div className='userPage page'>
<Navbar>
<Nav.section>

View File

@@ -1,31 +0,0 @@
.phb{
//Double hr for full width elements
hr+hr+blockquote{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
//*****************************
// * CLASS TABLE
// *****************************/
hr+table{
margin-top : -5px;
margin-bottom : 50px;
padding-top : 10px;
border-collapse : separate;
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 37px 17px;
border-image-repeat : round;
border-image-slice : 150 200 150 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
}
h5+hr+table{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 864 B

View File

@@ -6,6 +6,7 @@ module.exports = function(vitreum){
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link rel="icon" href="/assets/homebrew/favicon.ico" type="image/x-icon" />
<title>The Homebrewery - NaturalCrit</title>
${vitreum.head}
</head>

View File

@@ -3,16 +3,19 @@
"description": "Create authentic looking D&D homebrews using only markdown",
"version": "3.0.0",
"scripts": {
"logs": "heroku logs -t --app=homebrewery",
"dev": "node scripts/dev.js",
"quick": "node scripts/quick.js",
"build": "node scripts/build.js",
"phb": "node scripts/phb.js",
"populate": "node scripts/populate.js",
"prod": "set NODE_ENV=production&& npm run build",
"postinstall": "npm run build",
"start": "node server.js",
"test": "mocha test",
"test:dev": "nodemon -x mocha test || exit 0"
"snippet": "nodemon scripts/snippet.test.js",
"todo": "./node_modules/.bin/fixme -i node_modules/** -i build/**",
"test": "mocha tests",
"test:dev": "nodemon -x mocha tests || exit 0",
"test:markdown": "nodemon -x mocha tests/markdown.test.js || exit 0"
},
"author": "stolksdorf",
"license": "MIT",
@@ -27,7 +30,6 @@
"jwt-simple": "^0.5.1",
"lodash": "^4.17.3",
"loglevel": "^1.4.1",
"marked": "^0.3.5",
"moment": "^2.11.0",
"mongoose": "^4.3.3",
"nconf": "^0.8.4",
@@ -45,6 +47,7 @@
"chai": "^3.5.0",
"chai-as-promised": "^6.0.0",
"chai-subset": "^1.4.0",
"fixme": "^0.4.3",
"mocha": "^3.2.0",
"supertest": "^2.0.1",
"supertest-as-promised": "^4.0.2"

File diff suppressed because one or more lines are too long

View File

@@ -2,19 +2,20 @@ const label = 'build';
console.time(label);
const clean = require('vitreum/steps/clean.js');
const jsx = require('vitreum/steps/jsx.js').partial;
const lib = require('vitreum/steps/libs.js').partial;
const less = require('vitreum/steps/less.js').partial;
const asset = require('vitreum/steps/assets.js').partial;
const jsx = require('vitreum/steps/jsx.js');
const lib = require('vitreum/steps/libs.js');
const less = require('vitreum/steps/less.js');
const asset = require('vitreum/steps/assets.js');
const Proj = require('./project.json');
clean()
.then(lib(Proj.libs))
.then(jsx('homebrew', './client/homebrew/homebrew.jsx', Proj.libs, ['./shared']))
.then(less('homebrew', ['./shared']))
.then(jsx('admin', './client/admin/admin.jsx', Proj.libs, ['./shared']))
.then(less('admin', ['./shared']))
.then(asset(Proj.assets, ['./shared', './client']))
.then(console.timeEnd.bind(console, label))
Promise.resolve()
.then(()=>clean())
.then(()=>lib(Proj.libs))
.then(()=>jsx('homebrew', './client/homebrew/homebrew.jsx', Proj.libs, ['./shared']))
.then((deps)=>less('homebrew', ['./shared'], deps))
.then(()=>jsx('admin', './client/admin/admin.jsx', Proj.libs, ['./shared']))
.then((deps)=>less('admin', ['./shared'], deps))
.then(()=>asset(Proj.assets, ['./shared', './client']))
.then(()=>console.timeEnd.bind(console, label))
.catch(console.error);

View File

@@ -1,21 +1,21 @@
const label = 'dev';
console.time(label);
const jsx = require('vitreum/steps/jsx.watch.js').partial;
const less = require('vitreum/steps/less.watch.js').partial;
const assets = require('vitreum/steps/assets.watch.js').partial;
const server = require('vitreum/steps/server.watch.js').partial;
const livereload = require('vitreum/steps/livereload.js').partial;
const jsx = require('vitreum/steps/jsx.watch.js');
const less = require('vitreum/steps/less.watch.js');
const assets = require('vitreum/steps/assets.watch.js');
const server = require('vitreum/steps/server.watch.js');
const livereload = require('vitreum/steps/livereload.js');
const Proj = require('./project.json');
Promise.resolve()
.then(jsx('homebrew', './client/homebrew/homebrew.jsx', Proj.libs, './shared'))
.then(less('homebrew', './shared'))
.then(jsx('admin', './client/admin/admin.jsx', Proj.libs, './shared'))
.then(less('admin', './shared'))
.then(assets(Proj.assets, ['./shared', './client']))
.then(livereload())
.then(server('./server.js', ['server']))
.then(console.timeEnd.bind(console, label))
.then(()=>jsx('homebrew', './client/homebrew/homebrew.jsx', Proj.libs, './shared'))
.then((deps)=>less('homebrew', './shared', deps))
.then(()=>jsx('admin', './client/admin/admin.jsx', Proj.libs, './shared'))
.then((deps)=>less('admin', './shared', deps))
.then(()=>assets(Proj.assets, ['./shared', './client']))
.then(()=>livereload())
.then(()=>server('./server.js', ['server']))
.then(()=>console.timeEnd.bind(console, label))
.catch(console.error)

8
scripts/notes.js Normal file
View File

@@ -0,0 +1,8 @@
require('fixme')({
path: process.cwd(),
ignored_directories: ['node_modules/**', '.git/**', 'build/**'],
file_patterns: ['**/*.js', '**/*.jsx', '**/*.less'],
file_encoding: 'utf8',
line_length_limit: 200
});

View File

@@ -1,10 +0,0 @@
const less = require('less');
const fs = require('fs');
less.render(fs.readFileSync('./client/homebrew/phbStyle/phb.style.less', 'utf8'), {compress : true})
.then((output) => {
fs.writeFileSync('./phb.standalone.css', output.css);
console.log('phb.standalone.css created!');
}, (err) => {
console.error(err);
});

View File

@@ -3,18 +3,22 @@ const _ = require('lodash');
const DB = require('../server/db.js');
const BrewData = require('../server/brew.data.js');
const BrewGen = require('../test/brew.gen.js');
//const BrewGen = require('../tests/brew.gen.js');
const BrewGen = require('../shared/homebrewery/snippets/brew/brew.snippet.js');
const BREW_COUNT = 50;
return Promise.resolve()
.then(DB.connect)
.then(BrewData.removeAll)
.then(() => {
console.log('Adding random brews...');
return BrewGen.populateDB(BrewGen.random(50));
return _.reduce(_.times(BREW_COUNT, BrewGen.brewModel), (flow, model)=>{
return flow.then(()=>BrewData.create(model))
}, Promise.resolve());
})
.then(() => {
console.log('Adding specific brews...');
return BrewGen.populateDB(BrewGen.static());
console.log(`Added ${BREW_COUNT} brews`);
})
.then(() => {
return DB.close();

View File

@@ -1,7 +1,6 @@
{
"assets": ["*.png","*.otf", "*.ico"],
"shared":[
],
"assets": ["*.png","*.otf", "*.ico", "*.jpg"],
"shared":["./shared"],
"libs" : [
"react",
"react-dom",
@@ -10,6 +9,7 @@
"codemirror",
"codemirror/mode/gfm/gfm.js",
"codemirror/mode/javascript/javascript.js",
"codemirror/mode/css/css.js",
"moment",
"superagent",
"marked",

8
scripts/snippet.test.js Normal file
View File

@@ -0,0 +1,8 @@
const snippets = require('../shared/homebrewery/snippets');
console.log(snippets);
//console.log(snippets.brew.spell());
//console.log(snippets.brew.table());
console.log(snippets.brew.noncasterTable());

View File

@@ -7,9 +7,6 @@ const config = require('nconf')
const log = require('loglevel');
log.setLevel(config.get('log_level'));
//DB
require('./server/db.js').connect();
//Server
const app = require('./server/app.js');
@@ -29,7 +26,11 @@ app.use((req, res, next) => {
});
*/
const PORT = process.env.PORT || 8000;
const httpServer = app.listen(PORT, () => {
log.info(`server on port:${PORT}`);
});
require('./server/db.js').connect()
.then(()=>{
const PORT = process.env.PORT || 8000;
const httpServer = app.listen(PORT, () => {
log.info(`server on port:${PORT}`);
});
})
.catch((err)=>console.error(err))

View File

@@ -10,10 +10,12 @@ const BrewSchema = mongoose.Schema({
editId : {type : String, default: shortid.generate, index: { unique: true }},
text : {type : String, default : ""},
style : {type : String, default : ""},
title : {type : String, default : ""},
description : {type : String, default : ""},
tags : {type : String, default : ""},
thumbnail : {type : String, default : ""},
systems : [String],
authors : [String],
published : {type : Boolean, default : false},
@@ -22,7 +24,7 @@ const BrewSchema = mongoose.Schema({
updatedAt : { type: Date, default: Date.now},
lastViewed : { type: Date, default: Date.now},
views : {type:Number, default:0},
version : {type: Number, default:1}
version : {type: Number, default:2}
}, {
versionKey: false,
toJSON : {

View File

@@ -1,6 +1,6 @@
const _ = require('lodash');
module.exports = (Brew) => {
module.exports = (BrewModel) => {
const cmds = {
termSearch : (terms='', opts, fullAccess) => {
let query = {};
@@ -34,7 +34,8 @@ module.exports = (Brew) => {
opts.page = _.toNumber(opts.page);
let filter = {
text : 0
text : 0,
style : 0
};
if(!fullAccess){
@@ -42,7 +43,7 @@ module.exports = (Brew) => {
queryObj.published = true;
}
const searchQuery = Brew
const searchQuery = BrewModel
.find(queryObj)
.sort(opts.sort)
.select(filter)
@@ -51,7 +52,7 @@ module.exports = (Brew) => {
.lean()
.exec();
const countQuery = Brew.count(queryObj).exec();
const countQuery = BrewModel.count(queryObj).exec();
return Promise.all([searchQuery, countQuery])
.then((result) => {

View File

@@ -7,7 +7,7 @@ const dbPath = process.env.MONGODB_URI || process.env.MONGOLAB_URI || 'mongodb:/
module.exports = {
connect : ()=>{
return new Promise((resolve, reject)=>{
if(mongoose.connection.readyState == 1){
if(mongoose.connection.readyState !== 0){
log.warn('DB already connected');
return resolve();
}

View File

@@ -1,146 +0,0 @@
const _ = require('lodash');
const Moment = require('moment');
const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router();
//TODO: Possiblity remove
let homebrewTotal = 0;
const refreshCount = ()=>{
HomebrewModel.count({}, (err, total)=>{
homebrewTotal = total;
});
};
refreshCount();
const getTopBrews = (cb)=>{
HomebrewModel.find().sort({views: -1}).limit(5).exec(function(err, brews) {
cb(brews);
});
}
const getGoodBrewTitle = (text) => {
const titlePos = text.indexOf('# ');
if(titlePos !== -1){
const ending = text.indexOf('\n', titlePos);
return text.substring(titlePos + 2, ending);
}else{
return _.find(text.split('\n'), (line)=>{
return line;
});
}
};
router.post('/api', (req, res)=>{
let authors = [];
if(req.account) authors = [req.account.username];
const newHomebrew = new HomebrewModel(_.merge({},
req.body,
{authors : authors}
));
if(!newHomebrew.title){
newHomebrew.title = getGoodBrewTitle(newHomebrew.text);
}
newHomebrew.save((err, obj)=>{
if(err){
console.error(err, err.toString(), err.stack);
return res.status(500).send(`Error while creating new brew, ${err.toString()}`);
}
return res.json(obj);
})
});
router.put('/api/update/:id', (req, res)=>{
HomebrewModel.get({editId : req.params.id})
.then((brew)=>{
brew = _.merge(brew, req.body);
brew.updatedAt = new Date();
if(req.account) brew.authors = _.uniq(_.concat(brew.authors, req.account.username));
brew.markModified('authors');
brew.markModified('systems');
brew.save((err, obj)=>{
if(err) throw err;
return res.status(200).send(obj);
})
})
.catch((err)=>{
console.log(err);
return res.status(500).send("Error while saving");
});
});
router.get('/api/remove/:id', (req, res)=>{
HomebrewModel.find({editId : req.params.id}, (err, objs)=>{
if(!objs.length || err) return res.status(404).send("Can not find homebrew with that id");
var resEntry = objs[0];
resEntry.remove((err)=>{
if(err) return res.status(500).send("Error while removing");
return res.status(200).send();
})
});
});
module.exports = router;
/*
module.exports = function(app){
app;
app.get('/api/search', mw.adminOnly, function(req, res){
var page = req.query.page || 0;
var count = req.query.count || 20;
var query = {};
if(req.query && req.query.id){
query = {
"$or" : [{
editId : req.query.id
},{
shareId : req.query.id
}]
};
}
HomebrewModel.find(query, {
text : 0 //omit the text
}, {
skip: page*count,
limit: count*1
}, function(err, objs){
if(err) console.log(err);
return res.json({
page : page,
count : count,
total : homebrewTotal,
brews : objs
});
});
})
return app;
}
*/

View File

@@ -1,81 +0,0 @@
var mongoose = require('mongoose');
var shortid = require('shortid');
var _ = require('lodash');
var HomebrewSchema = mongoose.Schema({
shareId : {type : String, default: shortid.generate, index: { unique: true }},
editId : {type : String, default: shortid.generate, index: { unique: true }},
title : {type : String, default : ""},
text : {type : String, default : ""},
description : {type : String, default : ""},
tags : {type : String, default : ""},
systems : [String],
authors : [String],
published : {type : Boolean, default : false},
createdAt : { type: Date, default: Date.now },
updatedAt : { type: Date, default: Date.now},
lastViewed : { type: Date, default: Date.now},
views : {type:Number, default:0},
version : {type: Number, default:1}
}, { versionKey: false });
HomebrewSchema.methods.sanatize = function(full=false){
const brew = this.toJSON();
delete brew._id;
delete brew.__v;
if(full){
delete brew.editId;
}
return brew;
};
HomebrewSchema.methods.increaseView = function(){
return new Promise((resolve, reject) => {
this.lastViewed = new Date();
this.views = this.views + 1;
this.save((err) => {
if(err) return reject(err);
return resolve(this);
});
});
};
HomebrewSchema.statics.get = function(query){
return new Promise((resolve, reject) => {
Homebrew.find(query, (err, brews)=>{
if(err || !brews.length) return reject('Can not find brew');
return resolve(brews[0]);
});
});
};
HomebrewSchema.statics.getByUser = function(username, allowAccess=false){
return new Promise((resolve, reject) => {
let query = {authors : username, published : true};
if(allowAccess){
delete query.published;
}
Homebrew.find(query, (err, brews)=>{
if(err) return reject('Can not find brew');
return resolve(_.map(brews, (brew)=>{
return brew.sanatize(!allowAccess);
}));
});
});
};
var Homebrew = mongoose.model('Homebrew', HomebrewSchema);
module.exports = {
schema : HomebrewSchema,
model : Homebrew,
}

View File

@@ -1,4 +1,5 @@
const _ = require('lodash');
const fs = require('fs');
const config = require('nconf');
const utils = require('./utils.js');
const BrewData = require('./brew.data.js');
@@ -6,11 +7,29 @@ const router = require('express').Router();
const mw = require('./middleware.js');
const docs = {
welcomeBrew : require('fs').readFileSync('./welcome.brew.md', 'utf8'),
changelog : require('fs').readFileSync('./changelog.md', 'utf8'),
const statics = {
welcomeBrew : fs.readFileSync('./statics/welcome.brew.md', 'utf8'),
changelog : fs.readFileSync('./statics/changelog.md', 'utf8'),
faq : fs.readFileSync('./statics/faq.md', 'utf8'),
testBrew : fs.readFileSync('./statics/test.brew.md', 'utf8'),
oldTest : fs.readFileSync('./statics/oldTest.brew.md', 'utf8'),
};
let topBrews = [];
const getTopBrews = ()=>{
//Add in filter of last 2 weeks
return BrewData.search({}, {
limit : 4,
sort : {views : -1}
}, false).then(({brews, total})=>brews);
};
getTopBrews().then((brews)=>{
topBrews=brews;
});
const vitreumRender = require('vitreum/steps/render');
const templateFn = require('../client/template.js');
@@ -25,9 +44,7 @@ const renderPage = (req, res, next) => {
brews : req.brews,
brew : req.brew
})
.then((page) => {
return res.send(page)
})
.then((page)=>res.send(page))
.catch(next);
};
@@ -40,6 +57,7 @@ router.get('/edit/:editId', mw.loadBrew, renderPage);
//Print Page
router.get('/print/:shareId', mw.viewBrew, renderPage);
router.get('/print', renderPage);
//Source page
router.get('/source/:sharedId', mw.viewBrew, (req, res, next)=>{
@@ -48,9 +66,18 @@ router.get('/source/:sharedId', mw.viewBrew, (req, res, next)=>{
});
//User Page
router.get('/account', (req, res, next)=>{
if(req.account && req.account.username){
return res.redirect(`/user/${req.account.username}`);
}else{
return res.redirect(`${config.get('login_path')}?redirect=${encodeURIComponent(req.headers.referer)}`);
}
});
router.get('/user/:username', (req, res, next) => {
BrewData.search({ user : req.params.username })
.then((brews) => {
const isSelf = req.account && req.params.username == req.account.username;
BrewData.userSearch(req.params.username, isSelf)
.then(({brews, total}) => {
req.brews = brews;
return next();
})
@@ -59,6 +86,7 @@ router.get('/user/:username', (req, res, next) => {
//Search Page
router.get('/search', (req, res, next) => {
//TODO: Double check that the defaults are okay
BrewData.search()
.then((brews) => {
req.brews = brews;
@@ -70,19 +98,49 @@ router.get('/search', (req, res, next) => {
//Changelog Page
router.get('/changelog', (req, res, next) => {
req.brew = {
text : docs.changelog,
text : statics.changelog,
title : 'Changelog'
};
return next();
}, renderPage);
//faq Page
router.get('/faq', (req, res, next) => {
req.brew = {
text : statics.faq,
title : 'FAQ',
editId : true
};
return next();
}, renderPage);
//New Page
router.get('/new', renderPage);
//Home Page
router.get('/', (req, res, next) => {
req.brew = { text : docs.welcomeBrew };
req.brews = topBrews;
console.log(topBrews);
return next();
}, renderPage);
//Test pages
router.get('/test', (req, res, next) => {
req.brew = {
text : statics.testBrew
};
return next();
}, renderPage);
router.get('/test_old', (req, res, next) => {
req.brew = {
text : statics.oldTest,
version : 1
};
return next();
}, renderPage);
module.exports = router;

View File

@@ -0,0 +1,147 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const Markdown = require('depricated/markdown.old.js');
const ErrorBar = require('./errorBar/errorBar.jsx');
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx')
const Store = require('homebrewery/brew.store.js');
const PAGE_HEIGHT = 1056;
const PPR_THRESHOLD = 50;
const OLD_BrewRenderer = React.createClass({
getDefaultProps: function() {
return {
value : '',
style : '',
errors : []
};
},
getInitialState: function() {
const pages = this.props.value.split('\\page');
return {
viewablePageNumber: 0,
height : 0,
isMounted : false,
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD
};
},
height : 0,
pageHeight : PAGE_HEIGHT,
lastRender : <div></div>,
componentDidMount: function() {
this.updateSize();
window.addEventListener("resize", this.updateSize);
},
componentWillUnmount: function() {
window.removeEventListener("resize", this.updateSize);
},
componentWillReceiveProps: function(nextProps) {
if(this.refs.pages && this.refs.pages.firstChild) this.pageHeight = this.refs.pages.firstChild.clientHeight;
const pages = nextProps.value.split('\\page');
this.setState({
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD
})
},
updateSize : function() {
setTimeout(()=>{
if(this.refs.pages && this.refs.pages.firstChild) this.pageHeight = this.refs.pages.firstChild.clientHeight;
}, 1);
const parentNode = document.querySelector('.page .content');
this.setState({
height : parentNode.clientHeight,
isMounted : true
});
},
handleScroll : function(e){
this.setState({
viewablePageNumber : Math.floor(e.target.scrollTop / this.pageHeight)
});
},
shouldRender : function(pageText, index){
if(!this.state.isMounted) return false;
var viewIndex = this.state.viewablePageNumber;
if(index == viewIndex - 1) return true;
if(index == viewIndex) return true;
if(index == viewIndex + 1) return true;
//Check for style tages
if(pageText.indexOf('<style>') !== -1) return true;
return false;
},
renderPageInfo : function(){
return <div className='pageInfo'>
{this.state.viewablePageNumber + 1} / {this.state.pages.length}
</div>
},
renderPPRmsg : function(){
if(!this.state.usePPR) return;
return <div className='ppr_msg'>
Partial Page Renderer enabled, because your brew is so large. May effect rendering.
</div>
},
renderDummyPage : function(index){
return <div className='phb v1' id={`p${index + 1}`} key={index}>
<i className='fa fa-spinner fa-spin' />
</div>
},
renderPage : function(pageText, index){
return <div className='phb v1' id={`p${index + 1}`} dangerouslySetInnerHTML={{__html:Markdown.render(pageText)}} key={index} />
},
renderPages : function(){
if(this.state.usePPR){
return _.map(this.state.pages, (page, index)=>{
if(this.shouldRender(page, index)){
return this.renderPage(page, index);
}else{
return this.renderDummyPage(index);
}
});
}
if(this.props.errors && this.props.errors.length) return this.lastRender;
this.lastRender = _.map(this.state.pages, (page, index)=>{
return this.renderPage(page, index);
});
return this.lastRender;
},
render : function(){
return <div className='brewRendererOld'
onScroll={this.handleScroll}
ref='main'
style={{height : this.state.height}}>
<ErrorBar errors={this.props.errors} />
<RenderWarnings />
<div className='pages' ref='pages'>
{this.renderPages()}
</div>
{this.renderPageInfo()}
{this.renderPPRmsg()}
</div>
}
});
module.exports = OLD_BrewRenderer;

View File

@@ -0,0 +1,40 @@
@import 'shared/depricated/phb_style_v1/phb.v1.less';
.pane{
position : relative;
}
.brewRendererOld{
overflow-y : scroll;
.pageInfo{
position : absolute;
right : 17px;
bottom : 0;
z-index : 1000;
padding : 8px 10px;
background-color : #333;
font-size : 10px;
font-weight : 800;
color : white;
}
.ppr_msg{
position : absolute;
left : 0px;
bottom : 0;
z-index : 1000;
padding : 8px 10px;
background-color : #333;
font-size : 10px;
font-weight : 800;
color : white;
}
.pages{
margin : 30px 0px;
&>.phb{
margin-right : auto;
margin-bottom : 30px;
margin-left : auto;
box-shadow : 1px 4px 14px #000;
}
}
}

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

@@ -0,0 +1,82 @@
var _ = require('lodash');
var Markdown = require('marked');
var renderer = new Markdown.Renderer();
//Processes the markdown within an HTML block if it's just a class-wrapper
renderer.html = function (html) {
if(_.startsWith(_.trim(html), '<div') && _.endsWith(_.trim(html), '</div>')){
var openTag = html.substring(0, html.indexOf('>')+1);
html = html.substring(html.indexOf('>')+1);
html = html.substring(0, html.lastIndexOf('</div>'));
return `${openTag} ${Markdown(html)} </div>`;
}
return html;
};
const tagTypes = ['div', 'span', 'a'];
const tagRegex = new RegExp('(' +
_.map(tagTypes, (type)=>{
return `\\<${type}|\\</${type}>`;
}).join('|') + ')', 'g');
module.exports = {
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;
},
};

View File

@@ -1,47 +1,54 @@
@media print {
.phb.v1{
.descriptive, blockquote{
box-shadow : none;
}
}
}
@import (less) 'shared/naturalcrit/styles/reset.less';
@import (less) './client/homebrew/phbStyle/phb.fonts.css';
@import (less) './client/homebrew/phbStyle/phb.assets.less';
@import (less) './client/homebrew/phbStyle/phb.depricated.less';
//Colors
@background : #EEE5CE;
@noteGreen : #e0e5c1;
@headerUnderline : #c9ad6a;
@horizontalRule : #9c2b1b;
@headerText : #58180D;
@monsterStatBackground : #FDF1DC;
@page { margin: 0; }
body {
counter-reset : phb-page-numbers;
}
*{
-webkit-print-color-adjust : exact;
}
.useSansSerif(){
font-family : ScalySans;
em{
.phb.v1{
@import (less) './phb.fonts.v1.css';
@import (less) './phb.assets.v1.less';
//Colors
@background : #EEE5CE;
@noteGreen : #e0e5c1;
@headerUnderline : #c9ad6a;
@horizontalRule : #9c2b1b;
@headerText : #58180D;
@monsterStatBackground : #FDF1DC;
@page { margin: 0; }
.useSansSerif(){
font-family : ScalySans;
font-style : italic;
em{
font-family : ScalySans;
font-style : italic;
}
strong{
font-family : ScalySans;
font-weight : 800;
letter-spacing : -0.02em;
}
}
strong{
font-family : ScalySans;
font-weight : 800;
letter-spacing : -0.02em;
.useColumns(@multiplier : 1){
column-count : 2;
column-fill : auto;
column-gap : 1cm;
column-width : 8cm * @multiplier;
-webkit-column-count : 2;
-moz-column-count : 2;
-webkit-column-width : 8cm * @multiplier;
-moz-column-width : 8cm * @multiplier;
-webkit-column-gap : 1cm;
-moz-column-gap : 1cm;
}
& *{
-webkit-print-color-adjust : exact;
}
}
.useColumns(@multiplier : 1){
column-count : 2;
column-fill : auto;
column-gap : 1cm;
column-width : 8cm * @multiplier;
-webkit-column-count : 2;
-moz-column-count : 2;
-webkit-column-width : 8cm * @multiplier;
-moz-column-width : 8cm * @multiplier;
-webkit-column-gap : 1cm;
-moz-column-gap : 1cm;
}
.phb{
.useColumns();
counter-increment : phb-page-numbers;
position : relative;
@@ -59,6 +66,7 @@ body {
text-rendering : optimizeLegibility;
page-break-before : always;
page-break-after : always;
//*****************************
// * BASE
// *****************************/
@@ -357,124 +365,157 @@ body {
-webkit-column-break-inside : avoid;
column-break-inside : avoid;
}
}
//*****************************
// * SPELL LIST
// *****************************/
.phb .spellList{
.useSansSerif();
column-count : 4;
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
ul+h5{
margin-top : 15px;
}
p, ul{
font-size : 0.352cm;
line-height : 1.3em;
}
ul{
margin-bottom : 0.5em;
padding-left : 1em;
text-indent : -1em;
list-style-type : none;
-webkit-column-break-inside : auto;
column-break-inside : auto;
}
}
//*****************************
// * PRINT
// *****************************/
.phb.print{
blockquote{
box-shadow : none;
}
}
@media print {
.phb .descriptive, .phb blockquote{
box-shadow : none;
}
}
//*****************************
// * WIDE
// *****************************/
.phb .wide{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
//*****************************
// * CLASS TABLE
// *****************************/
.phb .classTable{
margin-top : 25px;
margin-bottom : 40px;
border-collapse : separate;
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 25px 17px;
border-image-repeat : round;
border-image-slice : 150 200 150 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
h5{
margin-bottom : 10px;
}
}
//*****************************
// * CLASS TABLE
// *****************************/
.phb .descriptive{
display : block-inline;
margin-bottom : 1em;
background-color : #faf7ea;
font-family : ScalySans;
border-style : solid;
border-width : 7px;
border-image : @descriptiveBoxImage 12 round;
border-image-outset : 4px;
box-shadow : 0px 0px 6px #faf7ea;
p{
display : block;
padding-bottom : 0px;
line-height : 1.5em;
}
p + p {
padding-top : .8em;
}
em {
font-family : ScalySans;
font-style : italic;
}
strong {
font-family : ScalySans;
font-weight : 800;
letter-spacing : -0.02em;
}
}
.phb pre+.descriptive{
margin-top : 8px;
}
//*****************************
// * TABLE OF CONTENTS
// *****************************/
.phb .toc{
-webkit-column-break-inside : avoid;
column-break-inside : avoid;
a{
color : black;
text-decoration : none;
&:hover{
text-decoration : underline;
//*****************************
// * SPELL LIST
// *****************************/
.spellList{
.useSansSerif();
column-count : 4;
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
ul+h5{
margin-top : 15px;
}
p, ul{
font-size : 0.352cm;
line-height : 1.3em;
}
ul{
margin-bottom : 0.5em;
padding-left : 1em;
text-indent : -1em;
list-style-type : none;
-webkit-column-break-inside : auto;
column-break-inside : auto;
}
}
ul{
padding-left : 0;
list-style-type : none;
//*****************************
// * PRINT
// *****************************/
&.print{
blockquote{
box-shadow : none;
}
}
&>ul>li{
margin-bottom : 10px;
//*****************************
// * WIDE
// *****************************/
.wide{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
//*****************************
// * CLASS TABLE
// *****************************/
.classTable{
margin-top : 25px;
margin-bottom : 40px;
border-collapse : separate;
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 25px 17px;
border-image-repeat : round;
border-image-slice : 150 200 150 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
h5{
margin-bottom : 10px;
}
}
//*****************************
// * CLASS TABLE
// *****************************/
.descriptive{
display : block-inline;
margin-bottom : 1em;
background-color : #faf7ea;
font-family : ScalySans;
border-style : solid;
border-width : 7px;
border-image : @descriptiveBoxImage 12 round;
border-image-outset : 4px;
box-shadow : 0px 0px 6px #faf7ea;
p{
display : block;
padding-bottom : 0px;
line-height : 1.5em;
}
p + p {
padding-top : .8em;
}
em {
font-family : ScalySans;
font-style : italic;
}
strong {
font-family : ScalySans;
font-weight : 800;
letter-spacing : -0.02em;
}
}
pre+.descriptive{
margin-top : 8px;
}
//*****************************
// * TABLE OF CONTENTS
// *****************************/
.toc{
-webkit-column-break-inside : avoid;
column-break-inside : avoid;
a{
color : black;
text-decoration : none;
&:hover{
text-decoration : underline;
}
}
ul{
padding-left : 0;
list-style-type : none;
}
&>ul>li{
margin-bottom : 10px;
}
}
//*****************************
// * Old Stuff
// *****************************/
//Double hr for full width elements
hr+hr+blockquote{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
//*****************************
// * CLASS TABLE
// *****************************/
hr+table{
margin-top : -5px;
margin-bottom : 50px;
padding-top : 10px;
border-collapse : separate;
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 37px 17px;
border-image-repeat : round;
border-image-slice : 150 200 150 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
}
h5+hr+table{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
}

View File

@@ -48,7 +48,7 @@ const getTOC = (pages) => {
}
module.exports = function(brew){
const pages = brew.split('\\page');
const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`)

View File

@@ -54,10 +54,13 @@ const Actions = {
setBrew : (brew) => {
dispatch('SET_BREW', brew);
},
updateBrewText : (brewText) => {
dispatch('UPDATE_BREW_TEXT', brewText)
updateBrewCode : (brewCode) => {
dispatch('UPDATE_BREW_CODE', brewCode)
},
updateMetaData : (meta) => {
updateBrewStyle : (style) => {
dispatch('UPDATE_BREW_STYLE', style)
},
updateMetadata : (meta) => {
dispatch('UPDATE_META', meta);
},
pendingSave : () => {
@@ -67,8 +70,9 @@ const Actions = {
},
localPrint : ()=>{
localStorage.setItem('print', Store.getBrewText());
window.open('/print?dialog=true&local=print','_blank');
const key = 'print';
localStorage.setItem(key, JSON.stringify(Store.getBrew()));
window.open(`/print?dialog=true&local=${key}`,'_blank');
},
print : ()=>{
window.open(`/print/${Store.getBrew().shareId}?dialog=true`, '_blank').focus();

View File

@@ -8,6 +8,7 @@ let State = {
brew : {
text : '',
style : '',
shareId : undefined,
editId : undefined,
createdAt : undefined,
@@ -29,9 +30,15 @@ const Store = flux.createStore({
SET_BREW : (brew) => {
State.brew = brew;
},
UPDATE_BREW_TEXT : (brewText) => {
State.brew.text = brewText;
State.errors = Markdown.validate(brewText);
UPDATE_BREW_CODE : (brewCode) => {
State.brew.text = brewCode;
//TODO: Remove?
State.errors = Markdown.validate(brewCode);
},
UPDATE_BREW_STYLE : (style) => {
//TODO: add in an error checker?
State.brew.style = style;
},
UPDATE_META : (meta) => {
State.brew = _.merge({}, State.brew, meta);
@@ -50,11 +57,14 @@ Store.init = (state)=>{
Store.getBrew = ()=>{
return State.brew;
};
Store.getBrewText = ()=>{
Store.getBrewCode = ()=>{
return State.brew.text;
};
Store.getBrewStyle = ()=>{
return State.brew.style;
};
Store.getMetaData = ()=>{
return _.omit(State.brew, ['text']);
return _.omit(State.brew, ['text', 'style']);
};
Store.getErrors = ()=>{
return State.errors;

View File

@@ -0,0 +1,30 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const BrewCard = React.createClass({
getDefaultProps: function() {
return {
brew : {
shareId : '',
title : '',
description : '',
views : 0,
editId : false
}
};
},
render: function(){
const brew = this.props.brew;
return <div className='brewCard'>
<h3>{brew.title}</h3>
<p>{brew.description}</p>
</div>
}
});
module.exports = BrewCard;

View File

@@ -0,0 +1,10 @@
.brewCard{
width : 300px;
height : 100px;
//border : 1px solid black;
background-image : url('/assets/homebrewery/brewCard/monster_bg.jpg');
position : relative;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

View File

Before

Width:  |  Height:  |  Size: 530 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -3,34 +3,37 @@ const _ = require('lodash');
const cx = require('classnames');
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
const SnippetBar = require('./snippetbar/snippetbar.jsx');
const MetadataEditor = require('./metadataEditor/metadataEditor.jsx');
const Menubar = require('./menubar/menubar.jsx');
const splice = function(str, index, inject){
return str.slice(0, index) + inject + str.slice(index);
};
const SNIPPETBAR_HEIGHT = 25;
const MENUBAR_HEIGHT = 25;
const BrewEditor = React.createClass({
getDefaultProps: function() {
return {
value : '',
onChange : ()=>{},
brew : {
text : '',
style : '',
},
metadata : {},
onMetadataChange : ()=>{},
onCodeChange : ()=>{},
onStyleChange : ()=>{},
onMetaChange : ()=>{},
};
},
getInitialState: function() {
return {
showMetadataEditor: false
view : 'code', //'code', 'style', 'meta'
};
},
cursorPosition : {
line : 0,
ch : 0
},
isCode : function(){ return this.state.view == 'code' },
isStyle : function(){ return this.state.view == 'style' },
isMeta : function(){ return this.state.view == 'meta' },
componentDidMount: function() {
this.updateEditorSize();
@@ -42,54 +45,80 @@ const BrewEditor = React.createClass({
},
updateEditorSize : function() {
let paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= SNIPPETBAR_HEIGHT + 1;
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
if(this.refs.codeEditor){
let paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= MENUBAR_HEIGHT + 1;
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
}
},
handleTextChange : function(text){
this.props.onChange(text);
},
handleCursorActivty : function(curpos){
this.cursorPosition = curpos;
},
handleInject : function(injectText){
const lines = this.props.value.split('\n');
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
const text = (this.isCode() ? this.props.brew.text : this.props.brew.style);
this.handleTextChange(lines.join('\n'));
this.refs.codeEditor.setCursorPosition(this.cursorPosition.line, this.cursorPosition.ch + injectText.length);
const lines = text.split('\n');
const cursorPos = this.refs.codeEditor.getCursorPosition();
lines[cursorPos.line] = splice(lines[cursorPos.line], cursorPos.ch, injectText);
this.refs.codeEditor.setCursorPosition(cursorPos.line, cursorPos.ch + injectText.length);
if(this.state.view == 'code') this.props.onCodeChange(lines.join('\n'));
if(this.state.view == 'style') this.props.onStyleChange(lines.join('\n'));
},
handgleToggle : function(){
handleViewChange : function(newView){
this.setState({
showMetadataEditor : !this.state.showMetadataEditor
})
view : newView
}, this.updateEditorSize);
},
brewJump : function(){
const currentPage = this.getCurrentPage();
window.location.hash = 'p' + currentPage;
},
//Called when there are changes to the editor's dimensions
/*
update : function(){
this.refs.codeEditor.updateSize();
if(this.refs.codeEditor) this.refs.codeEditor.updateSize();
},
*/
//TODO: convert this into a generic function for columns and blocks
//MOve this to a util.sj file
highlightPageLines : function(){
if(!this.refs.codeEditor) return;
if(!this.isCode()) return;
const codeMirror = this.refs.codeEditor.codeMirror;
const lineNumbers = _.reduce(this.props.value.split('\n'), (r, line, lineNumber)=>{
const lineNumbers = _.reduce(this.props.brew.text.split('\n'), (r, line, lineNumber)=>{
if(line.indexOf('\\page') !== -1){
codeMirror.addLineClass(lineNumber, 'background', 'pageLine');
r.push(lineNumber);
}
if(line.indexOf('\\column') === 0){
codeMirror.addLineClass(lineNumber, 'text', 'columnSplit');
r.push(lineNumber);
}
if(_.startsWith(line, '{{') || _.startsWith(line, '}}')){
codeMirror.addLineClass(lineNumber, 'text', 'block');
}
return r;
}, []);
return lineNumbers
},
/*
renderMetadataEditor : function(){
if(!this.state.showMetadataEditor) return;
return <MetadataEditor
@@ -97,25 +126,44 @@ const BrewEditor = React.createClass({
onChange={this.props.onMetadataChange}
/>
},
*/
renderEditor : function(){
if(this.isMeta()){
return <MetadataEditor
metadata={this.props.brew}
onChange={this.props.onMetaChange} />
}
if(this.isStyle()){
return <CodeEditor key='style'
ref='codeEditor'
language='css'
value={this.props.brew.style}
onChange={this.props.onStyleChange} />
}
if(this.isCode()){
return <CodeEditor key='code'
ref='codeEditor'
language='gfm'
value={this.props.brew.text}
onChange={this.props.onCodeChange} />
}
},
render : function(){
this.highlightPageLines();
return <div className='brewEditor' ref='main'>
<Menubar
view={this.state.view}
onViewChange={this.handleViewChange}
onSnippetInject={this.handleInject}
/>
{this.renderEditor()}
return<div className='brewEditor' ref='main'>
<SnippetBar
brew={this.props.value}
onInject={this.handleInject}
onToggle={this.handgleToggle}
showmeta={this.state.showMetadataEditor} />
{this.renderMetadataEditor()}
<CodeEditor
ref='codeEditor'
wrap={true}
language='gfm'
value={this.props.value}
onChange={this.handleTextChange}
onCursorActivity={this.handleCursorActivty} />
</div>
/*

View File

@@ -8,6 +8,14 @@
background-color : fade(#333, 15%);
border-bottom : #333 solid 1px;
}
.block{
color : purple;
//font-style: italic;
}
.columnSplit{
font-style : italic;
color : grey;
}
}
.brewJump{

View File

@@ -5,9 +5,10 @@ const BrewEditor = require('./brewEditor.jsx')
module.exports = Store.createSmartComponent(BrewEditor, ()=>{
return {
value : Store.getBrewText(),
onChange : Actions.updateBrewText,
metadata : Store.getMetaData(),
onMetadataChange : Actions.updateMetaData,
brew : Store.getBrew(),
onCodeChange : Actions.updateBrewCode,
onStyleChange : Actions.updateBrewStyle,
onMetaChange : Actions.updateMetadata,
};
});

View File

@@ -0,0 +1,76 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const SnippetMap = require('./snippet.map.js');
const SnippetGroup = require('./snippetGroup/snippetGroup.jsx');
const Menubar = React.createClass({
getDefaultProps: function() {
return {
view : 'code',
onViewChange : ()=>{},
onSnippetInject : ()=>{},
};
},
//TODO: remove
renderDevGroup : function(){
const Snippets = require('homebrewery/snippets/brew');
const snippets = _.map(Snippets, (gen, name)=>{
return {
name,
gen,
icon : 'fa-question'
}
})
return <SnippetGroup
name='All'
icon='fa-rocket'
snippets={snippets}
onClick={this.props.onSnippetInject}
key='dev'
/>
},
renderSnippets : function(){
if(this.props.view == 'meta') return ;
let mapping;
if(this.props.view == 'code') mapping = SnippetMap.brew;
if(this.props.view == 'style') mapping = SnippetMap.style;
let groups = _.map(mapping, (group)=>{
return <SnippetGroup {...group} onClick={this.props.onSnippetInject} key={group.name} />
});
groups = groups.concat(this.renderDevGroup());
return <div className='snippets'>{groups} </div>
},
render: function(){
return <div className='menubar'>
{this.renderSnippets()}
<div className='editors'>
<div className={cx('code', {selected : this.props.view == 'code'})}
onClick={this.props.onViewChange.bind(null, 'code')}>
<i className='fa fa-beer' />
</div>
<div className={cx('style', {selected : this.props.view == 'style'})}
onClick={this.props.onViewChange.bind(null, 'style')}>
<i className='fa fa-paint-brush' />
</div>
<div className={cx('meta', {selected : this.props.view == 'meta'})}
onClick={this.props.onViewChange.bind(null, 'meta')}>
<i className='fa fa-bars' />
</div>
</div>
</div>
}
});
module.exports = Menubar;

View File

@@ -0,0 +1,40 @@
.menubar{
@menuHeight : 25px;
position : relative;
height : @menuHeight;
background-color : #ddd;
.editors{
position : absolute;
display : flex;
top : 0px;
right : 0px;
height : @menuHeight;
width : 90px;
justify-content : space-between;
&>div{
height : @menuHeight;
width : @menuHeight;
cursor : pointer;
line-height : @menuHeight;
text-align : center;
&:hover,&.selected{
background-color : #999;
}
&.code{
.tooltipLeft('Brew Editor');
}
&.style{
.tooltipLeft('Style Editor');
}
&.meta{
.tooltipLeft('Metadata');
}
}
}
.snippets{
display : flex;
height : 100%;
}
}

View File

@@ -0,0 +1,48 @@
const Snippets = require('homebrewery/snippets');
module.exports = {
brew : [
{
name : 'PHB',
icon : 'fa-book',
snippets : [
{
name : 'Spell',
icon : 'fa-magic',
gen : Snippets.brew.spell
},
{
name : 'Table',
icon : 'fa-table',
gen : Snippets.brew.table
},
]
},
{
name : 'Mods',
icon : 'fa-gear',
snippets : []
}
],
style : [
{
name : 'Print',
icon : 'fa-print',
snippets : [
{
name : 'Ink Friendly',
icon : 'fa-tint',
gen : Snippets.style.inkFriendly
},
{
name : 'A4 Page Size',
icon : 'fa-file',
gen : Snippets.style.a4
},
]
}
]
}

View File

@@ -0,0 +1,41 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const SnippetGroup = React.createClass({
getDefaultProps: function() {
return {
name : '',
icon : 'fa-rocket',
snippets : [],
onClick : function(){},
};
},
handleSnippetClick : function(snippet){
this.props.onClick(snippet.gen());
},
renderSnippets : function(){
return _.map(this.props.snippets, (snippet)=>{
return <div className='snippet' key={snippet.name} onClick={this.handleSnippetClick.bind(this, snippet)}>
<i className={'fa fa-fw ' + snippet.icon} />
{snippet.name}
</div>
})
},
render : function(){
return <div className='snippetGroup'>
<div className='text'>
<i className={'fa fa-fw ' + this.props.icon} />
<span className='groupName'>{this.props.name}</span>
</div>
<div className='dropdown'>
{this.renderSnippets()}
</div>
</div>
},
});
module.exports = SnippetGroup;

View File

@@ -0,0 +1,56 @@
.snippetGroup{
//display : inline-block;
display : flex;
height : 100%;
align-items : center;
//height : @menuHeight;
padding : 0px 5px;
cursor : pointer;
font-size : 0.6em;
font-weight : 800;
///line-height : @menuHeight;
text-transform : uppercase;
border-right : 1px solid black;
i{
vertical-align : middle;
margin-right : 3px;
font-size : 1.2em;
}
&:hover, &.selected{
background-color : #999;
}
.text{
//line-height : @menuHeight;
.groupName{
font-size : 10px;
}
}
&:hover{
.dropdown{
visibility : visible;
}
}
.dropdown{
position : absolute;
top : 100%;
visibility : hidden;
z-index : 1000;
margin-left : -5px;
padding : 0px;
background-color : #ddd;
.snippet{
.animate(background-color);
padding : 10px;
cursor : pointer;
font-size : 10px;
i{
margin-right : 8px;
font-size : 13px;
}
&:hover{
background-color : #999;
}
}
}
}

View File

@@ -74,6 +74,7 @@ const MetadataEditor = React.createClass({
},
renderPublish : function(){
//TODO: Move the publish element into here
if(this.props.metadata.published){
return <button className='unpublish' onClick={this.handlePublish.bind(null, false)}>
<i className='fa fa-ban' /> unpublish
@@ -139,13 +140,12 @@ const MetadataEditor = React.createClass({
<textarea value={this.props.metadata.description} className='value'
onChange={this.handleFieldChange.bind(null, 'description')} />
</div>
{/*}
<div className='field tags'>
<label>tags</label>
<textarea value={this.props.metadata.tags}
onChange={this.handleFieldChange.bind(null, 'tags')} />
<div className='field thumbnail'>
<label>thumbnail</label>
<input type='text' className='value'
value={this.props.metadata.thumbnail}
onChange={this.handleFieldChange.bind(null, 'thumbnail')} />
</div>
*/}
<div className='field systems'>
<label>systems</label>

View File

@@ -1,11 +1,11 @@
.metadataEditor{
position : absolute;
z-index : 10000;
box-sizing : border-box;
width : 100%;
padding : 25px;
background-color : #999;
// background-color : #999;
background-color: white;
.field{
display : flex;
width : 100%;

View File

@@ -1,94 +0,0 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
const Snippets = require('./snippets/snippets.js');
const execute = function(val, brew){
if(_.isFunction(val)) return val(brew);
return val;
}
const Snippetbar = React.createClass({
getDefaultProps: function() {
return {
brew : '',
onInject : ()=>{},
onToggle : ()=>{},
showmeta : false
};
},
handleSnippetClick : function(injectedText){
this.props.onInject(injectedText)
},
renderSnippetGroups : function(){
return _.map(Snippets, (snippetGroup)=>{
return <SnippetGroup
brew={this.props.brew}
groupName={snippetGroup.groupName}
icon={snippetGroup.icon}
snippets={snippetGroup.snippets}
key={snippetGroup.groupName}
onSnippetClick={this.handleSnippetClick}
/>
})
},
render : function(){
return <div className='snippetBar'>
{this.renderSnippetGroups()}
<div className={cx('toggleMeta', {selected: this.props.showmeta})}
onClick={this.props.onToggle}>
<i className='fa fa-bars' />
</div>
</div>
}
});
module.exports = Snippetbar;
const SnippetGroup = React.createClass({
getDefaultProps: function() {
return {
brew : '',
groupName : '',
icon : 'fa-rocket',
snippets : [],
onSnippetClick : function(){},
};
},
handleSnippetClick : function(snippet){
this.props.onSnippetClick(execute(snippet.gen, this.props.brew));
},
renderSnippets : function(){
return _.map(this.props.snippets, (snippet)=>{
return <div className='snippet' key={snippet.name} onClick={this.handleSnippetClick.bind(this, snippet)}>
<i className={'fa fa-fw ' + snippet.icon} />
{snippet.name}
</div>
})
},
render : function(){
return <div className='snippetGroup'>
<div className='text'>
<i className={'fa fa-fw ' + this.props.icon} />
<span className='groupName'>{this.props.groupName}</span>
</div>
<div className='dropdown'>
{this.renderSnippets()}
</div>
</div>
},
});

View File

@@ -1,72 +0,0 @@
.snippetBar{
@height : 25px;
position : relative;
height : @height;
background-color : #ddd;
.toggleMeta{
position : absolute;
top : 0px;
right : 0px;
height : @height;
width : @height;
cursor : pointer;
line-height : @height;
text-align : center;
&:hover, &.selected{
background-color : #999;
}
}
.snippetGroup{
display : inline-block;
height : @height;
padding : 0px 5px;
cursor : pointer;
font-size : 0.6em;
font-weight : 800;
line-height : @height;
text-transform : uppercase;
border-right : 1px solid black;
i{
vertical-align : middle;
margin-right : 3px;
font-size : 1.2em;
}
&:hover, &.selected{
background-color : #999;
}
.text{
line-height : @height;
.groupName{
font-size : 10px;
}
}
&:hover{
.dropdown{
visibility : visible;
}
}
.dropdown{
position : absolute;
top : 100%;
visibility : hidden;
z-index : 1000;
margin-left : -5px;
padding : 0px;
background-color : #ddd;
.snippet{
.animate(background-color);
padding : 5px;
cursor : pointer;
font-size : 10px;
i{
margin-right : 8px;
font-size : 13px;
}
&:hover{
background-color : #999;
}
}
}
}
}

View File

@@ -7,9 +7,9 @@ const BrewRenderer = require('../brewRenderer/brewRenderer.smart.jsx');
const BrewInterface = React.createClass({
handleSplitMove : function(){
console.log('split move!');
const BrewEditor = this.refs.editor.refs.wrappedComponent;
BrewEditor.updateEditorSize();
},
render: function(){
return <SplitPane onDragFinish={this.handleSplitMove} ref='pane'>

View File

@@ -2,25 +2,29 @@ const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
//TODO: Convert old renderer to just be the old markdown parser
const OldBrewRenderer = require('depricated/brewRendererOld/brewRendererOld.jsx');
const Markdown = require('homebrewery/markdown.js');
const ErrorBar = require('./errorBar/errorBar.jsx');
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx')
const Store = require('homebrewery/brew.store.js');
const PAGE_HEIGHT = 1056;
const PPR_THRESHOLD = 50;
const PPR_RANGE = 0;
const BrewRenderer = React.createClass({
getDefaultProps: function() {
return {
brewText : '',
errors : []
text : '',
style : '',
version : 2,
//usePPR : false // TODO: maybe make this into an page index to render
pprPage : false
};
},
/*
getInitialState: function() {
const pages = this.props.brewText.split('\\page');
const pages = this.props.brew.text.split('\\page');
return {
viewablePageNumber: 0,
@@ -32,20 +36,24 @@ const BrewRenderer = React.createClass({
},
height : 0,
pageHeight : PAGE_HEIGHT,
*/
lastRender : <div></div>,
/*
componentDidMount: function() {
this.updateSize();
window.addEventListener("resize", this.updateSize);
window.addEventListener('resize', this.updateSize);
},
componentWillUnmount: function() {
window.removeEventListener("resize", this.updateSize);
window.removeEventListener('resize', this.updateSize);
},
componentWillReceiveProps: function(nextProps) {
if(this.refs.pages && this.refs.pages.firstChild) this.pageHeight = this.refs.pages.firstChild.clientHeight;
const pages = nextProps.brewText.split('\\page');
const pages = nextProps.brew.text.split('\\page');
this.setState({
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD
@@ -57,22 +65,21 @@ const BrewRenderer = React.createClass({
if(this.refs.pages && this.refs.pages.firstChild) this.pageHeight = this.refs.pages.firstChild.clientHeight;
}, 1);
this.setState({
height : this.refs.main.parentNode.clientHeight,
isMounted : true
});
},
handleScroll : function(e){
this.setState({
viewablePageNumber : Math.floor(e.target.scrollTop / this.pageHeight)
});
},
*/
shouldRender : function(pageText, index){
if(!this.state.isMounted) return false;
var viewIndex = this.state.viewablePageNumber;
//TODO: increase range, make into a simple function with lodash
if(index == viewIndex - 1) return true;
if(index == viewIndex) return true;
if(index == viewIndex + 1) return true;
@@ -83,12 +90,8 @@ const BrewRenderer = React.createClass({
return false;
},
renderPageInfo : function(){
return <div className='pageInfo'>
{this.state.viewablePageNumber + 1} / {this.state.pages.length}
</div>
},
/*
renderPPRmsg : function(){
if(!this.state.usePPR) return;
@@ -96,18 +99,25 @@ const BrewRenderer = React.createClass({
Partial Page Renderer enabled, because your brew is so large. May effect rendering.
</div>
},
*/
renderDummyPage : function(index){
return <div className='phb' id={`p${index + 1}`} key={index}>
<i className='fa fa-spinner fa-spin' />
</div>
},
renderPage : function(pageText, index){
return <div className='phb' id={`p${index + 1}`} dangerouslySetInnerHTML={{__html:Markdown.render(pageText)}} key={index} />
const html = Markdown.render(pageText);
return <div className='phb v2' id={`p${index + 1}`} dangerouslySetInnerHTML={{__html:html}} key={index} />
},
renderPPR : function(pages, pprPageIndex){
return _.map(pages, (page, index)=>{
if(_.inRange(index, pprPageIndex - PPR_RANGE, pprPageIndex + PPR_RANGE +1)){
this.renderPage(page, index);
}
return <div className='phb v2' id={`p${index + 1}`} key={index} />;
});
},
renderPages : function(){
/*
if(this.state.usePPR){
return _.map(this.state.pages, (page, index)=>{
if(this.shouldRender(page, index)){
@@ -117,27 +127,33 @@ const BrewRenderer = React.createClass({
}
});
}
if(this.props.errors && this.props.errors.length) return this.lastRender;
this.lastRender = _.map(this.state.pages, (page, index)=>{
return this.renderPage(page, index);
});
return this.lastRender;
*/
const pages = this.props.text.split('\\page');
console.log(this.props.pprPage);
if(this.props.pprPage !== false) return this.renderPPR(pages, this.props.pprPage);
return _.map(pages, (page, index)=>this.renderPage(page, index));
//TODO: See if you need error handling?
//if(this.props.errors && this.props.errors.length) return this.lastRender;
},
//TODO: This is pretty bad
renderStyle : function(){
return <style>{this.props.style.replace(/;/g, ' !important;')}</style>
},
render : function(){
return <div className='brewRenderer'
onScroll={this.handleScroll}
ref='main'
style={{height : this.state.height}}>
//TODO: Add in the brew version check
//if(this.props.brew.version == 1) return <OldBrewRenderer value={this.props.brew.text} />;
<ErrorBar errors={this.props.errors} />
<RenderWarnings />
<div className='pages' ref='pages'>
{this.renderPages()}
</div>
{this.renderPageInfo()}
{this.renderPPRmsg()}
return <div className='brewRenderer'>
{this.renderStyle()}
{this.renderPages()}
</div>
}
});

View File

@@ -1,39 +1,2 @@
@import (less) './client/homebrew/phbStyle/phb.style.less';
.pane{
position : relative;
}
.brewRenderer{
overflow-y : scroll;
.pageInfo{
position : absolute;
right : 17px;
bottom : 0;
z-index : 1000;
padding : 8px 10px;
background-color : #333;
font-size : 10px;
font-weight : 800;
color : white;
}
.ppr_msg{
position : absolute;
left : 0px;
bottom : 0;
z-index : 1000;
padding : 8px 10px;
background-color : #333;
font-size : 10px;
font-weight : 800;
color : white;
}
.pages{
margin : 30px 0px;
&>.phb{
margin-right : auto;
margin-bottom : 30px;
margin-left : auto;
box-shadow : 1px 4px 14px #000;
}
}
}
@import (less) './shared/homebrewery/phb_style/phb.less';

View File

@@ -3,8 +3,16 @@ const BrewRenderer = require('./brewRenderer.jsx');
module.exports = Store.createSmartComponent(BrewRenderer, () => {
const brew = Store.getBrew();
return {
brewText : Store.getBrewText(),
brew : Store.getBrew(),
brewText : Store.getBrewCode(),
style : Store.getBrewStyle(),
errors : Store.getErrors()
}
});

View File

@@ -0,0 +1,84 @@
const React = require('react');
const _ = require('lodash');
const cx = require('classnames');
//const ErrorBar = require('./errorBar/errorBar.jsx');
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
//const Store = require('homebrewery/brew.store.js');
const PAGE_HEIGHT = 1056;
const BrewRenderer = require('../brewRenderer/brewRenderer.jsx');
const BrewView = React.createClass({
getDefaultProps: function() {
return {
brew : {
text : '',
style : ''
},
};
},
getInitialState: function() {
const pages = this.props.brew.text.split('\\page');
return {
viewablePageNumber: 0,
//height : 0,
//isMounted : false,
//TODO: Change to page count ?
pages : pages,
};
},
//height : 0,
pageHeight : PAGE_HEIGHT,
componentWillReceiveProps: function(nextProps) {
//if(this.refs.pages && this.refs.pages.firstChild) this.pageHeight = this.refs.pages.firstChild.clientHeight;
const pages = nextProps.brew.text.split('\\page');
this.setState({
pages : pages,
//usePPR : pages.length >= PPR_THRESHOLD
})
},
handleScroll : function(e){
this.setState({
viewablePageNumber : Math.floor(e.target.scrollTop / PAGE_HEIGHT) //this.pageHeight)
});
},
renderPageInfo : function(){
return <div className='pageInfo'>
{this.state.viewablePageNumber + 1} / {this.state.pages.length}
</div>
},
render: function(){
return <div className='brewView' onScroll={this.handleScroll}>
<BrewRenderer
text={this.props.brew.text}
style={this.props.brew.style}
version={this.props.brew.version}
pprPage={2}
/>
{this.renderPageInfo()}
</div>
}
});
module.exports = BrewView;

View File

@@ -0,0 +1,42 @@
.pane{
position : relative;
}
.brewView{
overflow-y : scroll;
height : 100%;
.pageInfo{
position : absolute;
right : 17px;
bottom : 0;
z-index : 1000;
padding : 8px 10px;
background-color : #333;
font-size : 10px;
font-weight : 800;
color : white;
}
/*
.ppr_msg{
position : absolute;
left : 0px;
bottom : 0;
z-index : 1000;
padding : 8px 10px;
background-color : #333;
font-size : 10px;
font-weight : 800;
color : white;
}
*/
.brewRenderer{
margin : 30px 0px;
&>.phb{
margin-right : auto;
margin-bottom : 30px;
margin-left : auto;
box-shadow : 1px 4px 14px #000;
}
}
}

View File

@@ -0,0 +1,11 @@
//const Actions = require('homebrewery/brew.actions.js');
const Store = require('homebrewery/brew.store.js');
const BrewView = require('./brewView.jsx')
module.exports = Store.createSmartComponent(BrewView, ()=>{
return {
brew : Store.getBrew()
};
});

View File

@@ -1,82 +1,61 @@
var _ = require('lodash');
var Markdown = require('marked');
var renderer = new Markdown.Renderer();
const _ = require('lodash');
//const Markdown = require('marked');
const Markdown = require('./marked.lib.js');
//Processes the markdown within an HTML block if it's just a class-wrapper
renderer.html = function (html) {
if(_.startsWith(_.trim(html), '<div') && _.endsWith(_.trim(html), '</div>')){
var openTag = html.substring(0, html.indexOf('>')+1);
html = html.substring(html.indexOf('>')+1);
html = html.substring(0, html.lastIndexOf('</div>'));
return `${openTag} ${Markdown(html)} </div>`;
}
return html;
const renderer = new Markdown.Renderer();
let blockCount = 0;
renderer.paragraph = function(text){
const blockReg = /{{[\w|,]+|}}/g;
const matches = text.match(blockReg);
if(!matches) return `\n<p>${text}</p>\n`;
let matchIndex = 0;
const res = _.reduce(text.split(blockReg), (r, text) => {
//if(text) r.push(text);
if(text) r.push(Markdown(text, {renderer : renderer, sanitize: true}));
const block = matches[matchIndex];
if(block && block[0] == '{'){
r.push(`\n\n<div class="block ${block.substring(2).split(',').join(' ')}">`);
blockCount++;
}
if(block == '}}' && blockCount !== 0){
r.push('</div>\n\n');
blockCount--;
}
matchIndex++;
return r;
}, []).join('\n');
return res;
};
const tagTypes = ['div', 'span', 'a'];
const tagRegex = new RegExp('(' +
_.map(tagTypes, (type)=>{
return `\\<${type}|\\</${type}>`;
}).join('|') + ')', 'g');
renderer.image = function(href, title, text){
return `<img src="${href}" class="${text.split(',').join(' ')}"></img>`;
};
renderer.list = function(list, isOrdered, isDef){
if(isDef) return `<ul class='alt'>${list}</ul>`;
if(isOrdered) return `<ol>${list}</ol>`;
return `<ul>${list}</ul>`;
}
module.exports = {
marked : Markdown,
render : (rawBrewText)=>{
return Markdown(rawBrewText, {renderer : renderer})
blockCount = 0;
rawBrewText = rawBrewText.replace(/\\column/g, '{{columnSplit }}');
let html = Markdown(rawBrewText,{renderer : renderer, sanitize: true});
//Close all hanging block tags
html += _.times(blockCount, ()=>{return '</div>'}).join('\n');
return html;
},
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;
return [];
},
};

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

View File

Before

Width:  |  Height:  |  Size: 327 B

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

Some files were not shown because too many files have changed in this diff Show More