Compare commits
63 Commits
newAdmin
...
newRendere
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
570d86448d | ||
|
|
18d0b68eb0 | ||
|
|
74d17f32a5 | ||
|
|
469fa957aa | ||
|
|
4d0ebcb1d8 | ||
|
|
10f9ac14c7 | ||
|
|
705adbe2c6 | ||
|
|
7e7428504e | ||
|
|
c28647726a | ||
|
|
a30bd79399 | ||
|
|
87a9c46202 | ||
|
|
380bbd661f | ||
|
|
fa203047da | ||
|
|
b39f9041c2 | ||
|
|
2023ae4f6a | ||
|
|
d5f04ca2b6 | ||
|
|
f99bcabad0 | ||
|
|
32317bfa6f | ||
|
|
1946a50ce0 | ||
|
|
ee1827eab0 | ||
|
|
20371a8b3d | ||
|
|
28a3f31caa | ||
|
|
c647bdf5ee | ||
|
|
eb1827cedb | ||
|
|
a3251dfa19 | ||
|
|
30d3fcf168 | ||
|
|
393df1b181 | ||
|
|
94a3a96960 | ||
|
|
bfb2cea48e | ||
|
|
00f2703d0b | ||
|
|
4c874149fb | ||
|
|
0705e08381 | ||
|
|
e112808706 | ||
|
|
234d216d64 | ||
|
|
ef0265f4fa | ||
|
|
fbc18a017c | ||
|
|
9d4d337bb9 | ||
|
|
0d0ce101f3 | ||
|
|
540c00cb0c | ||
|
|
446ae9cbcf | ||
|
|
dc486cfba9 | ||
|
|
a6a1f41e77 | ||
|
|
fd567352a4 | ||
|
|
a33b1d845d | ||
|
|
b20f4ffb46 | ||
|
|
2f69ef3fe8 | ||
|
|
1da1f90a35 | ||
|
|
bd08858745 | ||
|
|
304cd0ffcd | ||
|
|
b40e5bc4c4 | ||
|
|
0663737e1c | ||
|
|
307dd2d9ba | ||
|
|
95c91b6ba8 | ||
|
|
c8c46725a2 | ||
|
|
7001b71d91 | ||
|
|
cbab4f4959 | ||
|
|
22d9982888 | ||
|
|
76ced9ca49 | ||
|
|
b1db8040a4 | ||
|
|
c8b089f7fb | ||
|
|
97c0443c76 | ||
|
|
c470bed591 | ||
|
|
4593099914 |
13
.github/issue_template.md
vendored
@@ -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>
|
||||
@@ -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(){
|
||||
|
||||
@@ -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>.
|
||||
|
||||
@@ -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>
|
||||
};
|
||||
@@ -120,7 +120,7 @@
|
||||
top : 29px;
|
||||
left : -20px;
|
||||
z-index : 1000;
|
||||
width : 120px;
|
||||
width : 170px;
|
||||
padding : 8px;
|
||||
background-color : #333;
|
||||
a{
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)=>{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
};
|
||||
BIN
client/homebrew/pages/homePage/dmg_bg.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
client/homebrew/pages/homePage/fantasy_background.jpg
Normal file
|
After Width: | Height: | Size: 822 KiB |
@@ -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>
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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();}
|
||||
// }
|
||||
BIN
client/homebrew/pages/homePage/phb_bg.jpg
Normal file
|
After Width: | Height: | Size: 171 KiB |
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 864 B |
@@ -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>
|
||||
|
||||
11
package.json
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
@@ -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
@@ -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
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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());
|
||||
15
server.js
@@ -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))
|
||||
|
||||
@@ -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 : {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
*/
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
147
shared/depricated/brewRendererOld/brewRendererOld.jsx
Normal 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;
|
||||
40
shared/depricated/brewRendererOld/brewRendererOld.less
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
shared/depricated/brewRendererOld/errorBar/errorBar.jsx
Normal 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;
|
||||
60
shared/depricated/brewRendererOld/errorBar/errorBar.less
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
shared/depricated/markdown.old.js
Normal 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;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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})**`)
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
30
shared/homebrewery/brewCard/brewCard.jsx
Normal 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;
|
||||
10
shared/homebrewery/brewCard/brewCard.less
Normal 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;
|
||||
|
||||
}
|
||||
BIN
shared/homebrewery/brewCard/monster_bg.jpg
Normal file
|
After Width: | Height: | Size: 339 KiB |
|
Before Width: | Height: | Size: 530 B After Width: | Height: | Size: 530 B |
BIN
shared/homebrewery/brewCard/note_border_shadow.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
@@ -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>
|
||||
|
||||
/*
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
});
|
||||
76
shared/homebrewery/brewEditor/menubar/menubar.jsx
Normal 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;
|
||||
40
shared/homebrewery/brewEditor/menubar/menubar.less
Normal 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%;
|
||||
}
|
||||
}
|
||||
48
shared/homebrewery/brewEditor/menubar/snippet.map.js
Normal 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
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -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>
|
||||
},
|
||||
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'>
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
});
|
||||
84
shared/homebrewery/brewView/brewView.jsx
Normal 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;
|
||||
42
shared/homebrewery/brewView/brewView.less
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
shared/homebrewery/brewView/brewView.smart.jsx
Normal 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()
|
||||
|
||||
};
|
||||
});
|
||||
@@ -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 [];
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
1288
shared/homebrewery/marked.lib.js
Normal file
BIN
shared/homebrewery/phb_style/fonts/Bookinsanity Bold Italic.otf
Normal file
BIN
shared/homebrewery/phb_style/fonts/Bookinsanity Bold.otf
Normal file
BIN
shared/homebrewery/phb_style/fonts/Bookinsanity Italic.otf
Normal file
BIN
shared/homebrewery/phb_style/fonts/Bookinsanity.otf
Normal file
BIN
shared/homebrewery/phb_style/fonts/Mr Eaves Small Caps.otf
Normal file
BIN
shared/homebrewery/phb_style/fonts/Scaly Sans Caps.otf
Normal file
BIN
shared/homebrewery/phb_style/fonts/Scaly Sans.otf
Normal file
BIN
shared/homebrewery/phb_style/fonts/Solbera Imitation.otf
Normal file
BIN
shared/homebrewery/phb_style/img/desc_border.png
Normal file
|
After Width: | Height: | Size: 311 B |
BIN
shared/homebrewery/phb_style/img/divider.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
shared/homebrewery/phb_style/img/dmg_bg.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
shared/homebrewery/phb_style/img/footer.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
shared/homebrewery/phb_style/img/footer_flip.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
shared/homebrewery/phb_style/img/frame_border.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
shared/homebrewery/phb_style/img/monster_bg.jpg
Normal file
|
After Width: | Height: | Size: 339 KiB |
|
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 327 B |
BIN
shared/homebrewery/phb_style/img/note_border - Copy.png
Normal file
|
After Width: | Height: | Size: 530 B |