1
0
mirror of https://github.com/stolksdorf/homebrewery.git synced 2025-12-15 13:45:57 +00:00
This commit is contained in:
Scott Tolksdorf
2018-04-08 22:23:03 -04:00
parent bf27250990
commit ed1b5252be
51 changed files with 2837 additions and 2852 deletions

View File

@@ -1,21 +1,20 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx'); const HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx');
var Admin = React.createClass({ const Admin = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
url : "", url : '',
admin_key : "", admin_key : '',
homebrews : [], homebrews : [],
}; };
}, },
render : function(){ render : function(){
var self = this; return (
return(
<div className='admin'> <div className='admin'>
<header> <header>

View File

@@ -7,14 +7,14 @@ const Moment = require('moment');
const BrewLookup = React.createClass({ const BrewLookup = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
adminKey : '', adminKey : '',
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
query:'', query : '',
resultBrew : null, resultBrew : null,
searching : false searching : false
}; };
@@ -23,19 +23,19 @@ const BrewLookup = React.createClass({
handleChange : function(e){ handleChange : function(e){
this.setState({ this.setState({
query : e.target.value query : e.target.value
}) });
}, },
lookup : function(){ lookup : function(){
this.setState({ searching : true }); this.setState({ searching: true });
request.get(`/admin/lookup/${this.state.query}`) request.get(`/admin/lookup/${this.state.query}`)
.query({ admin_key : this.props.adminKey }) .query({ admin_key: this.props.adminKey })
.end((err, res) => { .end((err, res)=>{
this.setState({ this.setState({
searching : false, searching : false,
resultBrew : (err ? null : res.body) resultBrew : (err ? null : res.body)
}); });
}) });
}, },
renderFoundBrew : function(){ renderFoundBrew : function(){
@@ -46,21 +46,21 @@ const BrewLookup = React.createClass({
return <div className='brewRow'> return <div className='brewRow'>
<div>{brew.title}</div> <div>{brew.title}</div>
<div>{brew.authors.join(', ')}</div> <div>{brew.authors.join(', ')}</div>
<div><a href={'/edit/' + brew.editId} target='_blank'>/edit/{brew.editId}</a></div> <div><a href={`/edit/${brew.editId}`} target='_blank'>/edit/{brew.editId}</a></div>
<div><a href={'/share/' + brew.shareId} target='_blank'>/share/{brew.shareId}</a></div> <div><a href={`/share/${brew.shareId}`} target='_blank'>/share/{brew.shareId}</a></div>
<div>{Moment(brew.updatedAt).fromNow()}</div> <div>{Moment(brew.updatedAt).fromNow()}</div>
<div>{brew.views}</div> <div>{brew.views}</div>
</div> </div>;
}, },
render: function(){ render : function(){
return <div className='brewLookup'> return <div className='brewLookup'>
<h1>Brew Lookup</h1> <h1>Brew Lookup</h1>
<input type='text' value={this.state.query} onChange={this.handleChange} placeholder='edit or share id...' /> <input type='text' value={this.state.query} onChange={this.handleChange} placeholder='edit or share id...' />
<button onClick={this.lookup}><i className='fa fa-search'/></button> <button onClick={this.lookup}><i className='fa fa-search'/></button>
{this.renderFoundBrew()} {this.renderFoundBrew()}
</div> </div>;
} }
}); });

View File

@@ -1,20 +1,20 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var request = require('superagent'); const request = require('superagent');
var BrewSearch = React.createClass({ const BrewSearch = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
admin_key : '' admin_key : ''
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
searchTerm: '', searchTerm : '',
brew : null, brew : null,
searching : false searching : false
}; };
@@ -26,7 +26,7 @@ var BrewSearch = React.createClass({
searching : true searching : true
}); });
request.get('/homebrew/api/search?id=' + this.state.searchTerm) request.get(`/homebrew/api/search?id=${this.state.searchTerm}`)
.query({ .query({
admin_key : this.props.admin_key, admin_key : this.props.admin_key,
}) })
@@ -36,7 +36,7 @@ var BrewSearch = React.createClass({
brew : res.body.brews[0], brew : res.body.brews[0],
searching : false searching : false
}) });
}); });
}, },
@@ -54,7 +54,7 @@ var BrewSearch = React.createClass({
return <div className='brew'> return <div className='brew'>
<div>Edit id : {this.state.brew.editId}</div> <div>Edit id : {this.state.brew.editId}</div>
<div>Share id : {this.state.brew.shareId}</div> <div>Share id : {this.state.brew.shareId}</div>
</div> </div>;
}, },
render : function(){ render : function(){
@@ -64,7 +64,7 @@ var BrewSearch = React.createClass({
<button onClick={this.handleSearchClick}>Search</button> <button onClick={this.handleSearchClick}>Search</button>
{this.renderBrew()} {this.renderBrew()}
</div> </div>;
}, },
}); });

View File

@@ -1,25 +1,24 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var request = require('superagent'); const request = require('superagent');
var Moment = require('moment'); const Moment = require('moment');
var BrewSearch = require('./brewSearch.jsx');
var BrewLookup = require('./brewLookup/brewLookup.jsx');
var HomebrewAdmin = React.createClass({ const BrewLookup = require('./brewLookup/brewLookup.jsx');
getDefaultProps: function() {
const HomebrewAdmin = React.createClass({
getDefaultProps : function() {
return { return {
admin_key : '' admin_key : ''
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
page: 0, page : 0,
count : 20, count : 20,
brewCache : {}, brewCache : {},
total : 0, total : 0,
@@ -43,11 +42,11 @@ var HomebrewAdmin = React.createClass({
brewCache : this.state.brewCache, brewCache : this.state.brewCache,
total : res.body.total, total : res.body.total,
count : res.body.count count : res.body.count
}) });
}) });
}, },
componentDidMount: function() { componentDidMount : function() {
this.fetchBrews(this.state.page); this.fetchBrews(this.state.page);
}, },
@@ -57,31 +56,31 @@ var HomebrewAdmin = React.createClass({
} }
this.setState({ this.setState({
page : page page : page
}) });
}, },
clearInvalidBrews : function(){ clearInvalidBrews : function(){
request.get('/api/invalid') request.get('/api/invalid')
.query({admin_key : this.props.admin_key}) .query({ admin_key: this.props.admin_key })
.end((err, res)=>{ .end((err, res)=>{
if(!confirm("This will remove " + res.body.count + " brews. Are you sure?")) return; if(!confirm(`This will remove ${res.body.count} brews. Are you sure?`)) return;
request.get('/api/invalid') request.get('/api/invalid')
.query({admin_key : this.props.admin_key, do_it : true}) .query({ admin_key: this.props.admin_key, do_it: true })
.end((err, res)=>{ .end((err, res)=>{
alert("Done!") alert('Done!');
}); });
}); });
}, },
deleteBrew : function(brewId){ deleteBrew : function(brewId){
if(!confirm("Are you sure you want to delete '" + brewId + "'?")) return; if(!confirm(`Are you sure you want to delete '${brewId}'?`)) return;
request.get('/api/remove/' + brewId) request.get(`/api/remove/${brewId}`)
.query({admin_key : this.props.admin_key}) .query({ admin_key: this.props.admin_key })
.end(function(err, res){ .end(function(err, res){
window.location.reload(); window.location.reload();
}) });
}, },
handlePageChange : function(dir){ handlePageChange : function(dir){
@@ -90,34 +89,34 @@ var HomebrewAdmin = React.createClass({
renderPagnination : function(){ renderPagnination : function(){
var outOf; let outOf;
if(this.state.total){ if(this.state.total){
outOf = this.state.page + ' / ' + Math.round(this.state.total/this.state.count); outOf = `${this.state.page} / ${Math.round(this.state.total/this.state.count)}`;
} }
return <div className='pagnination'> return <div className='pagnination'>
<i className='fa fa-chevron-left' onClick={this.handlePageChange.bind(this, -1)}/> <i className='fa fa-chevron-left' onClick={()=>this.handlePageChange(-1)}/>
{outOf} {outOf}
<i className='fa fa-chevron-right' onClick={this.handlePageChange.bind(this, 1)}/> <i className='fa fa-chevron-right' onClick={()=>this.handlePageChange(1)}/>
</div> </div>;
}, },
renderBrews : function(){ renderBrews : function(){
var brews = this.state.brewCache[this.state.page] || _.times(this.state.count); const brews = this.state.brewCache[this.state.page] || _.times(this.state.count);
return _.map(brews, (brew)=>{ return _.map(brews, (brew)=>{
return <tr className={cx('brewRow', {'isEmpty' : brew.text == "false"})} key={brew.shareId || brew}> return <tr className={cx('brewRow', { 'isEmpty': brew.text == 'false' })} key={brew.shareId || brew}>
<td><a href={'/edit/' + brew.editId} target='_blank'>{brew.editId}</a></td> <td><a href={`/edit/${brew.editId}`} target='_blank'>{brew.editId}</a></td>
<td><a href={'/share/' + brew.shareId} target='_blank'>{brew.shareId}</a></td> <td><a href={`/share/${brew.shareId}`} target='_blank'>{brew.shareId}</a></td>
<td>{Moment(brew.createdAt).fromNow()}</td> <td>{Moment(brew.createdAt).fromNow()}</td>
<td>{Moment(brew.updatedAt).fromNow()}</td> <td>{Moment(brew.updatedAt).fromNow()}</td>
<td>{Moment(brew.lastViewed).fromNow()}</td> <td>{Moment(brew.lastViewed).fromNow()}</td>
<td>{brew.views}</td> <td>{brew.views}</td>
<td> <td>
<div className='deleteButton' onClick={this.deleteBrew.bind(this, brew.editId)}> <div className='deleteButton' onClick={()=>this.deleteBrew(brew.editId)}>
<i className='fa fa-trash' /> <i className='fa fa-trash' />
</div> </div>
</td> </td>
</tr> </tr>;
}); });
}, },
@@ -138,11 +137,10 @@ var HomebrewAdmin = React.createClass({
{this.renderBrews()} {this.renderBrews()}
</tbody> </tbody>
</table> </table>
</div> </div>;
}, },
render : function(){ render : function(){
var self = this;
return <div className='homebrewAdmin'> return <div className='homebrewAdmin'>
<BrewLookup adminKey={this.props.admin_key} /> <BrewLookup adminKey={this.props.admin_key} />
@@ -165,7 +163,7 @@ var HomebrewAdmin = React.createClass({
<BrewSearch admin_key={this.props.admin_key} /> <BrewSearch admin_key={this.props.admin_key} />
*/} */}
</div> </div>;
} }
}); });

View File

@@ -6,23 +6,23 @@ const Markdown = require('naturalcrit/markdown.js');
const ErrorBar = require('./errorBar/errorBar.jsx'); const ErrorBar = require('./errorBar/errorBar.jsx');
//TODO: move to the brew renderer //TODO: move to the brew renderer
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx') const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
const PAGE_HEIGHT = 1056; const PAGE_HEIGHT = 1056;
const PPR_THRESHOLD = 50; const PPR_THRESHOLD = 50;
const BrewRenderer = React.createClass({ const BrewRenderer = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
text : '', text : '',
errors : [] errors : []
}; };
}, },
getInitialState: function() { getInitialState : function() {
const pages = this.props.text.split('\\page'); const pages = this.props.text.split('\\page');
return { return {
viewablePageNumber: 0, viewablePageNumber : 0,
height : 0, height : 0,
isMounted : false, isMounted : false,
@@ -38,22 +38,22 @@ const BrewRenderer = React.createClass({
pageHeight : PAGE_HEIGHT, pageHeight : PAGE_HEIGHT,
lastRender : <div></div>, lastRender : <div></div>,
componentDidMount: function() { componentDidMount : function() {
this.updateSize(); this.updateSize();
window.addEventListener("resize", this.updateSize); window.addEventListener('resize', this.updateSize);
}, },
componentWillUnmount: function() { componentWillUnmount : function() {
window.removeEventListener("resize", this.updateSize); window.removeEventListener('resize', this.updateSize);
}, },
componentWillReceiveProps: function(nextProps) { componentWillReceiveProps : function(nextProps) {
if(this.refs.pages && this.refs.pages.firstChild) this.pageHeight = this.refs.pages.firstChild.clientHeight; if(this.refs.pages && this.refs.pages.firstChild) this.pageHeight = this.refs.pages.firstChild.clientHeight;
const pages = nextProps.text.split('\\page'); const pages = nextProps.text.split('\\page');
this.setState({ this.setState({
pages : pages, pages : pages,
usePPR : pages.length >= PPR_THRESHOLD usePPR : pages.length >= PPR_THRESHOLD
}) });
}, },
updateSize : function() { updateSize : function() {
@@ -76,7 +76,7 @@ const BrewRenderer = React.createClass({
shouldRender : function(pageText, index){ shouldRender : function(pageText, index){
if(!this.state.isMounted) return false; if(!this.state.isMounted) return false;
var viewIndex = this.state.viewablePageNumber; const viewIndex = this.state.viewablePageNumber;
if(index == viewIndex - 3) return true; if(index == viewIndex - 3) return true;
if(index == viewIndex - 2) return true; if(index == viewIndex - 2) return true;
if(index == viewIndex - 1) return true; if(index == viewIndex - 1) return true;
@@ -94,7 +94,7 @@ const BrewRenderer = React.createClass({
renderPageInfo : function(){ renderPageInfo : function(){
return <div className='pageInfo'> return <div className='pageInfo'>
{this.state.viewablePageNumber + 1} / {this.state.pages.length} {this.state.viewablePageNumber + 1} / {this.state.pages.length}
</div> </div>;
}, },
renderPPRmsg : function(){ renderPPRmsg : function(){
@@ -102,17 +102,17 @@ const BrewRenderer = React.createClass({
return <div className='ppr_msg'> return <div className='ppr_msg'>
Partial Page Renderer enabled, because your brew is so large. May effect rendering. Partial Page Renderer enabled, because your brew is so large. May effect rendering.
</div> </div>;
}, },
renderDummyPage : function(index){ renderDummyPage : function(index){
return <div className='phb' id={`p${index + 1}`} key={index}> return <div className='phb' id={`p${index + 1}`} key={index}>
<i className='fa fa-spinner fa-spin' /> <i className='fa fa-spinner fa-spin' />
</div> </div>;
}, },
renderPage : function(pageText, index){ renderPage : function(pageText, index){
return <div className='phb' id={`p${index + 1}`} dangerouslySetInnerHTML={{__html:Markdown.render(pageText)}} key={index} /> return <div className='phb' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: Markdown.render(pageText) }} key={index} />;
}, },
renderPages : function(){ renderPages : function(){
@@ -120,7 +120,7 @@ const BrewRenderer = React.createClass({
return _.map(this.state.pages, (page, index)=>{ return _.map(this.state.pages, (page, index)=>{
if(this.shouldRender(page, index)){ if(this.shouldRender(page, index)){
return this.renderPage(page, index); return this.renderPage(page, index);
}else{ } else {
return this.renderDummyPage(index); return this.renderDummyPage(index);
} }
}); });
@@ -136,7 +136,7 @@ const BrewRenderer = React.createClass({
return <div className='brewRenderer' return <div className='brewRenderer'
onScroll={this.handleScroll} onScroll={this.handleScroll}
ref='main' ref='main'
style={{height : this.state.height}}> style={{ height: this.state.height }}>
<ErrorBar errors={this.props.errors} /> <ErrorBar errors={this.props.errors} />
<RenderWarnings /> <RenderWarnings />
@@ -146,7 +146,7 @@ const BrewRenderer = React.createClass({
</div> </div>
{this.renderPageInfo()} {this.renderPageInfo()}
{this.renderPPRmsg()} {this.renderPPRmsg()}
</div> </div>;
} }
}); });

View File

@@ -1,9 +1,9 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var ErrorBar = React.createClass({ const ErrorBar = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
errors : [] errors : []
}; };
@@ -19,20 +19,20 @@ var ErrorBar = React.createClass({
this.hasMatchError = false; this.hasMatchError = false;
var errors = _.map(this.props.errors, (err, idx) => { const errors = _.map(this.props.errors, (err, idx)=>{
if(err.id == 'OPEN') this.hasOpenError = true; if(err.id == 'OPEN') this.hasOpenError = true;
if(err.id == 'CLOSE') this.hasCloseError = true; if(err.id == 'CLOSE') this.hasCloseError = true;
if(err.id == 'MISMATCH') this.hasMatchError = true; if(err.id == 'MISMATCH') this.hasMatchError = true;
return <li key={idx}> return <li key={idx}>
Line {err.line} : {err.text}, '{err.type}' tag Line {err.line} : {err.text}, '{err.type}' tag
</li> </li>;
}); });
return <ul>{errors}</ul> return <ul>{errors}</ul>;
}, },
renderProtip : function(){ renderProtip : function(){
var msg = []; const msg = [];
if(this.hasOpenError){ if(this.hasOpenError){
msg.push(<div> 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! 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!
@@ -53,7 +53,7 @@ var ErrorBar = React.createClass({
return <div className='protips'> return <div className='protips'>
<h4>Protips!</h4> <h4>Protips!</h4>
{msg} {msg}
</div> </div>;
}, },
render : function(){ render : function(){
@@ -66,7 +66,7 @@ var ErrorBar = React.createClass({
{this.renderErrors()} {this.renderErrors()}
<hr /> <hr />
{this.renderProtip()} {this.renderProtip()}
</div> </div>;
} }
}); });

View File

@@ -14,7 +14,7 @@ const splice = function(str, index, inject){
const SNIPPETBAR_HEIGHT = 25; const SNIPPETBAR_HEIGHT = 25;
const Editor = React.createClass({ const Editor = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
value : '', value : '',
onChange : ()=>{}, onChange : ()=>{},
@@ -23,9 +23,9 @@ const Editor = React.createClass({
onMetadataChange : ()=>{}, onMetadataChange : ()=>{},
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
showMetadataEditor: false showMetadataEditor : false
}; };
}, },
cursorPosition : { cursorPosition : {
@@ -33,13 +33,13 @@ const Editor = React.createClass({
ch : 0 ch : 0
}, },
componentDidMount: function() { componentDidMount : function() {
this.updateEditorSize(); this.updateEditorSize();
this.highlightPageLines(); this.highlightPageLines();
window.addEventListener("resize", this.updateEditorSize); window.addEventListener('resize', this.updateEditorSize);
}, },
componentWillUnmount: function() { componentWillUnmount : function() {
window.removeEventListener("resize", this.updateEditorSize); window.removeEventListener('resize', this.updateEditorSize);
}, },
updateEditorSize : function() { updateEditorSize : function() {
@@ -64,12 +64,12 @@ const Editor = React.createClass({
handgleToggle : function(){ handgleToggle : function(){
this.setState({ this.setState({
showMetadataEditor : !this.state.showMetadataEditor showMetadataEditor : !this.state.showMetadataEditor
}) });
}, },
getCurrentPage : function(){ getCurrentPage : function(){
const lines = this.props.value.split('\n').slice(0, this.cursorPosition.line + 1); const lines = this.props.value.split('\n').slice(0, this.cursorPosition.line + 1);
return _.reduce(lines, (r, line) => { return _.reduce(lines, (r, line)=>{
if(line.indexOf('\\page') !== -1) r++; if(line.indexOf('\\page') !== -1) r++;
return r; return r;
}, 1); }, 1);
@@ -86,13 +86,13 @@ const Editor = React.createClass({
} }
return r; return r;
}, []); }, []);
return lineNumbers return lineNumbers;
}, },
brewJump : function(){ brewJump : function(){
const currentPage = this.getCurrentPage(); const currentPage = this.getCurrentPage();
window.location.hash = 'p' + currentPage; window.location.hash = `p${currentPage}`;
}, },
//Called when there are changes to the editor's dimensions //Called when there are changes to the editor's dimensions
@@ -105,12 +105,12 @@ const Editor = React.createClass({
return <MetadataEditor return <MetadataEditor
metadata={this.props.metadata} metadata={this.props.metadata}
onChange={this.props.onMetadataChange} onChange={this.props.onMetadataChange}
/> />;
}, },
render : function(){ render : function(){
this.highlightPageLines(); this.highlightPageLines();
return( return (
<div className='editor' ref='main'> <div className='editor' ref='main'>
<SnippetBar <SnippetBar
brew={this.props.value} brew={this.props.value}

View File

@@ -1,14 +1,14 @@
const React = require('react'); const React = require('react');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const request = require("superagent"); const request = require('superagent');
const SYSTEMS = ['5e', '4e', '3.5e', 'Pathfinder'] const SYSTEMS = ['5e', '4e', '3.5e', 'Pathfinder'];
const MetadataEditor = React.createClass({ const MetadataEditor = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
metadata: { metadata : {
editId : null, editId : null,
title : '', title : '',
description : '', description : '',
@@ -24,12 +24,12 @@ const MetadataEditor = React.createClass({
handleFieldChange : function(name, e){ handleFieldChange : function(name, e){
this.props.onChange(_.merge({}, this.props.metadata, { this.props.onChange(_.merge({}, this.props.metadata, {
[name] : e.target.value [name] : e.target.value
})) }));
}, },
handleSystem : function(system, e){ handleSystem : function(system, e){
if(e.target.checked){ if(e.target.checked){
this.props.metadata.systems.push(system); this.props.metadata.systems.push(system);
}else{ } else {
this.props.metadata.systems = _.without(this.props.metadata.systems, system); this.props.metadata.systems = _.without(this.props.metadata.systems, system);
} }
this.props.onChange(this.props.metadata); this.props.onChange(this.props.metadata);
@@ -41,10 +41,10 @@ const MetadataEditor = React.createClass({
}, },
handleDelete : function(){ handleDelete : function(){
if(!confirm("are you sure you want to delete this brew?")) return; if(!confirm('are you sure you want to delete this brew?')) return;
if(!confirm("are you REALLY sure? You will not be able to recover it")) return; if(!confirm('are you REALLY sure? You will not be able to recover it')) return;
request.get('/api/remove/' + this.props.metadata.editId) request.get(`/api/remove/${this.props.metadata.editId}`)
.send() .send()
.end(function(err, res){ .end(function(err, res){
window.location.href = '/'; window.location.href = '/';
@@ -67,21 +67,21 @@ const MetadataEditor = React.createClass({
<input <input
type='checkbox' type='checkbox'
checked={_.includes(this.props.metadata.systems, val)} checked={_.includes(this.props.metadata.systems, val)}
onChange={this.handleSystem.bind(null, val)} /> onChange={()=>this.handleSystem(val)} />
{val} {val}
</label> </label>;
}); });
}, },
renderPublish : function(){ renderPublish : function(){
if(this.props.metadata.published){ if(this.props.metadata.published){
return <button className='unpublish' onClick={this.handlePublish.bind(null, false)}> return <button className='unpublish' onClick={()=>this.handlePublish(false)}>
<i className='fa fa-ban' /> unpublish <i className='fa fa-ban' /> unpublish
</button> </button>;
}else{ } else {
return <button className='publish' onClick={this.handlePublish.bind(null, true)}> return <button className='publish' onClick={()=>this.handlePublish(true)}>
<i className='fa fa-globe' /> publish <i className='fa fa-globe' /> publish
</button> </button>;
} }
}, },
@@ -95,7 +95,7 @@ const MetadataEditor = React.createClass({
<i className='fa fa-trash' /> delete brew <i className='fa fa-trash' /> delete brew
</button> </button>
</div> </div>
</div> </div>;
}, },
renderAuthors : function(){ renderAuthors : function(){
@@ -108,7 +108,7 @@ const MetadataEditor = React.createClass({
<div className='value'> <div className='value'>
{text} {text}
</div> </div>
</div> </div>;
}, },
renderShareToReddit : function(){ renderShareToReddit : function(){
@@ -123,7 +123,7 @@ const MetadataEditor = React.createClass({
</button> </button>
</a> </a>
</div> </div>
</div> </div>;
}, },
render : function(){ render : function(){
@@ -132,18 +132,18 @@ const MetadataEditor = React.createClass({
<label>title</label> <label>title</label>
<input type='text' className='value' <input type='text' className='value'
value={this.props.metadata.title} value={this.props.metadata.title}
onChange={this.handleFieldChange.bind(null, 'title')} /> onChange={()=>this.handleFieldChange('title')} />
</div> </div>
<div className='field description'> <div className='field description'>
<label>description</label> <label>description</label>
<textarea value={this.props.metadata.description} className='value' <textarea value={this.props.metadata.description} className='value'
onChange={this.handleFieldChange.bind(null, 'description')} /> onChange={()=>this.handleFieldChange('description')} />
</div> </div>
{/*} {/*}
<div className='field tags'> <div className='field tags'>
<label>tags</label> <label>tags</label>
<textarea value={this.props.metadata.tags} <textarea value={this.props.metadata.tags}
onChange={this.handleFieldChange.bind(null, 'tags')} /> onChange={()=>this.handleFieldChange('tags')} />
</div> </div>
*/} */}
@@ -168,7 +168,7 @@ const MetadataEditor = React.createClass({
{this.renderDelete()} {this.renderDelete()}
</div> </div>;
} }
}); });

View File

@@ -8,12 +8,12 @@ const Snippets = require('./snippets/snippets.js');
const execute = function(val, brew){ const execute = function(val, brew){
if(_.isFunction(val)) return val(brew); if(_.isFunction(val)) return val(brew);
return val; return val;
} };
const Snippetbar = React.createClass({ const Snippetbar = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
brew : '', brew : '',
onInject : ()=>{}, onInject : ()=>{},
@@ -23,7 +23,7 @@ const Snippetbar = React.createClass({
}, },
handleSnippetClick : function(injectedText){ handleSnippetClick : function(injectedText){
this.props.onInject(injectedText) this.props.onInject(injectedText);
}, },
renderSnippetGroups : function(){ renderSnippetGroups : function(){
@@ -35,18 +35,18 @@ const Snippetbar = React.createClass({
snippets={snippetGroup.snippets} snippets={snippetGroup.snippets}
key={snippetGroup.groupName} key={snippetGroup.groupName}
onSnippetClick={this.handleSnippetClick} onSnippetClick={this.handleSnippetClick}
/> />;
}) });
}, },
render : function(){ render : function(){
return <div className='snippetBar'> return <div className='snippetBar'>
{this.renderSnippetGroups()} {this.renderSnippetGroups()}
<div className={cx('toggleMeta', {selected: this.props.showmeta})} <div className={cx('toggleMeta', { selected: this.props.showmeta })}
onClick={this.props.onToggle}> onClick={this.props.onToggle}>
<i className='fa fa-bars' /> <i className='fa fa-bars' />
</div> </div>
</div> </div>;
} }
}); });
@@ -58,7 +58,7 @@ module.exports = Snippetbar;
const SnippetGroup = React.createClass({ const SnippetGroup = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
brew : '', brew : '',
groupName : '', groupName : '',
@@ -72,23 +72,23 @@ const SnippetGroup = React.createClass({
}, },
renderSnippets : function(){ renderSnippets : function(){
return _.map(this.props.snippets, (snippet)=>{ return _.map(this.props.snippets, (snippet)=>{
return <div className='snippet' key={snippet.name} onClick={this.handleSnippetClick.bind(this, snippet)}> return <div className='snippet' key={snippet.name} onClick={()=>this.handleSnippetClick(snippet)}>
<i className={'fa fa-fw ' + snippet.icon} /> <i className={`fa fa-fw ${snippet.icon}`} />
{snippet.name} {snippet.name}
</div> </div>;
}) });
}, },
render : function(){ render : function(){
return <div className='snippetGroup'> return <div className='snippetGroup'>
<div className='text'> <div className='text'>
<i className={'fa fa-fw ' + this.props.icon} /> <i className={`fa fa-fw ${this.props.icon}`} />
<span className='groupName'>{this.props.groupName}</span> <span className='groupName'>{this.props.groupName}</span>
</div> </div>
<div className='dropdown'> <div className='dropdown'>
{this.renderSnippets()} {this.renderSnippets()}
</div> </div>
</div> </div>;
}, },
}); });

View File

@@ -1,42 +1,42 @@
var _ = require('lodash'); const _ = require('lodash');
module.exports = function(classname){ module.exports = function(classname){
classname = _.sample(['archivist', 'fancyman', 'linguist', 'fletcher', classname = _.sample(['archivist', 'fancyman', 'linguist', 'fletcher',
'notary', 'berserker-typist', 'fishmongerer', 'manicurist', 'haberdasher', 'concierge']) 'notary', 'berserker-typist', 'fishmongerer', 'manicurist', 'haberdasher', 'concierge']);
classname = classname.toLowerCase(); classname = classname.toLowerCase();
var hitDie = _.sample([4, 6, 8, 10, 12]); const hitDie = _.sample([4, 6, 8, 10, 12]);
var abilityList = ["Strength", "Dexerity", "Constitution", "Wisdom", "Charisma", "Intelligence"]; const abilityList = ['Strength', 'Dexerity', 'Constitution', 'Wisdom', 'Charisma', 'Intelligence'];
var skillList = ["Acrobatics ", "Animal Handling", "Arcana", "Athletics", "Deception", "History", "Insight", "Intimidation", "Investigation", "Medicine", "Nature", "Perception", "Performance", "Persuasion", "Religion", "Sleight of Hand", "Stealth", "Survival"]; const skillList = ['Acrobatics ', 'Animal Handling', 'Arcana', 'Athletics', 'Deception', 'History', 'Insight', 'Intimidation', 'Investigation', 'Medicine', 'Nature', 'Perception', 'Performance', 'Persuasion', 'Religion', 'Sleight of Hand', 'Stealth', 'Survival'];
return [ return [
"## Class Features", '## Class Features',
"As a " + classname + ", you gain the following class features", `As a ${classname}, you gain the following class features`,
"#### Hit Points", '#### Hit Points',
"___", '___',
"- **Hit Dice:** 1d" + hitDie + " per " + classname + " level", `- **Hit Dice:** 1d${hitDie} per ${classname} level`,
"- **Hit Points at 1st Level:** " + hitDie + " + your Constitution modifier", `- **Hit Points at 1st Level:** ${hitDie} + your Constitution modifier`,
"- **Hit Points at Higher Levels:** 1d" + hitDie + " (or " + (hitDie/2 + 1) + ") + your Constitution modifier per " + classname + " level after 1st", `- **Hit Points at Higher Levels:** 1d${hitDie} (or ${hitDie/2 + 1}) + your Constitution modifier per ${classname} level after 1st`,
"", '',
"#### Proficiencies", '#### Proficiencies',
"___", '___',
"- **Armor:** " + (_.sampleSize(["Light armor", "Medium armor", "Heavy armor", "Shields"], _.random(0,3)).join(', ') || "None"), `- **Armor:** ${_.sampleSize(['Light armor', 'Medium armor', 'Heavy armor', 'Shields'], _.random(0, 3)).join(', ') || 'None'}`,
"- **Weapons:** " + (_.sampleSize(["Squeegee", "Rubber Chicken", "Simple weapons", "Martial weapons"], _.random(0,2)).join(', ') || "None"), `- **Weapons:** ${_.sampleSize(['Squeegee', 'Rubber Chicken', 'Simple weapons', 'Martial weapons'], _.random(0, 2)).join(', ') || 'None'}`,
"- **Tools:** " + (_.sampleSize(["Artian's tools", "one musical instrument", "Thieve's tools"], _.random(0,2)).join(', ') || "None"), `- **Tools:** ${_.sampleSize(['Artian\'s tools', 'one musical instrument', 'Thieve\'s tools'], _.random(0, 2)).join(', ') || 'None'}`,
"", '',
"___", '___',
"- **Saving Throws:** " + (_.sampleSize(abilityList, 2).join(', ')), `- **Saving Throws:** ${_.sampleSize(abilityList, 2).join(', ')}`,
"- **Skills:** Choose two from " + (_.sampleSize(skillList, _.random(4, 6)).join(', ')), `- **Skills:** Choose two from ${_.sampleSize(skillList, _.random(4, 6)).join(', ')}`,
"", '',
"#### Equipment", '#### Equipment',
"You start with the following equipment, in addition to the equipment granted by your background:", 'You start with the following equipment, in addition to the equipment granted by your background:',
"- *(a)* a martial weapon and a shield or *(b)* two martial weapons", '- *(a)* a martial weapon and a shield or *(b)* two martial weapons',
"- *(a)* five javelins or *(b)* any simple melee weapon", '- *(a)* five javelins or *(b)* any simple melee weapon',
"- " + (_.sample(["10 lint fluffs", "1 button", "a cherished lost sock"])), `- ${_.sample(['10 lint fluffs', '1 button', 'a cherished lost sock'])}`,
"\n\n\n" '\n\n\n'
].join('\n'); ].join('\n');
} };

View File

@@ -1,114 +1,114 @@
var _ = require('lodash'); const _ = require('lodash');
var features = [ const features = [
"Astrological Botany", 'Astrological Botany',
"Astrological Chemistry", 'Astrological Chemistry',
"Biochemical Sorcery", 'Biochemical Sorcery',
"Civil Alchemy", 'Civil Alchemy',
"Consecrated Biochemistry", 'Consecrated Biochemistry',
"Demonic Anthropology", 'Demonic Anthropology',
"Divinatory Mineralogy", 'Divinatory Mineralogy',
"Genetic Banishing", 'Genetic Banishing',
"Hermetic Geography", 'Hermetic Geography',
"Immunological Incantations", 'Immunological Incantations',
"Nuclear Illusionism", 'Nuclear Illusionism',
"Ritual Astronomy", 'Ritual Astronomy',
"Seismological Divination", 'Seismological Divination',
"Spiritual Biochemistry", 'Spiritual Biochemistry',
"Statistical Occultism", 'Statistical Occultism',
"Police Necromancer", 'Police Necromancer',
"Sixgun Poisoner", 'Sixgun Poisoner',
"Pharmaceutical Gunslinger", 'Pharmaceutical Gunslinger',
"Infernal Banker", 'Infernal Banker',
"Spell Analyst", 'Spell Analyst',
"Gunslinger Corruptor", 'Gunslinger Corruptor',
"Torque Interfacer", 'Torque Interfacer',
"Exo Interfacer", 'Exo Interfacer',
"Gunpowder Torturer", 'Gunpowder Torturer',
"Orbital Gravedigger", 'Orbital Gravedigger',
"Phased Linguist", 'Phased Linguist',
"Mathematical Pharmacist", 'Mathematical Pharmacist',
"Plasma Outlaw", 'Plasma Outlaw',
"Malefic Chemist", 'Malefic Chemist',
"Police Cultist" 'Police Cultist'
]; ];
var classnames = ['Archivist', 'Fancyman', 'Linguist', 'Fletcher', const classnames = ['Archivist', 'Fancyman', 'Linguist', 'Fletcher',
'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge']; 'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge'];
var levels = ["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", "20th"] const levels = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th', '8th', '9th', '10th', '11th', '12th', '13th', '14th', '15th', '16th', '17th', '18th', '19th', '20th'];
var profBonus = [2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6]; const profBonus = [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6];
var getFeature = (level)=>{ const getFeature = (level)=>{
var res = [] let res = [];
if(_.includes([4,6,8,12,14,16,19], level+1)){ if(_.includes([4, 6, 8, 12, 14, 16, 19], level+1)){
res = ["Ability Score Improvement"] res = ['Ability Score Improvement'];
} }
res = _.union(res, _.sampleSize(features, _.sample([0,1,1,1,1,1]))); res = _.union(res, _.sampleSize(features, _.sample([0, 1, 1, 1, 1, 1])));
if(!res.length) return "─"; if(!res.length) return '─';
return res.join(', '); return res.join(', ');
} };
module.exports = { module.exports = {
full : function(){ full : function(){
var classname = _.sample(classnames) const classname = _.sample(classnames);
var maxes = [4,3,3,3,3,2,2,1,1] const maxes = [4, 3, 3, 3, 3, 2, 2, 1, 1];
var drawSlots = function(Slots){ const drawSlots = function(Slots){
var slots = Number(Slots); let slots = Number(Slots);
return _.times(9, function(i){ return _.times(9, function(i){
var max = maxes[i]; const max = maxes[i];
if(slots < 1) return "—"; if(slots < 1) return '—';
var res = _.min([max, slots]); const res = _.min([max, slots]);
slots -= res; slots -= res;
return res; return res;
}).join(' | ') }).join(' | ');
} };
var cantrips = 3; let cantrips = 3;
var spells = 1; let spells = 1;
var slots = 2; let slots = 2;
return "<div class='classTable wide'>\n##### The " + classname + "\n" + return `<div class='classTable wide'>\n##### The ${classname}\n` +
"| Level | Proficiency Bonus | Features | Cantrips Known | Spells Known | 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th |\n"+ `| Level | Proficiency Bonus | Features | Cantrips Known | Spells Known | 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th |\n`+
"|:---:|:---:|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n" + `|:---:|:---:|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n${
_.map(levels, function(levelName, level){ _.map(levels, function(levelName, level){
var res = [ const res = [
levelName, levelName,
"+" + profBonus[level], `+${profBonus[level]}`,
getFeature(level), getFeature(level),
cantrips, cantrips,
spells, spells,
drawSlots(slots) drawSlots(slots)
].join(' | '); ].join(' | ');
cantrips += _.random(0,1); cantrips += _.random(0, 1);
spells += _.random(0,1); spells += _.random(0, 1);
slots += _.random(0,2); slots += _.random(0, 2);
return "| " + res + " |"; return `| ${res} |`;
}).join('\n') +'\n</div>\n\n'; }).join('\n')}\n</div>\n\n`;
}, },
half : function(){ half : function(){
var classname = _.sample(classnames) const classname = _.sample(classnames);
var featureScore = 1 let featureScore = 1;
return "<div class='classTable'>\n##### The " + classname + "\n" + return `<div class='classTable'>\n##### The ${classname}\n` +
"| Level | Proficiency Bonus | Features | " + _.sample(features) + "|\n" + `| Level | Proficiency Bonus | Features | ${_.sample(features)}|\n` +
"|:---:|:---:|:---|:---:|\n" + `|:---:|:---:|:---|:---:|\n${
_.map(levels, function(levelName, level){ _.map(levels, function(levelName, level){
var res = [ const res = [
levelName, levelName,
"+" + profBonus[level], `+${profBonus[level]}`,
getFeature(level), getFeature(level),
"+" + featureScore `+${featureScore}`
].join(' | '); ].join(' | ');
featureScore += _.random(0,1); featureScore += _.random(0, 1);
return "| " + res + " |"; return `| ${res} |`;
}).join('\n') +'\n</div>\n\n'; }).join('\n')}\n</div>\n\n`;
} }
}; };

View File

@@ -1,104 +1,104 @@
var _ = require('lodash'); const _ = require('lodash');
var titles = [ const titles = [
"The Burning Gallows", 'The Burning Gallows',
"The Ring of Nenlast", 'The Ring of Nenlast',
"Below the Blind Tavern", 'Below the Blind Tavern',
"Below the Hungering River", 'Below the Hungering River',
"Before Bahamut's Land", 'Before Bahamut\'s Land',
"The Cruel Grave from Within", 'The Cruel Grave from Within',
"The Strength of Trade Road", 'The Strength of Trade Road',
"Through The Raven Queen's Worlds", 'Through The Raven Queen\'s Worlds',
"Within the Settlement", 'Within the Settlement',
"The Crown from Within", 'The Crown from Within',
"The Merchant Within the Battlefield", 'The Merchant Within the Battlefield',
"Ioun's Fading Traveler", 'Ioun\'s Fading Traveler',
"The Legion Ingredient", 'The Legion Ingredient',
"The Explorer Lure", 'The Explorer Lure',
"Before the Charming Badlands", 'Before the Charming Badlands',
"The Living Dead Above the Fearful Cage", 'The Living Dead Above the Fearful Cage',
"Vecna's Hidden Sage", 'Vecna\'s Hidden Sage',
"Bahamut's Demonspawn", 'Bahamut\'s Demonspawn',
"Across Gruumsh's Elemental Chaos", 'Across Gruumsh\'s Elemental Chaos',
"The Blade of Orcus", 'The Blade of Orcus',
"Beyond Revenge", 'Beyond Revenge',
"Brain of Insanity", 'Brain of Insanity',
"Breed Battle!, A New Beginning", 'Breed Battle!, A New Beginning',
"Evil Lake, A New Beginning", 'Evil Lake, A New Beginning',
"Invasion of the Gigantic Cat, Part II", 'Invasion of the Gigantic Cat, Part II',
"Kraken War 2020", 'Kraken War 2020',
"The Body Whisperers", 'The Body Whisperers',
"The Diabolical Tales of the Ape-Women", 'The Diabolical Tales of the Ape-Women',
"The Doctor Immortal", 'The Doctor Immortal',
"The Doctor from Heaven", 'The Doctor from Heaven',
"The Graveyard", 'The Graveyard',
"Azure Core", 'Azure Core',
"Core Battle", 'Core Battle',
"Core of Heaven: The Guardian of Amazement", 'Core of Heaven: The Guardian of Amazement',
"Deadly Amazement III", 'Deadly Amazement III',
"Dry Chaos IX", 'Dry Chaos IX',
"Gate Thunder", 'Gate Thunder',
"Guardian: Skies of the Dark Wizard", 'Guardian: Skies of the Dark Wizard',
"Lute of Eternity", 'Lute of Eternity',
"Mercury's Planet: Brave Evolution", 'Mercury\'s Planet: Brave Evolution',
"Ruby of Atlantis: The Quake of Peace", 'Ruby of Atlantis: The Quake of Peace',
"Sky of Zelda: The Thunder of Force", 'Sky of Zelda: The Thunder of Force',
"Vyse's Skies", 'Vyse\'s Skies',
"White Greatness III", 'White Greatness III',
"Yellow Divinity", 'Yellow Divinity',
"Zidane's Ghost" 'Zidane\'s Ghost'
]; ];
var subtitles = [ const subtitles = [
"In an ominous universe, a botanist opposes terrorism.", 'In an ominous universe, a botanist opposes terrorism.',
"In a demon-haunted city, in an age of lies and hate, a physicist tries to find an ancient treasure and battles a mob of aliens.", 'In a demon-haunted city, in an age of lies and hate, a physicist tries to find an ancient treasure and battles a mob of aliens.',
"In a land of corruption, two cyberneticists and a dungeon delver search for freedom.", 'In a land of corruption, two cyberneticists and a dungeon delver search for freedom.',
"In an evil empire of horror, two rangers battle the forces of hell.", 'In an evil empire of horror, two rangers battle the forces of hell.',
"In a lost city, in an age of sorcery, a librarian quests for revenge.", 'In a lost city, in an age of sorcery, a librarian quests for revenge.',
"In a universe of illusions and danger, three time travellers and an adventurer search for justice.", 'In a universe of illusions and danger, three time travellers and an adventurer search for justice.',
"In a forgotten universe of barbarism, in an era of terror and mysticism, a virtual reality programmer and a spy try to find vengance and battle crime.", 'In a forgotten universe of barbarism, in an era of terror and mysticism, a virtual reality programmer and a spy try to find vengance and battle crime.',
"In a universe of demons, in an era of insanity and ghosts, three bodyguards and a bodyguard try to find vengance.", 'In a universe of demons, in an era of insanity and ghosts, three bodyguards and a bodyguard try to find vengance.',
"In a kingdom of corruption and battle, seven artificial intelligences try to save the last living fertile woman.", 'In a kingdom of corruption and battle, seven artificial intelligences try to save the last living fertile woman.',
"In a universe of virutal reality and agony, in an age of ghosts and ghosts, a fortune-teller and a wanderer try to avert the apocalypse.", 'In a universe of virutal reality and agony, in an age of ghosts and ghosts, a fortune-teller and a wanderer try to avert the apocalypse.',
"In a crime-infested kingdom, three martial artists quest for the truth and oppose evil.", 'In a crime-infested kingdom, three martial artists quest for the truth and oppose evil.',
"In a terrifying universe of lost souls, in an era of lost souls, eight dancers fight evil.", 'In a terrifying universe of lost souls, in an era of lost souls, eight dancers fight evil.',
"In a galaxy of confusion and insanity, three martial artists and a duke battle a mob of psychics.", 'In a galaxy of confusion and insanity, three martial artists and a duke battle a mob of psychics.',
"In an amazing kingdom, a wizard and a secretary hope to prevent the destruction of mankind.", 'In an amazing kingdom, a wizard and a secretary hope to prevent the destruction of mankind.',
"In a kingdom of deception, a reporter searches for fame.", 'In a kingdom of deception, a reporter searches for fame.',
"In a hellish empire, a swordswoman and a duke try to find the ultimate weapon and battle a conspiracy.", 'In a hellish empire, a swordswoman and a duke try to find the ultimate weapon and battle a conspiracy.',
"In an evil galaxy of illusion, in a time of technology and misery, seven psychiatrists battle crime.", 'In an evil galaxy of illusion, in a time of technology and misery, seven psychiatrists battle crime.',
"In a dark city of confusion, three swordswomen and a singer battle lawlessness.", 'In a dark city of confusion, three swordswomen and a singer battle lawlessness.',
"In an ominous empire, in an age of hate, two philosophers and a student try to find justice and battle a mob of mages intent on stealing the souls of the innocent.", 'In an ominous empire, in an age of hate, two philosophers and a student try to find justice and battle a mob of mages intent on stealing the souls of the innocent.',
"In a kingdom of panic, six adventurers oppose lawlessness.", 'In a kingdom of panic, six adventurers oppose lawlessness.',
"In a land of dreams and hopelessness, three hackers and a cyborg search for justice.", 'In a land of dreams and hopelessness, three hackers and a cyborg search for justice.',
"On a planet of mysticism, three travelers and a fire fighter quest for the ultimate weapon and oppose evil.", 'On a planet of mysticism, three travelers and a fire fighter quest for the ultimate weapon and oppose evil.',
"In a wicked universe, five seers fight lawlessness.", 'In a wicked universe, five seers fight lawlessness.',
"In a kingdom of death, in an era of illusion and blood, four colonists search for fame.", 'In a kingdom of death, in an era of illusion and blood, four colonists search for fame.',
"In an amazing kingdom, in an age of sorcery and lost souls, eight space pirates quest for freedom.", 'In an amazing kingdom, in an age of sorcery and lost souls, eight space pirates quest for freedom.',
"In a cursed empire, five inventors oppose terrorism.", 'In a cursed empire, five inventors oppose terrorism.',
"On a crime-ridden planet of conspiracy, a watchman and an artificial intelligence try to find love and oppose lawlessness.", 'On a crime-ridden planet of conspiracy, a watchman and an artificial intelligence try to find love and oppose lawlessness.',
"In a forgotten land, a reporter and a spy try to stop the apocalypse.", 'In a forgotten land, a reporter and a spy try to stop the apocalypse.',
"In a forbidden land of prophecy, a scientist and an archivist oppose a cabal of barbarians intent on stealing the souls of the innocent.", 'In a forbidden land of prophecy, a scientist and an archivist oppose a cabal of barbarians intent on stealing the souls of the innocent.',
"On an infernal world of illusion, a grave robber and a watchman try to find revenge and combat a syndicate of mages intent on stealing the source of all magic.", 'On an infernal world of illusion, a grave robber and a watchman try to find revenge and combat a syndicate of mages intent on stealing the source of all magic.',
"In a galaxy of dark magic, four fighters seek freedom.", 'In a galaxy of dark magic, four fighters seek freedom.',
"In an empire of deception, six tomb-robbers quest for the ultimate weapon and combat an army of raiders.", 'In an empire of deception, six tomb-robbers quest for the ultimate weapon and combat an army of raiders.',
"In a kingdom of corruption and lost souls, in an age of panic, eight planetologists oppose evil.", 'In a kingdom of corruption and lost souls, in an age of panic, eight planetologists oppose evil.',
"In a galaxy of misery and hopelessness, in a time of agony and pain, five planetologists search for vengance.", 'In a galaxy of misery and hopelessness, in a time of agony and pain, five planetologists search for vengance.',
"In a universe of technology and insanity, in a time of sorcery, a computer techician quests for hope.", 'In a universe of technology and insanity, in a time of sorcery, a computer techician quests for hope.',
"On a planet of dark magic and barbarism, in an age of horror and blasphemy, seven librarians search for fame.", 'On a planet of dark magic and barbarism, in an age of horror and blasphemy, seven librarians search for fame.',
"In an empire of dark magic, in a time of blood and illusions, four monks try to find the ultimate weapon and combat terrorism.", 'In an empire of dark magic, in a time of blood and illusions, four monks try to find the ultimate weapon and combat terrorism.',
"In a forgotten empire of dark magic, six kings try to prevent the destruction of mankind.", 'In a forgotten empire of dark magic, six kings try to prevent the destruction of mankind.',
"In a galaxy of dark magic and horror, in an age of hopelessness, four marines and an outlaw combat evil.", 'In a galaxy of dark magic and horror, in an age of hopelessness, four marines and an outlaw combat evil.',
"In a mysterious city of illusion, in an age of computerization, a witch-hunter tries to find the ultimate weapon and opposes an evil corporation.", 'In a mysterious city of illusion, in an age of computerization, a witch-hunter tries to find the ultimate weapon and opposes an evil corporation.',
"In a damned kingdom of technology, a virtual reality programmer and a fighter seek fame.", 'In a damned kingdom of technology, a virtual reality programmer and a fighter seek fame.',
"In a hellish kingdom, in an age of blasphemy and blasphemy, an astrologer searches for fame.", 'In a hellish kingdom, in an age of blasphemy and blasphemy, an astrologer searches for fame.',
"In a damned world of devils, an alien and a ranger quest for love and oppose a syndicate of demons.", 'In a damned world of devils, an alien and a ranger quest for love and oppose a syndicate of demons.',
"In a cursed galaxy, in a time of pain, seven librarians hope to avert the apocalypse.", 'In a cursed galaxy, in a time of pain, seven librarians hope to avert the apocalypse.',
"In a crime-infested galaxy, in an era of hopelessness and panic, three champions and a grave robber try to solve the ultimate crime." 'In a crime-infested galaxy, in an era of hopelessness and panic, three champions and a grave robber try to solve the ultimate crime.'
]; ];
module.exports = () => { module.exports = ()=>{
return `<style> return `<style>
.phb#p1{ text-align:center; } .phb#p1{ text-align:center; }
.phb#p1:after{ display:none; } .phb#p1:after{ display:none; }
@@ -113,5 +113,5 @@ module.exports = () => {
##### ${_.sample(subtitles)} ##### ${_.sample(subtitles)}
</div> </div>
\\page` \\page`;
} };

View File

@@ -1,43 +1,43 @@
var _ = require('lodash'); const _ = require('lodash');
var ClassFeatureGen = require('./classfeature.gen.js'); const ClassFeatureGen = require('./classfeature.gen.js');
var ClassTableGen = require('./classtable.gen.js'); const ClassTableGen = require('./classtable.gen.js');
module.exports = function(){ module.exports = function(){
var classname = _.sample(['Archivist', 'Fancyman', 'Linguist', 'Fletcher', const classname = _.sample(['Archivist', 'Fancyman', 'Linguist', 'Fletcher',
'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge']) 'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge']);
var image = _.sample(_.map([ const image = _.sample(_.map([
"http://orig01.deviantart.net/4682/f/2007/099/f/c/bard_stick_figure_by_wrpigeek.png", 'http://orig01.deviantart.net/4682/f/2007/099/f/c/bard_stick_figure_by_wrpigeek.png',
"http://img07.deviantart.net/a3c9/i/2007/099/3/a/archer_stick_figure_by_wrpigeek.png", 'http://img07.deviantart.net/a3c9/i/2007/099/3/a/archer_stick_figure_by_wrpigeek.png',
"http://pre04.deviantart.net/d596/th/pre/f/2007/099/5/2/adventurer_stick_figure_by_wrpigeek.png", 'http://pre04.deviantart.net/d596/th/pre/f/2007/099/5/2/adventurer_stick_figure_by_wrpigeek.png',
"http://img13.deviantart.net/d501/i/2007/099/d/4/black_mage_stick_figure_by_wrpigeek.png", 'http://img13.deviantart.net/d501/i/2007/099/d/4/black_mage_stick_figure_by_wrpigeek.png',
"http://img09.deviantart.net/5cf3/i/2007/099/d/d/dark_knight_stick_figure_by_wrpigeek.png", 'http://img09.deviantart.net/5cf3/i/2007/099/d/d/dark_knight_stick_figure_by_wrpigeek.png',
"http://pre01.deviantart.net/7a34/th/pre/f/2007/099/6/3/monk_stick_figure_by_wrpigeek.png", 'http://pre01.deviantart.net/7a34/th/pre/f/2007/099/6/3/monk_stick_figure_by_wrpigeek.png',
"http://img11.deviantart.net/5dcc/i/2007/099/d/1/mystic_knight_stick_figure_by_wrpigeek.png", 'http://img11.deviantart.net/5dcc/i/2007/099/d/1/mystic_knight_stick_figure_by_wrpigeek.png',
"http://pre08.deviantart.net/ad45/th/pre/f/2007/099/a/0/thief_stick_figure_by_wrpigeek.png", 'http://pre08.deviantart.net/ad45/th/pre/f/2007/099/a/0/thief_stick_figure_by_wrpigeek.png',
], function(url){ ], function(url){
return "<img src = '" + url + "' style='max-width:8cm;max-height:25cm' />" return `<img src = '${url}' style='max-width:8cm;max-height:25cm' />`;
})) }));
return [ return `${[
image, image,
"", '',
"```", '```',
"```", '```',
"<div style='margin-top:240px'></div>\n\n", '<div style=\'margin-top:240px\'></div>\n\n',
"## " + classname, `## ${classname}`,
"Cool intro stuff will go here", 'Cool intro stuff will go here',
"\\page", '\\page',
ClassTableGen(classname), ClassTableGen(classname),
ClassFeatureGen(classname), ClassFeatureGen(classname),
].join('\n') + '\n\n\n'; ].join('\n')}\n\n\n`;
}; };

View File

@@ -1,60 +1,60 @@
var _ = require('lodash'); const _ = require('lodash');
var spellNames = [ const spellNames = [
"Astral Rite of Acne", 'Astral Rite of Acne',
"Create Acne", 'Create Acne',
"Cursed Ramen Erruption", 'Cursed Ramen Erruption',
"Dark Chant of the Dentists", 'Dark Chant of the Dentists',
"Erruption of Immaturity", 'Erruption of Immaturity',
"Flaming Disc of Inconvenience", 'Flaming Disc of Inconvenience',
"Heal Bad Hygene", 'Heal Bad Hygene',
"Heavenly Transfiguration of the Cream Devil", 'Heavenly Transfiguration of the Cream Devil',
"Hellish Cage of Mucus", 'Hellish Cage of Mucus',
"Irritate Peanut Butter Fairy", 'Irritate Peanut Butter Fairy',
"Luminous Erruption of Tea", 'Luminous Erruption of Tea',
"Mystic Spell of the Poser", 'Mystic Spell of the Poser',
"Sorcerous Enchantment of the Chimneysweep", 'Sorcerous Enchantment of the Chimneysweep',
"Steak Sauce Ray", 'Steak Sauce Ray',
"Talk to Groupie", 'Talk to Groupie',
"Astonishing Chant of Chocolate", 'Astonishing Chant of Chocolate',
"Astounding Pasta Puddle", 'Astounding Pasta Puddle',
"Ball of Annoyance", 'Ball of Annoyance',
"Cage of Yarn", 'Cage of Yarn',
"Control Noodles Elemental", 'Control Noodles Elemental',
"Create Nervousness", 'Create Nervousness',
"Cure Baldness", 'Cure Baldness',
"Cursed Ritual of Bad Hair", 'Cursed Ritual of Bad Hair',
"Dispell Piles in Dentist", 'Dispell Piles in Dentist',
"Eliminate Florists", 'Eliminate Florists',
"Illusionary Transfiguration of the Babysitter", 'Illusionary Transfiguration of the Babysitter',
"Necromantic Armor of Salad Dressing", 'Necromantic Armor of Salad Dressing',
"Occult Transfiguration of Foot Fetish", 'Occult Transfiguration of Foot Fetish',
"Protection from Mucus Giant", 'Protection from Mucus Giant',
"Tinsel Blast", 'Tinsel Blast',
"Alchemical Evocation of the Goths", 'Alchemical Evocation of the Goths',
"Call Fangirl", 'Call Fangirl',
"Divine Spell of Crossdressing", 'Divine Spell of Crossdressing',
"Dominate Ramen Giant", 'Dominate Ramen Giant',
"Eliminate Vindictiveness in Gym Teacher", 'Eliminate Vindictiveness in Gym Teacher',
"Extra-Planar Spell of Irritation", 'Extra-Planar Spell of Irritation',
"Induce Whining in Babysitter", 'Induce Whining in Babysitter',
"Invoke Complaining", 'Invoke Complaining',
"Magical Enchantment of Arrogance", 'Magical Enchantment of Arrogance',
"Occult Globe of Salad Dressing", 'Occult Globe of Salad Dressing',
"Overwhelming Enchantment of the Chocolate Fairy", 'Overwhelming Enchantment of the Chocolate Fairy',
"Sorcerous Dandruff Globe", 'Sorcerous Dandruff Globe',
"Spiritual Invocation of the Costumers", 'Spiritual Invocation of the Costumers',
"Ultimate Rite of the Confetti Angel", 'Ultimate Rite of the Confetti Angel',
"Ultimate Ritual of Mouthwash", 'Ultimate Ritual of Mouthwash',
]; ];
module.exports = { module.exports = {
spellList : function(){ spellList : function(){
var levels = ['Cantrips (0 Level)', '2nd Level', '3rd Level', '4th Level', '5th Level', '6th Level', '7th Level', '8th Level', '9th Level']; const levels = ['Cantrips (0 Level)', '2nd Level', '3rd Level', '4th Level', '5th Level', '6th Level', '7th Level', '8th Level', '9th Level'];
var content = _.map(levels, (level)=>{ const content = _.map(levels, (level)=>{
var spells = _.map(_.sampleSize(spellNames, _.random(5, 15)), (spell)=>{ const spells = _.map(_.sampleSize(spellNames, _.random(5, 15)), (spell)=>{
return `- ${spell}`; return `- ${spell}`;
}).join('\n'); }).join('\n');
return `##### ${level} \n${spells} \n`; return `##### ${level} \n${spells} \n`;
@@ -64,28 +64,28 @@ module.exports = {
}, },
spell : function(){ spell : function(){
var level = ["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th"]; const level = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th', '8th', '9th'];
var spellSchools = ["abjuration", "conjuration", "divination", "enchantment", "evocation", "illusion", "necromancy", "transmutation"]; const spellSchools = ['abjuration', 'conjuration', 'divination', 'enchantment', 'evocation', 'illusion', 'necromancy', 'transmutation'];
var components = _.sampleSize(["V", "S", "M"], _.random(1,3)).join(', '); let components = _.sampleSize(['V', 'S', 'M'], _.random(1, 3)).join(', ');
if(components.indexOf("M") !== -1){ if(components.indexOf('M') !== -1){
components += " (" + _.sampleSize(['a small doll', 'a crushed button worth at least 1cp', 'discarded gum wrapper'], _.random(1,3)).join(', ') + ")" components += ` (${_.sampleSize(['a small doll', 'a crushed button worth at least 1cp', 'discarded gum wrapper'], _.random(1, 3)).join(', ')})`;
} }
return [ return [
"#### " + _.sample(spellNames), `#### ${_.sample(spellNames)}`,
"*" + _.sample(level) + "-level " + _.sample(spellSchools) + "*", `*${_.sample(level)}-level ${_.sample(spellSchools)}*`,
"___", '___',
"- **Casting Time:** 1 action", '- **Casting Time:** 1 action',
"- **Range:** " + _.sample(["Self", "Touch", "30 feet", "60 feet"]), `- **Range:** ${_.sample(['Self', 'Touch', '30 feet', '60 feet'])}`,
"- **Components:** " + components, `- **Components:** ${components}`,
"- **Duration:** " + _.sample(["Until dispelled", "1 round", "Instantaneous", "Concentration, up to 10 minutes", "1 hour"]), `- **Duration:** ${_.sample(['Until dispelled', '1 round', 'Instantaneous', 'Concentration, up to 10 minutes', '1 hour'])}`,
"", '',
"A flame, equivalent in brightness to a torch, springs from from an object that you touch. ", 'A flame, equivalent in brightness to a torch, springs from from an object that you touch. ',
"The effect look like a regular flame, but it creates no heat and doesn't use oxygen. ", 'The effect look like a regular flame, but it creates no heat and doesn\'t use oxygen. ',
"A *continual flame* can be covered or hidden but not smothered or quenched.", 'A *continual flame* can be covered or hidden but not smothered or quenched.',
"\n\n\n" '\n\n\n'
].join('\n'); ].join('\n');
} }
} };

View File

@@ -1,196 +1,196 @@
var _ = require('lodash'); const _ = require('lodash');
var genList = function(list, max){ const genList = function(list, max){
return _.sampleSize(list, _.random(0,max)).join(', ') || "None"; return _.sampleSize(list, _.random(0, max)).join(', ') || 'None';
} };
var getMonsterName = function(){ const getMonsterName = function(){
return _.sample([ return _.sample([
"All-devouring Baseball Imp", 'All-devouring Baseball Imp',
"All-devouring Gumdrop Wraith", 'All-devouring Gumdrop Wraith',
"Chocolate Hydra", 'Chocolate Hydra',
"Devouring Peacock", 'Devouring Peacock',
"Economy-sized Colossus of the Lemonade Stand", 'Economy-sized Colossus of the Lemonade Stand',
"Ghost Pigeon", 'Ghost Pigeon',
"Gibbering Duck", 'Gibbering Duck',
"Sparklemuffin Peacock Spider", 'Sparklemuffin Peacock Spider',
"Gum Elemental", 'Gum Elemental',
"Illiterate Construct of the Candy Store", 'Illiterate Construct of the Candy Store',
"Ineffable Chihuahua", 'Ineffable Chihuahua',
"Irritating Death Hamster", 'Irritating Death Hamster',
"Irritating Gold Mouse", 'Irritating Gold Mouse',
"Juggernaut Snail", 'Juggernaut Snail',
"Juggernaut of the Sock Drawer", 'Juggernaut of the Sock Drawer',
"Koala of the Cosmos", 'Koala of the Cosmos',
"Mad Koala of the West", 'Mad Koala of the West',
"Milk Djinni of the Lemonade Stand", 'Milk Djinni of the Lemonade Stand',
"Mind Ferret", 'Mind Ferret',
"Mystic Salt Spider", 'Mystic Salt Spider',
"Necrotic Halitosis Angel", 'Necrotic Halitosis Angel',
"Pinstriped Famine Sheep", 'Pinstriped Famine Sheep',
"Ritalin Leech", 'Ritalin Leech',
"Shocker Kangaroo", 'Shocker Kangaroo',
"Stellar Tennis Juggernaut", 'Stellar Tennis Juggernaut',
"Wailing Quail of the Sun", 'Wailing Quail of the Sun',
"Angel Pigeon", 'Angel Pigeon',
"Anime Sphinx", 'Anime Sphinx',
"Bored Avalanche Sheep of the Wasteland", 'Bored Avalanche Sheep of the Wasteland',
"Devouring Nougat Sphinx of the Sock Drawer", 'Devouring Nougat Sphinx of the Sock Drawer',
"Djinni of the Footlocker", 'Djinni of the Footlocker',
"Ectoplasmic Jazz Devil", 'Ectoplasmic Jazz Devil',
"Flatuent Angel", 'Flatuent Angel',
"Gelatinous Duck of the Dream-Lands", 'Gelatinous Duck of the Dream-Lands',
"Gelatinous Mouse", 'Gelatinous Mouse',
"Golem of the Footlocker", 'Golem of the Footlocker',
"Lich Wombat", 'Lich Wombat',
"Mechanical Sloth of the Past", 'Mechanical Sloth of the Past',
"Milkshake Succubus", 'Milkshake Succubus',
"Puffy Bone Peacock of the East", 'Puffy Bone Peacock of the East',
"Rainbow Manatee", 'Rainbow Manatee',
"Rune Parrot", 'Rune Parrot',
"Sand Cow", 'Sand Cow',
"Sinister Vanilla Dragon", 'Sinister Vanilla Dragon',
"Snail of the North", 'Snail of the North',
"Spider of the Sewer", 'Spider of the Sewer',
"Stellar Sawdust Leech", 'Stellar Sawdust Leech',
"Storm Anteater of Hell", 'Storm Anteater of Hell',
"Stupid Spirit of the Brewery", 'Stupid Spirit of the Brewery',
"Time Kangaroo", 'Time Kangaroo',
"Tomb Poodle", 'Tomb Poodle',
]);
}
var getType = function(){
return _.sample(['Tiny', 'Small', 'Medium', 'Large', 'Gargantuan', 'Stupidly vast']) + " " + _.sample(['beast', 'fiend', 'annoyance', 'guy', 'cutie'])
}
var getAlignment = function(){
return _.sample([
"annoying evil",
"chaotic gossipy",
"chaotic sloppy",
"depressed neutral",
"lawful bogus",
"lawful coy",
"manic-depressive evil",
"narrow-minded neutral",
"neutral annoying",
"neutral ignorant",
"oedpipal neutral",
"silly neutral",
"unoriginal neutral",
"weird neutral",
"wordy evil",
"unaligned"
]); ]);
}; };
var getStats = function(){ const getType = function(){
return '>|' + _.times(6, function(){ return `${_.sample(['Tiny', 'Small', 'Medium', 'Large', 'Gargantuan', 'Stupidly vast'])} ${_.sample(['beast', 'fiend', 'annoyance', 'guy', 'cutie'])}`;
var num = _.random(1,20); };
var mod = Math.ceil(num/2 - 5)
return num + " (" + (mod >= 0 ? '+'+mod : mod ) + ")"
}).join('|') + '|';
}
var genAbilities = function(){ const getAlignment = function(){
return _.sample([ return _.sample([
"> ***Pack Tactics.*** These guys work together. Like super well, you don't even know.", 'annoying evil',
"> ***False Appearance. *** While the armor reamin motionless, it is indistinguishable from a normal suit of armor.", 'chaotic gossipy',
'chaotic sloppy',
'depressed neutral',
'lawful bogus',
'lawful coy',
'manic-depressive evil',
'narrow-minded neutral',
'neutral annoying',
'neutral ignorant',
'oedpipal neutral',
'silly neutral',
'unoriginal neutral',
'weird neutral',
'wordy evil',
'unaligned'
]); ]);
} };
var genAction = function(){ const getStats = function(){
var name = _.sample([ return `>|${_.times(6, function(){
"Abdominal Drop", const num = _.random(1, 20);
"Airplane Hammer", const mod = Math.ceil(num/2 - 5);
"Atomic Death Throw", return `${num} (${mod >= 0 ? `+${mod}` : mod})`;
"Bulldog Rake", }).join('|')}|`;
"Corkscrew Strike", };
"Crossed Splash",
"Crossface Suplex",
"DDT Powerbomb",
"Dual Cobra Wristlock",
"Dual Throw",
"Elbow Hold",
"Gory Body Sweep",
"Heel Jawbreaker",
"Jumping Driver",
"Open Chin Choke",
"Scorpion Flurry",
"Somersault Stump Fists",
"Suffering Wringer",
"Super Hip Submission",
"Super Spin",
"Team Elbow",
"Team Foot",
"Tilt-a-whirl Chin Sleeper",
"Tilt-a-whirl Eye Takedown",
"Turnbuckle Roll"
])
return "> ***" + name + ".*** *Melee Weapon Attack:* +4 to hit, reach 5ft., one target. *Hit* 5 (1d6 + 2) "; const genAbilities = function(){
} return _.sample([
'> ***Pack Tactics.*** These guys work together. Like super well, you don\'t even know.',
'> ***False Appearance. *** While the armor reamin motionless, it is indistinguishable from a normal suit of armor.',
]);
};
const genAction = function(){
const name = _.sample([
'Abdominal Drop',
'Airplane Hammer',
'Atomic Death Throw',
'Bulldog Rake',
'Corkscrew Strike',
'Crossed Splash',
'Crossface Suplex',
'DDT Powerbomb',
'Dual Cobra Wristlock',
'Dual Throw',
'Elbow Hold',
'Gory Body Sweep',
'Heel Jawbreaker',
'Jumping Driver',
'Open Chin Choke',
'Scorpion Flurry',
'Somersault Stump Fists',
'Suffering Wringer',
'Super Hip Submission',
'Super Spin',
'Team Elbow',
'Team Foot',
'Tilt-a-whirl Chin Sleeper',
'Tilt-a-whirl Eye Takedown',
'Turnbuckle Roll'
]);
return `> ***${name}.*** *Melee Weapon Attack:* +4 to hit, reach 5ft., one target. *Hit* 5 (1d6 + 2) `;
};
module.exports = { module.exports = {
full : function(){ full : function(){
return [ return `${[
"___", '___',
"___", '___',
"> ## " + getMonsterName(), `> ## ${getMonsterName()}`,
">*" + getType() + ", " + getAlignment() + "*", `>*${getType()}, ${getAlignment()}*`,
"> ___", '> ___',
"> - **Armor Class** " + _.random(10,20), `> - **Armor Class** ${_.random(10, 20)}`,
"> - **Hit Points** " + _.random(1, 150) + "(1d4 + 5)", `> - **Hit Points** ${_.random(1, 150)}(1d4 + 5)`,
"> - **Speed** " + _.random(0,50) + "ft.", `> - **Speed** ${_.random(0, 50)}ft.`,
">___", '>___',
">|STR|DEX|CON|INT|WIS|CHA|", '>|STR|DEX|CON|INT|WIS|CHA|',
">|:---:|:---:|:---:|:---:|:---:|:---:|", '>|:---:|:---:|:---:|:---:|:---:|:---:|',
getStats(), getStats(),
">___", '>___',
"> - **Condition Immunities** " + genList(["groggy", "swagged", "weak-kneed", "buzzed", "groovy", "melancholy", "drunk"], 3), `> - **Condition Immunities** ${genList(['groggy', 'swagged', 'weak-kneed', 'buzzed', 'groovy', 'melancholy', 'drunk'], 3)}`,
"> - **Senses** passive Perception " + _.random(3, 20), `> - **Senses** passive Perception ${_.random(3, 20)}`,
"> - **Languages** " + genList(["Common", "Pottymouth", "Gibberish", "Latin", "Jive"], 2), `> - **Languages** ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}`,
"> - **Challenge** " + _.random(0, 15) + " (" + _.random(10,10000)+ " XP)", `> - **Challenge** ${_.random(0, 15)} (${_.random(10, 10000)} XP)`,
"> ___", '> ___',
_.times(_.random(3,6), function(){ _.times(_.random(3, 6), function(){
return genAbilities() return genAbilities();
}).join('\n>\n'), }).join('\n>\n'),
"> ### Actions", '> ### Actions',
_.times(_.random(4,6), function(){ _.times(_.random(4, 6), function(){
return genAction() return genAction();
}).join('\n>\n'), }).join('\n>\n'),
].join('\n') + '\n\n\n'; ].join('\n')}\n\n\n`;
}, },
half : function(){ half : function(){
return [ return `${[
"___", '___',
"> ## " + getMonsterName(), `> ## ${getMonsterName()}`,
">*" + getType() + ", " + getAlignment() + "*", `>*${getType()}, ${getAlignment()}*`,
"> ___", '> ___',
"> - **Armor Class** " + _.random(10,20), `> - **Armor Class** ${_.random(10, 20)}`,
"> - **Hit Points** " + _.random(1, 150) + "(1d4 + 5)", `> - **Hit Points** ${_.random(1, 150)}(1d4 + 5)`,
"> - **Speed** " + _.random(0,50) + "ft.", `> - **Speed** ${_.random(0, 50)}ft.`,
">___", '>___',
">|STR|DEX|CON|INT|WIS|CHA|", '>|STR|DEX|CON|INT|WIS|CHA|',
">|:---:|:---:|:---:|:---:|:---:|:---:|", '>|:---:|:---:|:---:|:---:|:---:|:---:|',
getStats(), getStats(),
">___", '>___',
"> - **Condition Immunities** " + genList(["groggy", "swagged", "weak-kneed", "buzzed", "groovy", "melancholy", "drunk"], 3), `> - **Condition Immunities** ${genList(['groggy', 'swagged', 'weak-kneed', 'buzzed', 'groovy', 'melancholy', 'drunk'], 3)}`,
"> - **Senses** passive Perception " + _.random(3, 20), `> - **Senses** passive Perception ${_.random(3, 20)}`,
"> - **Languages** " + genList(["Common", "Pottymouth", "Gibberish", "Latin", "Jive"], 2), `> - **Languages** ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}`,
"> - **Challenge** " + _.random(0, 15) + " (" + _.random(10,10000)+ " XP)", `> - **Challenge** ${_.random(0, 15)} (${_.random(10, 10000)} XP)`,
"> ___", '> ___',
_.times(_.random(0,2), function(){ _.times(_.random(0, 2), function(){
return genAbilities() return genAbilities();
}).join('\n>\n'), }).join('\n>\n'),
"> ### Actions", '> ### Actions',
_.times(_.random(1,2), function(){ _.times(_.random(1, 2), function(){
return genAction() return genAction();
}).join('\n>\n'), }).join('\n>\n'),
].join('\n') + '\n\n\n'; ].join('\n')}\n\n\n`;
} }
} };

View File

@@ -1,10 +1,11 @@
var MagicGen = require('./magic.gen.js'); /* eslint-disable max-lines */
var ClassTableGen = require('./classtable.gen.js');
var MonsterBlockGen = require('./monsterblock.gen.js'); const MagicGen = require('./magic.gen.js');
var ClassFeatureGen = require('./classfeature.gen.js'); const ClassTableGen = require('./classtable.gen.js');
var FullClassGen = require('./fullclass.gen.js'); const MonsterBlockGen = require('./monsterblock.gen.js');
var CoverPageGen = require('./coverpage.gen.js'); const ClassFeatureGen = require('./classfeature.gen.js');
var TableOfContentsGen = require('./tableOfContents.gen.js'); const CoverPageGen = require('./coverpage.gen.js');
const TableOfContentsGen = require('./tableOfContents.gen.js');
module.exports = [ module.exports = [
@@ -14,65 +15,65 @@ module.exports = [
icon : 'fa-pencil', icon : 'fa-pencil',
snippets : [ snippets : [
{ {
name : "Column Break", name : 'Column Break',
icon : 'fa-columns', icon : 'fa-columns',
gen : "```\n```\n\n" gen : '```\n```\n\n'
}, },
{ {
name : "New Page", name : 'New Page',
icon : 'fa-file-text', icon : 'fa-file-text',
gen : "\\page\n\n" gen : '\\page\n\n'
}, },
{ {
name : "Vertical Spacing", name : 'Vertical Spacing',
icon : 'fa-arrows-v', icon : 'fa-arrows-v',
gen : "<div style='margin-top:140px'></div>\n\n" gen : '<div style=\'margin-top:140px\'></div>\n\n'
}, },
{ {
name : "Wide Block", name : 'Wide Block',
icon : 'fa-arrows-h', icon : 'fa-arrows-h',
gen : "<div class='wide'>\nEverything in here will be extra wide. Tables, text, everything! Beware though, CSS columns can behave a bit weird sometimes.\n</div>\n" gen : '<div class=\'wide\'>\nEverything in here will be extra wide. Tables, text, everything! Beware though, CSS columns can behave a bit weird sometimes.\n</div>\n'
}, },
{ {
name : "Image", name : 'Image',
icon : 'fa-image', icon : 'fa-image',
gen : [ gen : [
"<img ", '<img ',
" src='https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg' ", ' src=\'https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg\' ',
" style='width:325px' />", ' style=\'width:325px\' />',
"Credit: Kyounghwan Kim" 'Credit: Kyounghwan Kim'
].join('\n') ].join('\n')
}, },
{ {
name : "Background Image", name : 'Background Image',
icon : 'fa-tree', icon : 'fa-tree',
gen : [ gen : [
"<img ", '<img ',
" src='http://i.imgur.com/hMna6G0.png' ", ' src=\'http://i.imgur.com/hMna6G0.png\' ',
" style='position:absolute; top:50px; right:30px; width:280px' />" ' style=\'position:absolute; top:50px; right:30px; width:280px\' />'
].join('\n') ].join('\n')
}, },
{ {
name : "Page Number", name : 'Page Number',
icon : 'fa-bookmark', icon : 'fa-bookmark',
gen : "<div class='pageNumber'>1</div>\n<div class='footnote'>PART 1 | FANCINESS</div>\n\n" gen : '<div class=\'pageNumber\'>1</div>\n<div class=\'footnote\'>PART 1 | FANCINESS</div>\n\n'
}, },
{ {
name : "Auto-incrementing Page Number", name : 'Auto-incrementing Page Number',
icon : 'fa-sort-numeric-asc', icon : 'fa-sort-numeric-asc',
gen : "<div class='pageNumber auto'></div>\n" gen : '<div class=\'pageNumber auto\'></div>\n'
}, },
{ {
name : "Link to page", name : 'Link to page',
icon : 'fa-link', icon : 'fa-link',
gen : "[Click here](#p3) to go to page 3\n" gen : '[Click here](#p3) to go to page 3\n'
}, },
{ {
name : "Table of Contents", name : 'Table of Contents',
icon : 'fa-book', icon : 'fa-book',
gen : TableOfContentsGen gen : TableOfContentsGen
}, },
@@ -108,10 +109,10 @@ module.exports = [
icon : 'fa-sticky-note', icon : 'fa-sticky-note',
gen : function(){ gen : function(){
return [ return [
"> ##### Time to Drop Knowledge", '> ##### Time to Drop Knowledge',
"> Use notes to point out some interesting information. ", '> Use notes to point out some interesting information. ',
"> ", '> ',
"> **Tables and lists** both work within a note." '> **Tables and lists** both work within a note.'
].join('\n'); ].join('\n');
}, },
}, },
@@ -120,12 +121,12 @@ module.exports = [
icon : 'fa-sticky-note-o', icon : 'fa-sticky-note-o',
gen : function(){ gen : function(){
return [ return [
"<div class='descriptive'>", '<div class=\'descriptive\'>',
"##### Time to Drop Knowledge", '##### Time to Drop Knowledge',
"Use notes to point out some interesting information. ", 'Use notes to point out some interesting information. ',
"", '',
"**Tables and lists** both work within a note.", '**Tables and lists** both work within a note.',
"</div>" '</div>'
].join('\n'); ].join('\n');
}, },
}, },
@@ -156,12 +157,12 @@ module.exports = [
icon : 'fa-table', icon : 'fa-table',
snippets : [ snippets : [
{ {
name : "Class Table", name : 'Class Table',
icon : 'fa-table', icon : 'fa-table',
gen : ClassTableGen.full, gen : ClassTableGen.full,
}, },
{ {
name : "Half Class Table", name : 'Half Class Table',
icon : 'fa-list-alt', icon : 'fa-list-alt',
gen : ClassTableGen.half, gen : ClassTableGen.half,
}, },
@@ -170,14 +171,14 @@ module.exports = [
icon : 'fa-th-list', icon : 'fa-th-list',
gen : function(){ gen : function(){
return [ return [
"##### Cookie Tastiness", '##### Cookie Tastiness',
"| Tastiness | Cookie Type |", '| Tastiness | Cookie Type |',
"|:----:|:-------------|", '|:----:|:-------------|',
"| -5 | Raisin |", '| -5 | Raisin |',
"| 8th | Chocolate Chip |", '| 8th | Chocolate Chip |',
"| 11th | 2 or lower |", '| 11th | 2 or lower |',
"| 14th | 3 or lower |", '| 14th | 3 or lower |',
"| 17th | 4 or lower |\n\n", '| 17th | 4 or lower |\n\n',
].join('\n'); ].join('\n');
}, },
}, },
@@ -186,16 +187,16 @@ module.exports = [
icon : 'fa-list', icon : 'fa-list',
gen : function(){ gen : function(){
return [ return [
"<div class='wide'>", '<div class=\'wide\'>',
"##### Cookie Tastiness", '##### Cookie Tastiness',
"| Tastiness | Cookie Type |", '| Tastiness | Cookie Type |',
"|:----:|:-------------|", '|:----:|:-------------|',
"| -5 | Raisin |", '| -5 | Raisin |',
"| 8th | Chocolate Chip |", '| 8th | Chocolate Chip |',
"| 11th | 2 or lower |", '| 11th | 2 or lower |',
"| 14th | 3 or lower |", '| 14th | 3 or lower |',
"| 17th | 4 or lower |", '| 17th | 4 or lower |',
"</div>\n\n" '</div>\n\n'
].join('\n'); ].join('\n');
}, },
}, },
@@ -204,26 +205,26 @@ module.exports = [
icon : 'fa-th-large', icon : 'fa-th-large',
gen : function(){ gen : function(){
return [ return [
"<div style='column-count:2'>", '<div style=\'column-count:2\'>',
"| d10 | Damage Type |", '| d10 | Damage Type |',
"|:---:|:------------|", '|:---:|:------------|',
"| 1 | Acid |", '| 1 | Acid |',
"| 2 | Cold |", '| 2 | Cold |',
"| 3 | Fire |", '| 3 | Fire |',
"| 4 | Force |", '| 4 | Force |',
"| 5 | Lightning |", '| 5 | Lightning |',
"", '',
"```", '```',
"```", '```',
"", '',
"| d10 | Damage Type |", '| d10 | Damage Type |',
"|:---:|:------------|", '|:---:|:------------|',
"| 6 | Necrotic |", '| 6 | Necrotic |',
"| 7 | Poison |", '| 7 | Poison |',
"| 8 | Psychic |", '| 8 | Psychic |',
"| 9 | Radiant |", '| 9 | Radiant |',
"| 10 | Thunder |", '| 10 | Thunder |',
"</div>\n\n", '</div>\n\n',
].join('\n'); ].join('\n');
}, },
} }
@@ -240,7 +241,7 @@ module.exports = [
icon : 'fa-print', icon : 'fa-print',
snippets : [ snippets : [
{ {
name : "A4 PageSize", name : 'A4 PageSize',
icon : 'fa-file-o', icon : 'fa-file-o',
gen : ['<style>', gen : ['<style>',
' .phb{', ' .phb{',
@@ -251,7 +252,7 @@ module.exports = [
].join('\n') ].join('\n')
}, },
{ {
name : "Ink Friendly", name : 'Ink Friendly',
icon : 'fa-tint', icon : 'fa-tint',
gen : ['<style>', gen : ['<style>',
' .phb{ background : white;}', ' .phb{ background : white;}',
@@ -264,4 +265,4 @@ module.exports = [
] ]
}, },
] ];

View File

@@ -1,13 +1,13 @@
const _ = require('lodash'); const _ = require('lodash');
const getTOC = (pages) => { const getTOC = (pages)=>{
const add1 = (title, page)=>{ const add1 = (title, page)=>{
res.push({ res.push({
title : title, title : title,
page : page + 1, page : page + 1,
children : [] children : []
}); });
} };
const add2 = (title, page)=>{ const add2 = (title, page)=>{
if(!_.last(res)) add1('', page); if(!_.last(res)) add1('', page);
_.last(res).children.push({ _.last(res).children.push({
@@ -15,7 +15,7 @@ const getTOC = (pages) => {
page : page + 1, page : page + 1,
children : [] children : []
}); });
} };
const add3 = (title, page)=>{ const add3 = (title, page)=>{
if(!_.last(res)) add1('', page); if(!_.last(res)) add1('', page);
if(!_.last(_.last(res).children)) add2('', page); if(!_.last(_.last(res).children)) add2('', page);
@@ -24,15 +24,15 @@ const getTOC = (pages) => {
page : page + 1, page : page + 1,
children : [] children : []
}); });
} };
let res = []; const res = [];
_.each(pages, (page, pageNum)=>{ _.each(pages, (page, pageNum)=>{
const lines = page.split('\n'); const lines = page.split('\n');
_.each(lines, (line) => { _.each(lines, (line)=>{
if(_.startsWith(line, '# ')){ if(_.startsWith(line, '# ')){
const title = line.replace('# ', ''); const title = line.replace('# ', '');
add1(title, pageNum) add1(title, pageNum);
} }
if(_.startsWith(line, '## ')){ if(_.startsWith(line, '## ')){
const title = line.replace('## ', ''); const title = line.replace('## ', '');
@@ -42,21 +42,21 @@ const getTOC = (pages) => {
const title = line.replace('### ', ''); const title = line.replace('### ', '');
add3(title, pageNum); add3(title, pageNum);
} }
}) });
}); });
return res; return res;
} };
module.exports = function(brew){ module.exports = function(brew){
const pages = brew.split('\\page'); const pages = brew.split('\\page');
const TOC = getTOC(pages); const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{ const markdown = _.reduce(TOC, (r, g1, idx1)=>{
r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`) r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`);
if(g1.children.length){ if(g1.children.length){
_.each(g1.children, (g2, idx2) => { _.each(g1.children, (g2, idx2)=>{
r.push(` - [${idx1 + 1}.${idx2 + 1} ${g2.title}](#p${g2.page})`); r.push(` - [${idx1 + 1}.${idx2 + 1} ${g2.title}](#p${g2.page})`);
if(g2.children.length){ if(g2.children.length){
_.each(g2.children, (g3, idx3) => { _.each(g2.children, (g3, idx3)=>{
r.push(` - [${idx1 + 1}.${idx2 + 1}.${idx3 + 1} ${g3.title}](#p${g3.page})`); r.push(` - [${idx1 + 1}.${idx2 + 1}.${idx3 + 1} ${g3.title}](#p${g3.page})`);
}); });
} }
@@ -69,4 +69,4 @@ module.exports = function(brew){
##### Table Of Contents ##### Table Of Contents
${markdown} ${markdown}
</div>\n`; </div>\n`;
} };

View File

@@ -14,7 +14,7 @@ const PrintPage = require('./pages/printPage/printPage.jsx');
let Router; let Router;
const Homebrew = React.createClass({ const Homebrew = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
url : '', url : '',
welcomeText : '', welcomeText : '',
@@ -31,49 +31,49 @@ const Homebrew = React.createClass({
} }
}; };
}, },
componentWillMount: function() { componentWillMount : function() {
global.account = this.props.account; global.account = this.props.account;
global.version = this.props.version; global.version = this.props.version;
Router = CreateRouter({ Router = CreateRouter({
'/edit/:id' : (args) => { '/edit/:id' : (args)=>{
if(!this.props.brew.editId){ if(!this.props.brew.editId){
return <ErrorPage errorId={args.id}/> return <ErrorPage errorId={args.id}/>;
} }
return <EditPage return <EditPage
id={args.id} id={args.id}
brew={this.props.brew} /> brew={this.props.brew} />;
}, },
'/share/:id' : (args) => { '/share/:id' : (args)=>{
if(!this.props.brew.shareId){ if(!this.props.brew.shareId){
return <ErrorPage errorId={args.id}/> return <ErrorPage errorId={args.id}/>;
} }
return <SharePage return <SharePage
id={args.id} id={args.id}
brew={this.props.brew} /> brew={this.props.brew} />;
}, },
'/user/:username' : (args) => { '/user/:username' : (args)=>{
return <UserPage return <UserPage
username={args.username} username={args.username}
brews={this.props.brews} brews={this.props.brews}
/> />;
}, },
'/print/:id' : (args, query) => { '/print/:id' : (args, query)=>{
return <PrintPage brew={this.props.brew} query={query}/>; return <PrintPage brew={this.props.brew} query={query}/>;
}, },
'/print' : (args, query) => { '/print' : (args, query)=>{
return <PrintPage query={query}/>; return <PrintPage query={query}/>;
}, },
'/new' : (args) => { '/new' : (args)=>{
return <NewPage /> return <NewPage />;
}, },
'/changelog' : (args) => { '/changelog' : (args)=>{
return <SharePage return <SharePage
brew={{title : 'Changelog', text : this.props.changelog}} /> brew={{ title: 'Changelog', text: this.props.changelog }} />;
}, },
'*' : <HomePage '*' : <HomePage
welcomeText={this.props.welcomeText} />, welcomeText={this.props.welcomeText} />,
@@ -82,7 +82,7 @@ const Homebrew = React.createClass({
render : function(){ render : function(){
return <div className='homebrew'> return <div className='homebrew'>
<Router initialUrl={this.props.url}/> <Router initialUrl={this.props.url}/>
</div> </div>;
} }
}); });

View File

@@ -5,13 +5,13 @@ module.exports = function(props){
if(global.account){ if(global.account){
return <Nav.item href={`/user/${global.account.username}`} color='yellow' icon='fa-user'> return <Nav.item href={`/user/${global.account.username}`} color='yellow' icon='fa-user'>
{global.account.username} {global.account.username}
</Nav.item> </Nav.item>;
} }
let url = ''; let url = '';
if(typeof window !== 'undefined'){ if(typeof window !== 'undefined'){
url = window.location.href url = window.location.href;
} }
return <Nav.item href={`http://naturalcrit.com/login?redirect=${url}`} color='teal' icon='fa-sign-in'> return <Nav.item href={`http://naturalcrit.com/login?redirect=${url}`} color='teal' icon='fa-sign-in'>
login login
</Nav.item> </Nav.item>;
}; };

View File

@@ -1,13 +1,13 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const MAX_TITLE_LENGTH = 50; const MAX_TITLE_LENGTH = 50;
var EditTitle = React.createClass({ const EditTitle = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
title : '', title : '',
onChange : function(){} onChange : function(){}
@@ -22,10 +22,10 @@ var EditTitle = React.createClass({
return <Nav.item className='editTitle'> return <Nav.item className='editTitle'>
<input placeholder='Brew Title' type='text' value={this.props.title} onChange={this.handleChange} /> <input placeholder='Brew Title' type='text' value={this.props.title} onChange={this.handleChange} />
<div className={cx('charCount', {'max' : this.props.title.length >= MAX_TITLE_LENGTH})}> <div className={cx('charCount', { 'max': this.props.title.length >= MAX_TITLE_LENGTH })}>
{this.props.title.length}/{MAX_TITLE_LENGTH} {this.props.title.length}/{MAX_TITLE_LENGTH}
</div> </div>
</Nav.item> </Nav.item>;
}, },
}); });

View File

@@ -1,8 +1,8 @@
var React = require('react'); const React = require('react');
var Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
module.exports = function(props){ module.exports = function(props){
return <Nav.item newTab={true} href='https://www.reddit.com/r/homebrewery/submit?selftext=true&title=[Issue]' color='red' icon='fa-bug'> return <Nav.item newTab={true} href='https://www.reddit.com/r/homebrewery/submit?selftext=true&title=[Issue]' color='red' icon='fa-bug'>
report issue report issue
</Nav.item> </Nav.item>;
}; };

View File

@@ -4,22 +4,22 @@ const _ = require('lodash');
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = React.createClass({ const Navbar = React.createClass({
getInitialState: function() { getInitialState : function() {
return { return {
//showNonChromeWarning : false, //showNonChromeWarning : false,
ver : '0.0.0' ver : '0.0.0'
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
//const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor); //const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
this.setState({ this.setState({
//showNonChromeWarning : !isChrome, //showNonChromeWarning : !isChrome,
ver : window.version ver : window.version
}) });
}, },
/* /*
renderChromeWarning : function(){ renderChromeWarning : function(){
if(!this.state.showNonChromeWarning) return; if(!this.state.showNonChromeWarning) return;
return <Nav.item className='warning' icon='fa-exclamation-triangle'> return <Nav.item className='warning' icon='fa-exclamation-triangle'>
@@ -42,7 +42,7 @@ const Navbar = React.createClass({
{/*this.renderChromeWarning()*/} {/*this.renderChromeWarning()*/}
</Nav.section> </Nav.section>
{this.props.children} {this.props.children}
</Nav.base> </Nav.base>;
} }
}); });

View File

@@ -1,5 +1,5 @@
var React = require('react'); const React = require('react');
var Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
module.exports = function(props){ module.exports = function(props){
return <Nav.item return <Nav.item
@@ -9,5 +9,5 @@ module.exports = function(props){
color='green' color='green'
icon='fa-heart'> icon='fa-heart'>
help out help out
</Nav.item> </Nav.item>;
}; };

View File

@@ -1,8 +1,8 @@
var React = require('react'); const React = require('react');
var Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
module.exports = function(props){ module.exports = function(props){
return <Nav.item newTab={true} href={'/print/' + props.shareId +'?dialog=true'} color='purple' icon='fa-file-pdf-o'> return <Nav.item newTab={true} href={`/print/${props.shareId}?dialog=true`} color='purple' icon='fa-file-pdf-o'>
get PDF get PDF
</Nav.item> </Nav.item>;
}; };

View File

@@ -1,34 +1,34 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var Moment = require('moment'); const Moment = require('moment');
var Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const VIEW_KEY = 'homebrewery-recently-viewed'; const VIEW_KEY = 'homebrewery-recently-viewed';
const EDIT_KEY = 'homebrewery-recently-edited'; const EDIT_KEY = 'homebrewery-recently-edited';
var BaseItem = React.createClass({ const BaseItem = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
storageKey : '', storageKey : '',
text : '', text : '',
currentBrew:{ currentBrew : {
title : '', title : '',
id : '', id : '',
url : '' url : ''
} }
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
showDropdown: false, showDropdown : false,
brews : [] brews : []
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
var brews = JSON.parse(localStorage.getItem(this.props.storageKey) || '[]'); let brews = JSON.parse(localStorage.getItem(this.props.storageKey) || '[]');
brews = _.filter(brews, (brew)=>{ brews = _.filter(brews, (brew)=>{
return brew.id !== this.props.currentBrew.id; return brew.id !== this.props.currentBrew.id;
@@ -51,29 +51,29 @@ var BaseItem = React.createClass({
handleDropdown : function(show){ handleDropdown : function(show){
this.setState({ this.setState({
showDropdown : show showDropdown : show
}) });
}, },
renderDropdown : function(){ renderDropdown : function(){
if(!this.state.showDropdown) return null; if(!this.state.showDropdown) return null;
var items = _.map(this.state.brews, (brew)=>{ const items = _.map(this.state.brews, (brew)=>{
return <a href={brew.url} className='item' key={brew.id} target='_blank'> return <a href={brew.url} className='item' key={brew.id} target='_blank'>
<span className='title'>{brew.title}</span> <span className='title'>{brew.title}</span>
<span className='time'>{Moment(brew.ts).fromNow()}</span> <span className='time'>{Moment(brew.ts).fromNow()}</span>
</a> </a>;
}); });
return <div className='dropdown'>{items}</div> return <div className='dropdown'>{items}</div>;
}, },
render : function(){ render : function(){
return <Nav.item icon='fa-clock-o' color='grey' className='recent' return <Nav.item icon='fa-clock-o' color='grey' className='recent'
onMouseEnter={this.handleDropdown.bind(null, true)} onMouseEnter={()=>this.handleDropdown(true)}
onMouseLeave={this.handleDropdown.bind(null, false)}> onMouseLeave={()=>this.handleDropdown(false)}>
{this.props.text} {this.props.text}
{this.renderDropdown()} {this.renderDropdown()}
</Nav.item> </Nav.item>;
}, },
}); });
@@ -82,7 +82,7 @@ var BaseItem = React.createClass({
module.exports = { module.exports = {
viewed : React.createClass({ viewed : React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
brew : { brew : {
title : '', title : '',
@@ -97,12 +97,12 @@ module.exports = {
title : this.props.brew.title, title : this.props.brew.title,
url : `/share/${this.props.brew.shareId}` url : `/share/${this.props.brew.shareId}`
}} }}
/> />;
}, },
}), }),
edited : React.createClass({ edited : React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
brew : { brew : {
title : '', title : '',
@@ -117,35 +117,35 @@ module.exports = {
title : this.props.brew.title, title : this.props.brew.title,
url : `/edit/${this.props.brew.editId}` url : `/edit/${this.props.brew.editId}`
}} }}
/> />;
}, },
}), }),
both : React.createClass({ both : React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
errorId : null errorId : null
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
showDropdown: false, showDropdown : false,
edit : [], edit : [],
view : [] view : []
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
var edited = JSON.parse(localStorage.getItem(EDIT_KEY) || '[]'); let edited = JSON.parse(localStorage.getItem(EDIT_KEY) || '[]');
var viewed = JSON.parse(localStorage.getItem(VIEW_KEY) || '[]'); let viewed = JSON.parse(localStorage.getItem(VIEW_KEY) || '[]');
if(this.props.errorId){ if(this.props.errorId){
edited = _.filter(edited, (edit) => { edited = _.filter(edited, (edit)=>{
return edit.id !== this.props.errorId; return edit.id !== this.props.errorId;
}); });
viewed = _.filter(viewed, (view) => { viewed = _.filter(viewed, (view)=>{
return view.id !== this.props.errorId; return view.id !== this.props.errorId;
}); });
@@ -163,18 +163,18 @@ module.exports = {
handleDropdown : function(show){ handleDropdown : function(show){
this.setState({ this.setState({
showDropdown : show showDropdown : show
}) });
}, },
renderDropdown : function(){ renderDropdown : function(){
if(!this.state.showDropdown) return null; if(!this.state.showDropdown) return null;
var makeItems = (brews) => { const makeItems = (brews)=>{
return _.map(brews, (brew)=>{ return _.map(brews, (brew)=>{
return <a href={brew.url} className='item' key={brew.id} target='_blank'> return <a href={brew.url} className='item' key={brew.id} target='_blank'>
<span className='title'>{brew.title}</span> <span className='title'>{brew.title}</span>
<span className='time'>{Moment(brew.ts).fromNow()}</span> <span className='time'>{Moment(brew.ts).fromNow()}</span>
</a> </a>;
}); });
}; };
@@ -183,17 +183,17 @@ module.exports = {
{makeItems(this.state.edit)} {makeItems(this.state.edit)}
<h4>viewed</h4> <h4>viewed</h4>
{makeItems(this.state.view)} {makeItems(this.state.view)}
</div> </div>;
}, },
render : function(){ render : function(){
return <Nav.item icon='fa-clock-o' color='grey' className='recent' return <Nav.item icon='fa-clock-o' color='grey' className='recent'
onMouseEnter={this.handleDropdown.bind(null, true)} onMouseEnter={()=>this.handleDropdown(true)}
onMouseLeave={this.handleDropdown.bind(null, false)}> onMouseLeave={()=>this.handleDropdown(false)}>
Recent brews Recent brews
{this.renderDropdown()} {this.renderDropdown()}
</Nav.item> </Nav.item>;
} }
}) })
} };

View File

@@ -1,17 +1,17 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
//var striptags = require('striptags'); //var striptags = require('striptags');
var Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const MAX_URL_SIZE = 2083; const MAX_URL_SIZE = 2083;
const MAIN_URL = "https://www.reddit.com/r/UnearthedArcana/submit?selftext=true" const MAIN_URL = 'https://www.reddit.com/r/UnearthedArcana/submit?selftext=true';
var RedditShare = React.createClass({ const RedditShare = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
brew : { brew : {
title : '', title : '',
@@ -27,11 +27,11 @@ var RedditShare = React.createClass({
handleClick : function(){ handleClick : function(){
var url = [ const url = [
MAIN_URL, MAIN_URL,
'title=' + encodeURIComponent(this.props.brew.title ? this.props.brew.title : 'Check out my brew!'), `title=${encodeURIComponent(this.props.brew.title ? this.props.brew.title : 'Check out my brew!')}`,
'text=' + encodeURIComponent(this.props.brew.text) `text=${encodeURIComponent(this.props.brew.text)}`
].join('&'); ].join('&');
@@ -43,7 +43,7 @@ var RedditShare = React.createClass({
render : function(){ render : function(){
return <Nav.item icon='fa-reddit-alien' color='red' onClick={this.handleClick}> return <Nav.item icon='fa-reddit-alien' color='red' onClick={this.handleClick}>
share on reddit share on reddit
</Nav.item> </Nav.item>;
}, },
}); });

View File

@@ -1,7 +1,7 @@
const React = require('react'); const React = require('react');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const request = require("superagent"); const request = require('superagent');
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx'); const Navbar = require('../../navbar/navbar.jsx');
@@ -21,7 +21,7 @@ const SAVE_TIMEOUT = 3000;
const EditPage = React.createClass({ const EditPage = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
brew : { brew : {
text : '', text : '',
@@ -40,7 +40,7 @@ const EditPage = React.createClass({
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
brew : this.props.brew, brew : this.props.brew,
@@ -53,7 +53,7 @@ const EditPage = React.createClass({
}, },
savedBrew : null, savedBrew : null,
componentDidMount: function(){ componentDidMount : function(){
this.trySave(); this.trySave();
window.onbeforeunload = ()=>{ window.onbeforeunload = ()=>{
if(this.state.isSaving || this.state.isPending){ if(this.state.isSaving || this.state.isPending){
@@ -63,11 +63,11 @@ const EditPage = React.createClass({
this.setState({ this.setState({
htmlErrors : Markdown.validate(this.state.brew.text) htmlErrors : Markdown.validate(this.state.brew.text)
}) });
document.addEventListener('keydown', this.handleControlKeys); document.addEventListener('keydown', this.handleControlKeys);
}, },
componentWillUnmount: function() { componentWillUnmount : function() {
window.onbeforeunload = function(){}; window.onbeforeunload = function(){};
document.removeEventListener('keydown', this.handleControlKeys); document.removeEventListener('keydown', this.handleControlKeys);
}, },
@@ -102,11 +102,11 @@ const EditPage = React.createClass({
handleTextChange : function(text){ handleTextChange : function(text){
//If there are errors, run the validator on everychange to give quick feedback //If there are errors, run the validator on everychange to give quick feedback
var htmlErrors = this.state.htmlErrors; let htmlErrors = this.state.htmlErrors;
if(htmlErrors.length) htmlErrors = Markdown.validate(text); if(htmlErrors.length) htmlErrors = Markdown.validate(text);
this.setState({ this.setState({
brew : _.merge({}, this.state.brew, {text : text}), brew : _.merge({}, this.state.brew, { text: text }),
isPending : true, isPending : true,
htmlErrors : htmlErrors htmlErrors : htmlErrors
}); });
@@ -116,9 +116,9 @@ const EditPage = React.createClass({
hasChanges : function(){ hasChanges : function(){
if(this.savedBrew){ if(this.savedBrew){
return !_.isEqual(this.state.brew, this.savedBrew) return !_.isEqual(this.state.brew, this.savedBrew);
}else{ } else {
return !_.isEqual(this.state.brew, this.props.brew) return !_.isEqual(this.state.brew, this.props.brew);
} }
return false; return false;
}, },
@@ -127,7 +127,7 @@ const EditPage = React.createClass({
if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT);
if(this.hasChanges()){ if(this.hasChanges()){
this.debounceSave(); this.debounceSave();
}else{ } else {
this.debounceSave.cancel(); this.debounceSave.cancel();
} }
}, },
@@ -142,51 +142,51 @@ const EditPage = React.createClass({
}); });
request request
.put('/api/update/' + this.props.brew.editId) .put(`/api/update/${this.props.brew.editId}`)
.send(this.state.brew) .send(this.state.brew)
.end((err, res) => { .end((err, res)=>{
if(err){ if(err){
this.setState({ this.setState({
errors : err, errors : err,
}) });
}else{ } else {
this.savedBrew = res.body; this.savedBrew = res.body;
this.setState({ this.setState({
isPending : false, isPending : false,
isSaving : false, isSaving : false,
lastUpdated : res.body.updatedAt lastUpdated : res.body.updatedAt
}) });
} }
}) });
}, },
renderSaveButton : function(){ renderSaveButton : function(){
if(this.state.errors){ if(this.state.errors){
var errMsg = ''; let errMsg = '';
try{ try {
errMsg += this.state.errors.toString() + '\n\n'; errMsg += `${this.state.errors.toString()}\n\n`;
errMsg += '```\n' + JSON.stringify(this.state.errors.response.error, null, ' ') + '\n```'; errMsg += `\`\`\`\n${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``;
}catch(e){} } catch (e){}
return <Nav.item className='save error' icon="fa-warning"> return <Nav.item className='save error' icon='fa-warning'>
Oops! Oops!
<div className='errorContainer'> <div className='errorContainer'>
Looks like there was a problem saving. <br /> Looks like there was a problem saving. <br />
Report the issue <a target='_blank' href={'https://github.com/stolksdorf/naturalcrit/issues/new?body='+ encodeURIComponent(errMsg)}> Report the issue <a target='_blank' href={`https://github.com/stolksdorf/naturalcrit/issues/new?body=${encodeURIComponent(errMsg)}`}>
here here
</a>. </a>.
</div> </div>
</Nav.item> </Nav.item>;
} }
if(this.state.isSaving){ if(this.state.isSaving){
return <Nav.item className='save' icon="fa-spinner fa-spin">saving...</Nav.item> return <Nav.item className='save' icon='fa-spinner fa-spin'>saving...</Nav.item>;
} }
if(this.state.isPending && this.hasChanges()){ if(this.state.isPending && this.hasChanges()){
return <Nav.item className='save' onClick={this.save} color='blue' icon='fa-save'>Save Now</Nav.item> return <Nav.item className='save' onClick={this.save} color='blue' icon='fa-save'>Save Now</Nav.item>;
} }
if(!this.state.isPending && !this.state.isSaving){ if(!this.state.isPending && !this.state.isSaving){
return <Nav.item className='save saved'>saved.</Nav.item> return <Nav.item className='save saved'>saved.</Nav.item>;
} }
}, },
renderNavbar : function(){ renderNavbar : function(){
@@ -198,13 +198,13 @@ const EditPage = React.createClass({
{this.renderSaveButton()} {this.renderSaveButton()}
{/*<RecentlyEdited brew={this.props.brew} />*/} {/*<RecentlyEdited brew={this.props.brew} />*/}
<ReportIssue /> <ReportIssue />
<Nav.item newTab={true} href={'/share/' + this.props.brew.shareId} color='teal' icon='fa-share-alt'> <Nav.item newTab={true} href={`/share/${this.props.brew.shareId}`} color='teal' icon='fa-share-alt'>
Share Share
</Nav.item> </Nav.item>
<PrintLink shareId={this.props.brew.shareId} /> <PrintLink shareId={this.props.brew.shareId} />
<Account /> <Account />
</Nav.section> </Nav.section>
</Navbar> </Navbar>;
}, },
render : function(){ render : function(){
@@ -223,7 +223,7 @@ const EditPage = React.createClass({
<BrewRenderer text={this.state.brew.text} errors={this.state.htmlErrors} /> <BrewRenderer text={this.state.brew.text} errors={this.state.htmlErrors} />
</SplitPane> </SplitPane>
</div> </div>
</div> </div>;
} }
}); });

View File

@@ -1,20 +1,20 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
var Navbar = require('../../navbar/navbar.jsx'); const Navbar = require('../../navbar/navbar.jsx');
var PatreonNavItem = require('../../navbar/patreon.navitem.jsx'); const PatreonNavItem = require('../../navbar/patreon.navitem.jsx');
var IssueNavItem = require('../../navbar/issue.navitem.jsx'); const IssueNavItem = require('../../navbar/issue.navitem.jsx');
var RecentNavItem = require('../../navbar/recent.navitem.jsx'); const RecentNavItem = require('../../navbar/recent.navitem.jsx');
var BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
var ErrorPage = React.createClass({ const ErrorPage = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
ver : '0.0.0', ver : '0.0.0',
errorId: '' errorId : ''
}; };
}, },
@@ -39,7 +39,7 @@ var ErrorPage = React.createClass({
<div className='content'> <div className='content'>
<BrewRenderer text={this.text} /> <BrewRenderer text={this.text} />
</div> </div>
</div> </div>;
} }
}); });

View File

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

View File

@@ -1,7 +1,7 @@
const React = require('react'); const React = require('react');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const request = require("superagent"); const request = require('superagent');
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx'); const Navbar = require('../../navbar/navbar.jsx');
@@ -18,15 +18,15 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const HomePage = React.createClass({ const HomePage = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
welcomeText : '', welcomeText : '',
ver : '0.0.0' ver : '0.0.0'
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
text: this.props.welcomeText text : this.props.welcomeText
}; };
}, },
handleSave : function(){ handleSave : function(){
@@ -36,8 +36,8 @@ const HomePage = React.createClass({
}) })
.end((err, res)=>{ .end((err, res)=>{
if(err) return; if(err) return;
var brew = res.body; const brew = res.body;
window.location = '/edit/' + brew.editId; window.location = `/edit/${brew.editId}`;
}); });
}, },
handleSplitMove : function(){ handleSplitMove : function(){
@@ -64,7 +64,7 @@ const HomePage = React.createClass({
</Nav.item> </Nav.item>
*/} */}
</Nav.section> </Nav.section>
</Navbar> </Navbar>;
}, },
render : function(){ render : function(){
@@ -78,14 +78,14 @@ const HomePage = React.createClass({
</SplitPane> </SplitPane>
</div> </div>
<div className={cx('floatingSaveButton', {show : this.props.welcomeText != this.state.text})} onClick={this.handleSave}> <div className={cx('floatingSaveButton', { show: this.props.welcomeText != this.state.text })} onClick={this.handleSave}>
Save current <i className='fa fa-save' /> Save current <i className='fa fa-save' />
</div> </div>
<a href='/new' className='floatingNewButton'> <a href='/new' className='floatingNewButton'>
Create your own <i className='fa fa-magic' /> Create your own <i className='fa fa-magic' />
</a> </a>
</div> </div>;
} }
}); });

View File

@@ -1,7 +1,7 @@
const React = require('react'); const React = require('react');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const request = require("superagent"); const request = require('superagent');
const Markdown = require('naturalcrit/markdown.js'); const Markdown = require('naturalcrit/markdown.js');
@@ -18,7 +18,7 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const KEY = 'homebrewery-new'; const KEY = 'homebrewery-new';
const NewPage = React.createClass({ const NewPage = React.createClass({
getInitialState: function() { getInitialState : function() {
return { return {
metadata : { metadata : {
title : '', title : '',
@@ -29,21 +29,21 @@ const NewPage = React.createClass({
systems : [] systems : []
}, },
text: '', text : '',
isSaving : false, isSaving : false,
errors : [] errors : []
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
const storage = localStorage.getItem(KEY); const storage = localStorage.getItem(KEY);
if(storage){ if(storage){
this.setState({ this.setState({
text : storage text : storage
}) });
} }
document.addEventListener('keydown', this.handleControlKeys); document.addEventListener('keydown', this.handleControlKeys);
}, },
componentWillUnmount: function() { componentWillUnmount : function() {
document.removeEventListener('keydown', this.handleControlKeys); document.removeEventListener('keydown', this.handleControlKeys);
}, },
@@ -96,31 +96,31 @@ const NewPage = React.createClass({
window.onbeforeunload = function(){}; window.onbeforeunload = function(){};
const brew = res.body; const brew = res.body;
localStorage.removeItem(KEY); localStorage.removeItem(KEY);
window.location = '/edit/' + brew.editId; window.location = `/edit/${brew.editId}`;
}) });
}, },
renderSaveButton : function(){ renderSaveButton : function(){
if(this.state.isSaving){ if(this.state.isSaving){
return <Nav.item icon='fa-spinner fa-spin' className='saveButton'> return <Nav.item icon='fa-spinner fa-spin' className='saveButton'>
save... save...
</Nav.item> </Nav.item>;
}else{ } else {
return <Nav.item icon='fa-save' className='saveButton' onClick={this.save}> return <Nav.item icon='fa-save' className='saveButton' onClick={this.save}>
save save
</Nav.item> </Nav.item>;
} }
}, },
print : function(){ print : function(){
localStorage.setItem('print', this.state.text); localStorage.setItem('print', this.state.text);
window.open('/print?dialog=true&local=print','_blank'); window.open('/print?dialog=true&local=print', '_blank');
}, },
renderLocalPrintButton : function(){ renderLocalPrintButton : function(){
return <Nav.item color='purple' icon='fa-file-pdf-o' onClick={this.print}> return <Nav.item color='purple' icon='fa-file-pdf-o' onClick={this.print}>
get PDF get PDF
</Nav.item> </Nav.item>;
}, },
renderNavbar : function(){ renderNavbar : function(){
@@ -136,7 +136,7 @@ const NewPage = React.createClass({
<IssueNavItem /> <IssueNavItem />
<AccountNavItem /> <AccountNavItem />
</Nav.section> </Nav.section>
</Navbar> </Navbar>;
}, },
render : function(){ render : function(){
@@ -154,7 +154,7 @@ const NewPage = React.createClass({
<BrewRenderer text={this.state.text} errors={this.state.errors} /> <BrewRenderer text={this.state.text} errors={this.state.errors} />
</SplitPane> </SplitPane>
</div> </div>
</div> </div>;
} }
}); });

View File

@@ -4,7 +4,7 @@ const cx = require('classnames');
const Markdown = require('naturalcrit/markdown.js'); const Markdown = require('naturalcrit/markdown.js');
const PrintPage = React.createClass({ const PrintPage = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
query : {}, query : {},
brew : { brew : {
@@ -13,26 +13,26 @@ const PrintPage = React.createClass({
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
brewText: this.props.brew.text brewText : this.props.brew.text
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
if(this.props.query.local){ if(this.props.query.local){
this.setState({ brewText : localStorage.getItem(this.props.query.local)}); this.setState({ brewText: localStorage.getItem(this.props.query.local) });
} }
if(this.props.query.dialog) window.print(); if(this.props.query.dialog) window.print();
}, },
renderPages : function(){ renderPages : function(){
return _.map(this.state.brewText.split('\\page'), (page, index) => { return _.map(this.state.brewText.split('\\page'), (page, index)=>{
return <div return <div
className='phb' className='phb'
id={`p${index + 1}`} id={`p${index + 1}`}
dangerouslySetInnerHTML={{__html:Markdown.render(page)}} dangerouslySetInnerHTML={{ __html: Markdown.render(page) }}
key={index} />; key={index} />;
}); });
}, },
@@ -40,7 +40,7 @@ const PrintPage = React.createClass({
render : function(){ render : function(){
return <div> return <div>
{this.renderPages()} {this.renderPages()}
</div> </div>;
} }
}); });

View File

@@ -14,7 +14,7 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const SharePage = React.createClass({ const SharePage = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
brew : { brew : {
title : '', title : '',
@@ -27,10 +27,10 @@ const SharePage = React.createClass({
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
document.addEventListener('keydown', this.handleControlKeys); document.addEventListener('keydown', this.handleControlKeys);
}, },
componentWillUnmount: function() { componentWillUnmount : function() {
document.removeEventListener('keydown', this.handleControlKeys); document.removeEventListener('keydown', this.handleControlKeys);
}, },
handleControlKeys : function(e){ handleControlKeys : function(e){
@@ -54,7 +54,7 @@ const SharePage = React.createClass({
<ReportIssue /> <ReportIssue />
{/*<RecentlyViewed brew={this.props.brew} />*/} {/*<RecentlyViewed brew={this.props.brew} />*/}
<PrintLink shareId={this.props.brew.shareId} /> <PrintLink shareId={this.props.brew.shareId} />
<Nav.item href={'/source/' + this.props.brew.shareId} color='teal' icon='fa-code'> <Nav.item href={`/source/${this.props.brew.shareId}`} color='teal' icon='fa-code'>
source source
</Nav.item> </Nav.item>
<Account /> <Account />
@@ -64,7 +64,7 @@ const SharePage = React.createClass({
<div className='content'> <div className='content'>
<BrewRenderer text={this.props.brew.text} /> <BrewRenderer text={this.props.brew.text} />
</div> </div>
</div> </div>;
} }
}); });

View File

@@ -2,10 +2,10 @@ const React = require('react');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const moment = require('moment'); const moment = require('moment');
const request = require("superagent"); const request = require('superagent');
const BrewItem = React.createClass({ const BrewItem = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
brew : { brew : {
title : '', title : '',
@@ -17,29 +17,29 @@ const BrewItem = React.createClass({
}, },
deleteBrew : function(){ deleteBrew : function(){
if(!confirm("are you sure you want to delete this brew?")) return; if(!confirm('are you sure you want to delete this brew?')) return;
if(!confirm("are you REALLY sure? You will not be able to recover it")) return; if(!confirm('are you REALLY sure? You will not be able to recover it')) return;
request.get('/api/remove/' + this.props.brew.editId) request.get(`/api/remove/${this.props.brew.editId}`)
.send() .send()
.end(function(err, res){ .end(function(err, res){
location.reload(); location.reload();
}); });
}, },
renderDeleteBrewLink: function(){ renderDeleteBrewLink : function(){
if(!this.props.brew.editId) return; if(!this.props.brew.editId) return;
return <a onClick={this.deleteBrew}> return <a onClick={this.deleteBrew}>
<i className='fa fa-trash' /> <i className='fa fa-trash' />
</a> </a>;
}, },
renderEditLink: function(){ renderEditLink : function(){
if(!this.props.brew.editId) return; if(!this.props.brew.editId) return;
return <a href={`/edit/${this.props.brew.editId}`} target='_blank'> return <a href={`/edit/${this.props.brew.editId}`} target='_blank'>
<i className='fa fa-pencil' /> <i className='fa fa-pencil' />
</a> </a>;
}, },
render : function(){ render : function(){
@@ -68,7 +68,7 @@ const BrewItem = React.createClass({
{this.renderEditLink()} {this.renderEditLink()}
{this.renderDeleteBrewLink()} {this.renderDeleteBrewLink()}
</div> </div>
</div> </div>;
} }
}); });

View File

@@ -9,16 +9,16 @@ const RecentNavItem = require('../../navbar/recent.navitem.jsx');
const Account = require('../../navbar/account.navitem.jsx'); const Account = require('../../navbar/account.navitem.jsx');
const BrewItem = require('./brewItem/brewItem.jsx'); const BrewItem = require('./brewItem/brewItem.jsx');
const brew = { // const brew = {
title : 'SUPER Long title woah now', // title : 'SUPER Long title woah now',
authors : [] // authors : []
} // };
const BREWS = _.times(25, ()=>{ return brew}); //const BREWS = _.times(25, ()=>{ return brew;});
const UserPage = React.createClass({ const UserPage = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
username : '', username : '',
brews : [] brews : []
@@ -30,14 +30,14 @@ const UserPage = React.createClass({
const sortedBrews = _.sortBy(brews, (brew)=>{ return brew.title; }); const sortedBrews = _.sortBy(brews, (brew)=>{ return brew.title; });
return _.map(sortedBrews, (brew, idx) => { return _.map(sortedBrews, (brew, idx)=>{
return <BrewItem brew={brew} key={idx}/> return <BrewItem brew={brew} key={idx}/>;
}); });
}, },
getSortedBrews : function(){ getSortedBrews : function(){
return _.groupBy(this.props.brews, (brew)=>{ return _.groupBy(this.props.brews, (brew)=>{
return (brew.published ? 'published' : 'private') return (brew.published ? 'published' : 'private');
}); });
}, },
@@ -68,7 +68,7 @@ const UserPage = React.createClass({
{this.renderPrivateBrews(brews.private)} {this.renderPrivateBrews(brews.private)}
</div> </div>
</div> </div>
</div> </div>;
} }
}); });

View File

@@ -15,7 +15,7 @@ module.exports = function(vitreum){
${vitreum.js} ${vitreum.js}
</html> </html>
`; `;
} };

View File

@@ -38,5 +38,6 @@ If you're updating dependencies, please make sure you use npm@5.6.0 and commit t
- For ambitious tasks, you should try to get your work in front of the community for feedback as soon as possible. Open a pull request as soon as you have done the minimum needed to demonstrate your idea. At this early stage, don't worry about making things perfect, or 100% complete. Add a [WIP] prefix to the title, and describe what you still need to do. This lets reviewers know not to nit-pick small details or point out improvements you already know you need to make. - For ambitious tasks, you should try to get your work in front of the community for feedback as soon as possible. Open a pull request as soon as you have done the minimum needed to demonstrate your idea. At this early stage, don't worry about making things perfect, or 100% complete. Add a [WIP] prefix to the title, and describe what you still need to do. This lets reviewers know not to nit-pick small details or point out improvements you already know you need to make.
- New features should be accompanied with tests and documentation if applicable. - New features should be accompanied with tests and documentation if applicable.
- Lint and test before submitting the pull request by running `$ npm run verify`. - Lint and test before submitting the pull request by running `$ npm run verify`.
- If your code is not passing Linting checks due to a non-fixable warning, and you feel it's valid (eg. we lint on a file being too long, but sometimes a file just _has_ to be long), add `/* eslint-disable [rule-name] */` to the top of the file. Be sure to justfiy your lint override in your PR description.
- Use a clear and descriptive title for the pull request and commits. - Use a clear and descriptive title for the pull request and commits.
- You might be asked to do changes to your pull request. There's never a need to open another pull request. [Just update the existing one.](https://github.com/RichardLitt/knowledge/blob/master/github/amending-a-commit-guide.md) - You might be asked to do changes to your pull request. There's never a need to open another pull request. [Just update the existing one.](https://github.com/RichardLitt/knowledge/blob/master/github/amending-a-commit-guide.md)

View File

@@ -20,4 +20,4 @@ Promise.resolve()
.then(livereload()) .then(livereload())
.then(server('./server.js', ['server'])) .then(server('./server.js', ['server']))
.then(console.timeEnd.bind(console, label)) .then(console.timeEnd.bind(console, label))
.catch(console.error) .catch(console.error);

View File

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

View File

@@ -1,10 +1,10 @@
const _ = require('lodash'); const _ = require('lodash');
const jwt = require('jwt-simple'); const jwt = require('jwt-simple');
const express = require("express"); const express = require('express');
const app = express(); const app = express();
app.use(express.static(__dirname + '/build'));'' app.use(express.static(`${__dirname}/build`));'';
app.use(require('body-parser').json({limit: '25mb'})); app.use(require('body-parser').json({ limit: '25mb' }));
app.use(require('cookie-parser')()); app.use(require('cookie-parser')());
const config = require('nconf') const config = require('nconf')
@@ -16,18 +16,18 @@ const config = require('nconf')
//DB //DB
require('mongoose') require('mongoose')
.connect(process.env.MONGODB_URI || process.env.MONGOLAB_URI || 'mongodb://localhost/naturalcrit') .connect(process.env.MONGODB_URI || process.env.MONGOLAB_URI || 'mongodb://localhost/naturalcrit')
.connection.on('error', () => { .connection.on('error', ()=>{
console.log('Error : Could not connect to a Mongo Database.'); console.log('Error : Could not connect to a Mongo Database.');
console.log(' If you are running locally, make sure mongodb.exe is running.'); console.log(' If you are running locally, make sure mongodb.exe is running.');
}); });
//Account MIddleware //Account MIddleware
app.use((req, res, next) => { app.use((req, res, next)=>{
if(req.cookies && req.cookies.nc_session){ if(req.cookies && req.cookies.nc_session){
try{ try {
req.account = jwt.decode(req.cookies.nc_session, config.get('secret')); req.account = jwt.decode(req.cookies.nc_session, config.get('secret'));
}catch(e){} } catch (e){}
} }
return next(); return next();
}); });
@@ -43,9 +43,9 @@ const changelogText = require('fs').readFileSync('./changelog.md', 'utf8');
//Source page //Source page
String.prototype.replaceAll = function(s,r){return this.split(s).join(r)} String.prototype.replaceAll = function(s, r){return this.split(s).join(r);};
app.get('/source/:id', (req, res)=>{ app.get('/source/:id', (req, res)=>{
HomebrewModel.get({shareId : req.params.id}) HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{ .then((brew)=>{
const text = brew.text.replaceAll('<', '&lt;').replaceAll('>', '&gt;'); const text = brew.text.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
return res.send(`<code><pre>${text}</pre></code>`); return res.send(`<code><pre>${text}</pre></code>`);
@@ -53,25 +53,25 @@ app.get('/source/:id', (req, res)=>{
.catch((err)=>{ .catch((err)=>{
console.log(err); console.log(err);
return res.status(404).send('Could not find Homebrew with that id'); return res.status(404).send('Could not find Homebrew with that id');
}) });
}); });
app.get('/user/:username', (req, res, next) => { app.get('/user/:username', (req, res, next)=>{
const fullAccess = req.account && (req.account.username == req.params.username); const fullAccess = req.account && (req.account.username == req.params.username);
HomebrewModel.getByUser(req.params.username, fullAccess) HomebrewModel.getByUser(req.params.username, fullAccess)
.then((brews) => { .then((brews)=>{
req.brews = brews; req.brews = brews;
return next(); return next();
}) })
.catch((err) => { .catch((err)=>{
console.log(err); console.log(err);
}) });
}) });
app.get('/edit/:id', (req, res, next)=>{ app.get('/edit/:id', (req, res, next)=>{
HomebrewModel.get({editId : req.params.id}) HomebrewModel.get({ editId: req.params.id })
.then((brew)=>{ .then((brew)=>{
req.brew = brew.sanatize(); req.brew = brew.sanatize();
return next(); return next();
@@ -84,7 +84,7 @@ app.get('/edit/:id', (req, res, next)=>{
//Share Page //Share Page
app.get('/share/:id', (req, res, next)=>{ app.get('/share/:id', (req, res, next)=>{
HomebrewModel.get({shareId : req.params.id}) HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{ .then((brew)=>{
return brew.increaseView(); return brew.increaseView();
}) })
@@ -100,7 +100,7 @@ app.get('/share/:id', (req, res, next)=>{
//Print Page //Print Page
app.get('/print/:id', (req, res, next)=>{ app.get('/print/:id', (req, res, next)=>{
HomebrewModel.get({shareId : req.params.id}) HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{ .then((brew)=>{
req.brew = brew.sanatize(true); req.brew = brew.sanatize(true);
return next(); return next();
@@ -115,20 +115,20 @@ app.get('/print/:id', (req, res, next)=>{
//Render Page //Render Page
const render = require('vitreum/steps/render'); const render = require('vitreum/steps/render');
const templateFn = require('./client/template.js'); const templateFn = require('./client/template.js');
app.use((req, res) => { app.use((req, res)=>{
render('homebrew', templateFn, { render('homebrew', templateFn, {
version : require('./package.json').version, version : require('./package.json').version,
url: req.originalUrl, url : req.originalUrl,
welcomeText : welcomeText, welcomeText : welcomeText,
changelog : changelogText, changelog : changelogText,
brew : req.brew, brew : req.brew,
brews : req.brews, brews : req.brews,
account : req.account account : req.account
}) })
.then((page) => { .then((page)=>{
return res.send(page) return res.send(page);
}) })
.catch((err) => { .catch((err)=>{
console.log(err); console.log(err);
return res.sendStatus(500); return res.sendStatus(500);
}); });

View File

@@ -20,9 +20,9 @@ process.env.ADMIN_KEY = process.env.ADMIN_KEY || 'admin_key';
//Removes all empty brews that are older than 3 days and that are shorter than a tweet //Removes all empty brews that are older than 3 days and that are shorter than a tweet
router.get('/api/invalid', mw.adminOnly, (req, res)=>{ router.get('/api/invalid', mw.adminOnly, (req, res)=>{
const invalidBrewQuery = HomebrewModel.find({ const invalidBrewQuery = HomebrewModel.find({
'$where' : "this.text.length < 140", '$where' : 'this.text.length < 140',
createdAt: { createdAt : {
$lt: Moment().subtract(3, 'days').toDate() $lt : Moment().subtract(3, 'days').toDate()
} }
}); });
@@ -30,26 +30,26 @@ router.get('/api/invalid', mw.adminOnly, (req, res)=>{
invalidBrewQuery.remove().exec((err, objs)=>{ invalidBrewQuery.remove().exec((err, objs)=>{
refreshCount(); refreshCount();
return res.send(200); return res.send(200);
}) });
}else{ } else {
invalidBrewQuery.exec((err, objs)=>{ invalidBrewQuery.exec((err, objs)=>{
if(err) console.log(err); if(err) console.log(err);
return res.json({ return res.json({
count : objs.length count : objs.length
}) });
}) });
} }
}); });
router.get('/admin/lookup/:id', mw.adminOnly, (req, res, next) => { router.get('/admin/lookup/:id', mw.adminOnly, (req, res, next)=>{
//search for mathcing edit id //search for mathcing edit id
//search for matching share id //search for matching share id
// search for partial match // search for partial match
HomebrewModel.findOne({ $or:[ HomebrewModel.findOne({ $or : [
{editId : { "$regex": req.params.id, "$options": "i" }}, { editId: { '$regex': req.params.id, '$options': 'i' } },
{shareId : { "$regex": req.params.id, "$options": "i" }}, { shareId: { '$regex': req.params.id, '$options': 'i' } },
]}).exec((err, brew) => { ] }).exec((err, brew)=>{
return res.json(brew); return res.json(brew);
}); });
}); });
@@ -61,19 +61,19 @@ router.get('/admin/lookup/:id', mw.adminOnly, (req, res, next) => {
const render = require('vitreum/steps/render'); const render = require('vitreum/steps/render');
const templateFn = require('../client/template.js'); const templateFn = require('../client/template.js');
router.get('/admin', function(req, res){ router.get('/admin', function(req, res){
const credentials = auth(req) const credentials = auth(req);
if (!credentials || credentials.name !== process.env.ADMIN_USER || credentials.pass !== process.env.ADMIN_PASS) { if(!credentials || credentials.name !== process.env.ADMIN_USER || credentials.pass !== process.env.ADMIN_PASS) {
res.setHeader('WWW-Authenticate', 'Basic realm="example"') res.setHeader('WWW-Authenticate', 'Basic realm="example"');
return res.status(401).send('Access denied') return res.status(401).send('Access denied');
} }
render('admin', templateFn, { render('admin', templateFn, {
url: req.originalUrl, url : req.originalUrl,
admin_key : process.env.ADMIN_KEY, admin_key : process.env.ADMIN_KEY,
}) })
.then((page) => { .then((page)=>{
return res.send(page) return res.send(page);
}) })
.catch((err) => { .catch((err)=>{
console.log(err); console.log(err);
return res.sendStatus(500); return res.sendStatus(500);
}); });

View File

@@ -1,33 +1,19 @@
const _ = require('lodash'); const _ = require('lodash');
const Moment = require('moment');
const HomebrewModel = require('./homebrew.model.js').model; const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router(); const router = require('express').Router();
// const getTopBrews = (cb)=>{
// HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
// cb(brews);
// });
// };
const getGoodBrewTitle = (text)=>{
//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('# '); const titlePos = text.indexOf('# ');
if(titlePos !== -1){ if(titlePos !== -1){
const ending = text.indexOf('\n', titlePos); const ending = text.indexOf('\n', titlePos);
return text.substring(titlePos + 2, ending); return text.substring(titlePos + 2, ending);
}else{ } else {
return _.find(text.split('\n'), (line)=>{ return _.find(text.split('\n'), (line)=>{
return line; return line;
}); });
@@ -43,7 +29,7 @@ router.post('/api', (req, res)=>{
const newHomebrew = new HomebrewModel(_.merge({}, const newHomebrew = new HomebrewModel(_.merge({},
req.body, req.body,
{authors : authors} { authors: authors }
)); ));
if(!newHomebrew.title){ if(!newHomebrew.title){
newHomebrew.title = getGoodBrewTitle(newHomebrew.text); newHomebrew.title = getGoodBrewTitle(newHomebrew.text);
@@ -54,11 +40,11 @@ router.post('/api', (req, res)=>{
return res.status(500).send(`Error while creating new brew, ${err.toString()}`); return res.status(500).send(`Error while creating new brew, ${err.toString()}`);
} }
return res.json(obj); return res.json(obj);
}) });
}); });
router.put('/api/update/:id', (req, res)=>{ router.put('/api/update/:id', (req, res)=>{
HomebrewModel.get({editId : req.params.id}) HomebrewModel.get({ editId: req.params.id })
.then((brew)=>{ .then((brew)=>{
brew = _.merge(brew, req.body); brew = _.merge(brew, req.body);
brew.updatedAt = new Date(); brew.updatedAt = new Date();
@@ -70,22 +56,22 @@ router.put('/api/update/:id', (req, res)=>{
brew.save((err, obj)=>{ brew.save((err, obj)=>{
if(err) throw err; if(err) throw err;
return res.status(200).send(obj); return res.status(200).send(obj);
}) });
}) })
.catch((err)=>{ .catch((err)=>{
console.log(err); console.log(err);
return res.status(500).send("Error while saving"); return res.status(500).send('Error while saving');
}); });
}); });
router.get('/api/remove/:id', (req, res)=>{ router.get('/api/remove/:id', (req, res)=>{
HomebrewModel.find({editId : req.params.id}, (err, objs)=>{ HomebrewModel.find({ editId: req.params.id }, (err, objs)=>{
if(!objs.length || err) return res.status(404).send("Can not find homebrew with that id"); if(!objs.length || err) return res.status(404).send('Can not find homebrew with that id');
var resEntry = objs[0]; const resEntry = objs[0];
resEntry.remove((err)=>{ resEntry.remove((err)=>{
if(err) return res.status(500).send("Error while removing"); if(err) return res.status(500).send('Error while removing');
return res.status(200).send(); return res.status(200).send();
}) });
}); });
}); });

View File

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

View File

@@ -6,16 +6,16 @@ const cx = require('classnames');
const DISMISS_KEY = 'dismiss_render_warning'; const DISMISS_KEY = 'dismiss_render_warning';
const RenderWarnings = React.createClass({ const RenderWarnings = React.createClass({
getInitialState: function() { getInitialState : function() {
return { return {
warnings: {} warnings : {}
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
this.checkWarnings(); this.checkWarnings();
window.addEventListener('resize', this.checkWarnings); window.addEventListener('resize', this.checkWarnings);
}, },
componentWillUnmount: function() { componentWillUnmount : function() {
window.removeEventListener('resize', this.checkWarnings); window.removeEventListener('resize', this.checkWarnings);
}, },
warnings : { warnings : {
@@ -43,21 +43,21 @@ const RenderWarnings = React.createClass({
}, },
checkWarnings : function(){ checkWarnings : function(){
const hideDismiss = localStorage.getItem(DISMISS_KEY); const hideDismiss = localStorage.getItem(DISMISS_KEY);
if(hideDismiss) return this.setState({warnings : {}}); if(hideDismiss) return this.setState({ warnings: {} });
this.setState({ this.setState({
warnings : _.reduce(this.warnings, (r, fn, type) => { warnings : _.reduce(this.warnings, (r, fn, type)=>{
const element = fn(); const element = fn();
if(element) r[type] = element; if(element) r[type] = element;
return r; return r;
}, {}) }, {})
}) });
}, },
dismiss : function(){ dismiss : function(){
localStorage.setItem(DISMISS_KEY, true); localStorage.setItem(DISMISS_KEY, true);
this.checkWarnings(); this.checkWarnings();
}, },
render: function(){ render : function(){
if(_.isEmpty(this.state.warnings)) return null; if(_.isEmpty(this.state.warnings)) return null;
return <div className='renderWarnings'> return <div className='renderWarnings'>
@@ -66,7 +66,7 @@ const RenderWarnings = React.createClass({
<h3>Render Warnings</h3> <h3>Render Warnings</h3>
<small>If this homebrew is rendering badly if might be because of the following:</small> <small>If this homebrew is rendering badly if might be because of the following:</small>
<ul>{_.values(this.state.warnings)}</ul> <ul>{_.values(this.state.warnings)}</ul>
</div> </div>;
} }
}); });

View File

@@ -1,11 +1,11 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var CodeMirror; let CodeMirror;
if(typeof navigator !== 'undefined'){ if(typeof navigator !== 'undefined'){
var CodeMirror = require('codemirror'); CodeMirror = require('codemirror');
//Language Modes //Language Modes
require('codemirror/mode/gfm/gfm.js'); //Github flavoured markdown require('codemirror/mode/gfm/gfm.js'); //Github flavoured markdown
@@ -13,8 +13,8 @@ if(typeof navigator !== 'undefined'){
} }
var CodeEditor = React.createClass({ const CodeEditor = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
language : '', language : '',
value : '', value : '',
@@ -24,10 +24,10 @@ var CodeEditor = React.createClass({
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
this.codeMirror = CodeMirror(this.refs.editor,{ this.codeMirror = CodeMirror(this.refs.editor, {
value : this.props.value, value : this.props.value,
lineNumbers: true, lineNumbers : true,
lineWrapping : this.props.wrap, lineWrapping : this.props.wrap,
mode : this.props.language mode : this.props.language
}); });
@@ -37,13 +37,13 @@ var CodeEditor = React.createClass({
this.updateSize(); this.updateSize();
}, },
componentWillReceiveProps: function(nextProps){ componentWillReceiveProps : function(nextProps){
if(this.codeMirror && nextProps.value !== undefined && this.codeMirror.getValue() != nextProps.value) { if(this.codeMirror && nextProps.value !== undefined && this.codeMirror.getValue() != nextProps.value) {
this.codeMirror.setValue(nextProps.value); this.codeMirror.setValue(nextProps.value);
} }
}, },
shouldComponentUpdate: function(nextProps, nextState) { shouldComponentUpdate : function(nextProps, nextState) {
return false; return false;
}, },
@@ -66,7 +66,7 @@ var CodeEditor = React.createClass({
}, },
render : function(){ render : function(){
return <div className='codeEditor' ref='editor' /> return <div className='codeEditor' ref='editor' />;
} }
}); });

View File

@@ -1,11 +1,11 @@
var _ = require('lodash'); const _ = require('lodash');
var Markdown = require('marked'); const Markdown = require('marked');
var renderer = new Markdown.Renderer(); const renderer = new Markdown.Renderer();
//Processes the markdown within an HTML block if it's just a class-wrapper //Processes the markdown within an HTML block if it's just a class-wrapper
renderer.html = function (html) { renderer.html = function (html) {
if(_.startsWith(_.trim(html), '<div') && _.endsWith(_.trim(html), '</div>')){ if(_.startsWith(_.trim(html), '<div') && _.endsWith(_.trim(html), '</div>')){
var openTag = html.substring(0, html.indexOf('>')+1); const openTag = html.substring(0, html.indexOf('>')+1);
html = html.substring(html.indexOf('>')+1); html = html.substring(html.indexOf('>')+1);
html = html.substring(0, html.lastIndexOf('</div>')); html = html.substring(0, html.lastIndexOf('</div>'));
return `${openTag} ${Markdown(html)} </div>`; return `${openTag} ${Markdown(html)} </div>`;
@@ -15,23 +15,23 @@ renderer.html = function (html) {
const tagTypes = ['div', 'span', 'a']; const tagTypes = ['div', 'span', 'a'];
const tagRegex = new RegExp('(' + const tagRegex = new RegExp(`(${
_.map(tagTypes, (type)=>{ _.map(tagTypes, (type)=>{
return `\\<${type}|\\</${type}>`; return `\\<${type}|\\</${type}>`;
}).join('|') + ')', 'g'); }).join('|')})`, 'g');
module.exports = { module.exports = {
marked : Markdown, marked : Markdown,
render : (rawBrewText)=>{ render : (rawBrewText)=>{
return Markdown(rawBrewText, {renderer : renderer}) return Markdown(rawBrewText, { renderer: renderer });
}, },
validate : (rawBrewText) => { validate : (rawBrewText)=>{
var errors = []; const errors = [];
var leftovers = _.reduce(rawBrewText.split('\n'), (acc, line, _lineNumber) => { const leftovers = _.reduce(rawBrewText.split('\n'), (acc, line, _lineNumber)=>{
var lineNumber = _lineNumber + 1; const lineNumber = _lineNumber + 1;
var matches = line.match(tagRegex); const matches = line.match(tagRegex);
if(!matches || !matches.length) return acc; if(!matches || !matches.length) return acc;
_.each(matches, (match)=>{ _.each(matches, (match)=>{
@@ -50,11 +50,11 @@ module.exports = {
text : 'Unmatched closing tag', text : 'Unmatched closing tag',
id : 'CLOSE' id : 'CLOSE'
}); });
}else if(_.last(acc).type == type){ } else if(_.last(acc).type == type){
acc.pop(); acc.pop();
}else{ } else {
errors.push({ errors.push({
line : _.last(acc).line + ' to ' + lineNumber, line : `${_.last(acc).line} to ${lineNumber}`,
type : type, type : type,
text : 'Type mismatch on closing tag', text : 'Type mismatch on closing tag',
id : 'MISMATCH' id : 'MISMATCH'
@@ -71,9 +71,9 @@ module.exports = {
errors.push({ errors.push({
line : unmatched.line, line : unmatched.line,
type : unmatched.type, type : unmatched.type,
text : "Unmatched opening tag", text : 'Unmatched opening tag',
id : 'OPEN' id : 'OPEN'
}) });
}); });
return errors; return errors;

View File

@@ -1,21 +1,21 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx'); const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
var Nav = { const Nav = {
base : React.createClass({ base : React.createClass({
render : function(){ render : function(){
return <nav> return <nav>
<div className='navContent'> <div className='navContent'>
{this.props.children} {this.props.children}
</div> </div>
</nav> </nav>;
} }
}), }),
logo : function(){ logo : function(){
return <a className='navLogo' href="http://naturalcrit.com"> return <a className='navLogo' href='http://naturalcrit.com'>
<NaturalCritIcon /> <NaturalCritIcon />
<span className='name'> <span className='name'>
Natural<span className='crit'>Crit</span> Natural<span className='crit'>Crit</span>
@@ -27,12 +27,12 @@ var Nav = {
render : function(){ render : function(){
return <div className='navSection'> return <div className='navSection'>
{this.props.children} {this.props.children}
</div> </div>;
} }
}), }),
item : React.createClass({ item : React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
icon : null, icon : null,
href : null, href : null,
@@ -45,10 +45,10 @@ var Nav = {
this.props.onClick(); this.props.onClick();
}, },
render : function(){ render : function(){
var classes = cx('navItem', this.props.color, this.props.className); const classes = cx('navItem', this.props.color, this.props.className);
var icon; let icon;
if(this.props.icon) icon = <i className={'fa ' + this.props.icon} />; if(this.props.icon) icon = <i className={`fa ${this.props.icon}`} />;
const props = _.omit(this.props, ['newTab']); const props = _.omit(this.props, ['newTab']);
@@ -56,12 +56,12 @@ var Nav = {
return <a {...props} className={classes} target={this.props.newTab ? '_blank' : '_self'} > return <a {...props} className={classes} target={this.props.newTab ? '_blank' : '_self'} >
{this.props.children} {this.props.children}
{icon} {icon}
</a> </a>;
}else{ } else {
return <div {...props} className={classes} onClick={this.handleClick} > return <div {...props} className={classes} onClick={this.handleClick} >
{this.props.children} {this.props.children}
{icon} {icon}
</div> </div>;
} }
} }
}), }),

View File

@@ -1,27 +1,27 @@
var React = require('react'); const React = require('react');
var _ = require('lodash'); const _ = require('lodash');
var cx = require('classnames'); const cx = require('classnames');
var SplitPane = React.createClass({ const SplitPane = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
storageKey : 'naturalcrit-pane-split', storageKey : 'naturalcrit-pane-split',
onDragFinish : function(){} //fires when dragging onDragFinish : function(){} //fires when dragging
}; };
}, },
getInitialState: function() { getInitialState : function() {
return { return {
size : null, size : null,
isDragging : false isDragging : false
}; };
}, },
componentDidMount: function() { componentDidMount : function() {
var paneSize = window.localStorage.getItem(this.props.storageKey); const paneSize = window.localStorage.getItem(this.props.storageKey);
if(paneSize){ if(paneSize){
this.setState({ this.setState({
size : paneSize size : paneSize
}) });
} }
}, },
@@ -30,10 +30,10 @@ var SplitPane = React.createClass({
this.props.onDragFinish(this.state.size); this.props.onDragFinish(this.state.size);
window.localStorage.setItem(this.props.storageKey, this.state.size); window.localStorage.setItem(this.props.storageKey, this.state.size);
} }
this.setState({ isDragging : false }); this.setState({ isDragging: false });
}, },
handleDown : function(){ handleDown : function(){
this.setState({ isDragging : true }); this.setState({ isDragging: true });
//this.unFocus() //this.unFocus()
}, },
handleMove : function(e){ handleMove : function(e){
@@ -42,7 +42,7 @@ var SplitPane = React.createClass({
size : e.pageX size : e.pageX
}); });
}, },
/* /*
unFocus : function() { unFocus : function() {
if(document.selection){ if(document.selection){
document.selection.empty(); document.selection.empty();
@@ -58,7 +58,7 @@ var SplitPane = React.createClass({
<i className='fa fa-circle' /> <i className='fa fa-circle' />
<i className='fa fa-circle' /> <i className='fa fa-circle' />
</div> </div>
</div> </div>;
}, },
render : function(){ render : function(){
@@ -66,7 +66,7 @@ var SplitPane = React.createClass({
<Pane ref='pane1' width={this.state.size}>{this.props.children[0]}</Pane> <Pane ref='pane1' width={this.state.size}>{this.props.children[0]}</Pane>
{this.renderDivider()} {this.renderDivider()}
<Pane ref='pane2'>{this.props.children[1]}</Pane> <Pane ref='pane2'>{this.props.children[1]}</Pane>
</div> </div>;
} }
}); });
@@ -75,23 +75,23 @@ var SplitPane = React.createClass({
var Pane = React.createClass({ const Pane = React.createClass({
getDefaultProps: function() { getDefaultProps : function() {
return { return {
width : null width : null
}; };
}, },
render : function(){ render : function(){
var styles = {}; let styles = {};
if(this.props.width){ if(this.props.width){
styles = { styles = {
flex : 'none', flex : 'none',
width : this.props.width + 'px' width : `${this.props.width}px`
} };
} }
return <div className={cx('pane', this.props.className)} style={styles}> return <div className={cx('pane', this.props.className)} style={styles}>
{this.props.children} {this.props.children}
</div> </div>;
} }
}); });

View File

@@ -1,3 +1,3 @@
module.exports = function(props){ module.exports = function(props){
return <svg version="1.1" x="0px" y="0px" viewBox="0 0 80 100" enableBackground="new 0 0 80 80"><g><g><polygon fill="#000000" points="12.9,71.4 7.6,66.1 19.3,54.4 20.7,55.8 10.4,66.1 12.9,68.6 23.2,58.3 24.6,59.7 "/></g><g><path fill="#000000" d="M29,61.6c-1.7,0-3.4-0.7-4.6-1.9l-5.1-5.1c-2.5-2.5-2.5-6.6,0-9.2l0.7-0.7L34.3,59l-0.7,0.7 C32.4,60.9,30.8,61.6,29,61.6z M20.1,47.6c-1.1,1.7-0.9,4.1,0.6,5.6l5.1,5.1c0.8,0.8,2,1.3,3.2,1.3c0.9,0,1.7-0.2,2.4-0.7 L20.1,47.6z"/></g><g><path fill="#000000" d="M12.3,74.8c-0.8,0-1.5-0.3-2-0.8l-5.2-5.2c-0.5-0.5-0.8-1.2-0.8-2c0-0.8,0.3-1.5,0.8-2 c1.1-1.1,2.9-1.1,4,0l5.2,5.2c1.1,1.1,1.1,2.9,0,4C13.8,74.5,13.1,74.8,12.3,74.8z M7.1,65.9c-0.2,0-0.4,0.1-0.6,0.2 c-0.2,0.2-0.2,0.4-0.2,0.6s0.1,0.4,0.2,0.6l5.2,5.2c0.3,0.3,0.9,0.3,1.2,0c0.3-0.3,0.3-0.8,0-1.2l-5.2-5.2 C7.5,66,7.3,65.9,7.1,65.9z"/></g><g><polygon fill="#000000" points="31.7,58.7 30.3,57.3 70,17.6 70,9 62.4,9 23.3,49.4 21.9,48 61.6,7 72,7 72,18.4 "/></g><g><rect x="46" y="6.7" transform="matrix(0.7168 0.6973 -0.6973 0.7168 35.9716 -23.568)" fill="#000000" width="2" height="51.6"/></g><g><rect x="13" y="61" fill="#000000" width="2" height="7"/></g><g><rect x="17" y="57" fill="#000000" width="2" height="7"/></g></g><g><g><polygon fill="#000000" points="68.4,71.4 56.7,59.7 58.1,58.3 68.4,68.6 70.8,66.1 60.5,55.8 61.9,54.4 73.6,66.1 "/></g><g><path fill="#000000" d="M52.2,61.6c-1.7,0-3.4-0.7-4.6-1.9L46.9,59l14.3-14.3l0.7,0.7c2.5,2.5,2.5,6.6,0,9.2l-5.1,5.1 C55.6,60.9,53.9,61.6,52.2,61.6z M49.8,58.9c0.7,0.4,1.5,0.7,2.4,0.7c1.2,0,2.3-0.5,3.2-1.3l5.1-5.1c1.5-1.5,1.7-3.8,0.6-5.6 L49.8,58.9z"/></g><g><path fill="#000000" d="M68.9,74.8c-0.8,0-1.5-0.3-2-0.8c-1.1-1.1-1.1-2.9,0-4l5.2-5.2c1.1-1.1,2.9-1.1,4,0c0.5,0.5,0.8,1.2,0.8,2 c0,0.8-0.3,1.5-0.8,2L70.9,74C70.4,74.5,69.7,74.8,68.9,74.8z M74.2,65.9c-0.2,0-0.4,0.1-0.6,0.2l-5.2,5.2c-0.3,0.3-0.3,0.8,0,1.2 c0.3,0.3,0.9,0.3,1.2,0l5.2-5.2c0.2-0.2,0.2-0.4,0.2-0.6s-0.1-0.4-0.2-0.6C74.6,66,74.4,65.9,74.2,65.9z"/></g><g><rect x="38.6" y="52.3" transform="matrix(0.7082 0.706 -0.706 0.7082 50.8397 -16.4875)" fill="#000000" width="13.4" height="2"/></g><g><polygon fill="#000000" points="30.6,39.9 9,18.4 9,7 19.7,7 41.1,29.1 39.7,30.5 18.8,9 11,9 11,17.6 32,38.5 "/></g><g><rect x="47.8" y="43.1" transform="matrix(0.6959 0.7181 -0.7181 0.6959 48.1381 -25.5246)" fill="#000000" width="12.8" height="2"/></g><g><rect x="12" y="23.1" transform="matrix(0.6974 0.7167 -0.7167 0.6974 25.1384 -11.3825)" fill="#000000" width="28.1" height="2"/></g><g><rect x="43.8" y="46.4" transform="matrix(0.6974 0.7167 -0.7167 0.6974 48.7492 -20.5985)" fill="#000000" width="10" height="2"/></g><g><rect x="66" y="61" fill="#000000" width="2" height="7"/></g><g><rect x="62" y="57" fill="#000000" width="2" height="7"/></g></g></svg>; return <svg version='1.1' x='0px' y='0px' viewBox='0 0 80 100' enableBackground='new 0 0 80 80'><g><g><polygon fill='#000000' points='12.9,71.4 7.6,66.1 19.3,54.4 20.7,55.8 10.4,66.1 12.9,68.6 23.2,58.3 24.6,59.7 '/></g><g><path fill='#000000' d='M29,61.6c-1.7,0-3.4-0.7-4.6-1.9l-5.1-5.1c-2.5-2.5-2.5-6.6,0-9.2l0.7-0.7L34.3,59l-0.7,0.7 C32.4,60.9,30.8,61.6,29,61.6z M20.1,47.6c-1.1,1.7-0.9,4.1,0.6,5.6l5.1,5.1c0.8,0.8,2,1.3,3.2,1.3c0.9,0,1.7-0.2,2.4-0.7 L20.1,47.6z'/></g><g><path fill='#000000' d='M12.3,74.8c-0.8,0-1.5-0.3-2-0.8l-5.2-5.2c-0.5-0.5-0.8-1.2-0.8-2c0-0.8,0.3-1.5,0.8-2 c1.1-1.1,2.9-1.1,4,0l5.2,5.2c1.1,1.1,1.1,2.9,0,4C13.8,74.5,13.1,74.8,12.3,74.8z M7.1,65.9c-0.2,0-0.4,0.1-0.6,0.2 c-0.2,0.2-0.2,0.4-0.2,0.6s0.1,0.4,0.2,0.6l5.2,5.2c0.3,0.3,0.9,0.3,1.2,0c0.3-0.3,0.3-0.8,0-1.2l-5.2-5.2 C7.5,66,7.3,65.9,7.1,65.9z'/></g><g><polygon fill='#000000' points='31.7,58.7 30.3,57.3 70,17.6 70,9 62.4,9 23.3,49.4 21.9,48 61.6,7 72,7 72,18.4 '/></g><g><rect x='46' y='6.7' transform='matrix(0.7168 0.6973 -0.6973 0.7168 35.9716 -23.568)' fill='#000000' width='2' height='51.6'/></g><g><rect x='13' y='61' fill='#000000' width='2' height='7'/></g><g><rect x='17' y='57' fill='#000000' width='2' height='7'/></g></g><g><g><polygon fill='#000000' points='68.4,71.4 56.7,59.7 58.1,58.3 68.4,68.6 70.8,66.1 60.5,55.8 61.9,54.4 73.6,66.1 '/></g><g><path fill='#000000' d='M52.2,61.6c-1.7,0-3.4-0.7-4.6-1.9L46.9,59l14.3-14.3l0.7,0.7c2.5,2.5,2.5,6.6,0,9.2l-5.1,5.1 C55.6,60.9,53.9,61.6,52.2,61.6z M49.8,58.9c0.7,0.4,1.5,0.7,2.4,0.7c1.2,0,2.3-0.5,3.2-1.3l5.1-5.1c1.5-1.5,1.7-3.8,0.6-5.6 L49.8,58.9z'/></g><g><path fill='#000000' d='M68.9,74.8c-0.8,0-1.5-0.3-2-0.8c-1.1-1.1-1.1-2.9,0-4l5.2-5.2c1.1-1.1,2.9-1.1,4,0c0.5,0.5,0.8,1.2,0.8,2 c0,0.8-0.3,1.5-0.8,2L70.9,74C70.4,74.5,69.7,74.8,68.9,74.8z M74.2,65.9c-0.2,0-0.4,0.1-0.6,0.2l-5.2,5.2c-0.3,0.3-0.3,0.8,0,1.2 c0.3,0.3,0.9,0.3,1.2,0l5.2-5.2c0.2-0.2,0.2-0.4,0.2-0.6s-0.1-0.4-0.2-0.6C74.6,66,74.4,65.9,74.2,65.9z'/></g><g><rect x='38.6' y='52.3' transform='matrix(0.7082 0.706 -0.706 0.7082 50.8397 -16.4875)' fill='#000000' width='13.4' height='2'/></g><g><polygon fill='#000000' points='30.6,39.9 9,18.4 9,7 19.7,7 41.1,29.1 39.7,30.5 18.8,9 11,9 11,17.6 32,38.5 '/></g><g><rect x='47.8' y='43.1' transform='matrix(0.6959 0.7181 -0.7181 0.6959 48.1381 -25.5246)' fill='#000000' width='12.8' height='2'/></g><g><rect x='12' y='23.1' transform='matrix(0.6974 0.7167 -0.7167 0.6974 25.1384 -11.3825)' fill='#000000' width='28.1' height='2'/></g><g><rect x='43.8' y='46.4' transform='matrix(0.6974 0.7167 -0.7167 0.6974 48.7492 -20.5985)' fill='#000000' width='10' height='2'/></g><g><rect x='66' y='61' fill='#000000' width='2' height='7'/></g><g><rect x='62' y='57' fill='#000000' width='2' height='7'/></g></g></svg>;
} };

View File

@@ -1,9 +1,9 @@
var React = require('react'); const React = require('react');
module.exports = function(props){ module.exports = function(props){
return <svg version="1.1" x="0px" y="0px" viewBox="0 0 90 112.5" enableBackground="new 0 0 90 90" > return <svg version='1.1' x='0px' y='0px' viewBox='0 0 90 112.5' enableBackground='new 0 0 90 90' >
<path d="M25.363,25.54c0,1.906,8.793,3.454,19.636,3.454c10.848,0,19.638-1.547,19.638-3.454c0-1.12-3.056-2.117-7.774-2.75 c-1.418,1.891-3.659,3.133-6.208,3.133c-2.85,0-5.315-1.547-6.67-3.833C33.617,22.185,25.363,23.692,25.363,25.54z"/><path d="M84.075,54.142c0-8.68-2.868-17.005-8.144-23.829c1.106-1.399,1.41-2.771,1.41-3.854c0-6.574-10.245-9.358-19.264-10.533 c0.209,0.706,0.359,1.439,0.359,2.215c0,0.09-0.022,0.17-0.028,0.26l0,0c-0.028,0.853-0.195,1.667-0.479,2.429 c9.106,1.282,14.508,3.754,14.508,5.63c0,2.644-10.688,6.486-27.439,6.486c-16.748,0-27.438-3.842-27.438-6.486 c0-2.542,9.904-6.183,25.559-6.459c-0.098-0.396-0.159-0.807-0.2-1.223c0.006,0,0.013,0,0.017,0 c-0.017-0.213-0.063-0.417-0.063-0.636c0-1.084,0.226-2.119,0.628-3.058c-6.788,0.129-30.846,1.299-30.846,11.376 c0,1.083,0.305,2.455,1.411,3.854c-5.276,6.823-8.145,15.149-8.145,23.829c0,11.548,5.187,20.107,14.693,25.115 c-0.902,3.146-1.391,7.056,1.111,8.181c2.626,1.178,5.364-2.139,7.111-5.005c4.73,1.261,10.13,1.923,16.161,1.923 c6.034,0,11.428-0.661,16.158-1.922c1.75,2.865,4.493,6.18,7.112,5.004c2.504-1.123,2.014-5.035,1.113-8.179 C78.889,74.249,84.075,65.689,84.075,54.142z M70.39,31.392c5.43,6.046,8.78,14,8.78,22.75c0,20.919-18.582,25.309-34.171,25.309 c-15.587,0-34.17-4.39-34.17-25.309c0-8.75,3.35-16.7,8.781-22.753c5.561,2.643,15.502,4.009,25.389,4.009 C54.886,35.397,64.829,34.031,70.39,31.392z"/><path d="M50.654,23.374c2.892,0,5.234-2.341,5.234-5.233c0-2.887-2.343-5.23-5.234-5.23c-2.887,0-5.231,2.343-5.231,5.23 C45.423,21.032,47.768,23.374,50.654,23.374z"/> <path d='M25.363,25.54c0,1.906,8.793,3.454,19.636,3.454c10.848,0,19.638-1.547,19.638-3.454c0-1.12-3.056-2.117-7.774-2.75 c-1.418,1.891-3.659,3.133-6.208,3.133c-2.85,0-5.315-1.547-6.67-3.833C33.617,22.185,25.363,23.692,25.363,25.54z'/><path d='M84.075,54.142c0-8.68-2.868-17.005-8.144-23.829c1.106-1.399,1.41-2.771,1.41-3.854c0-6.574-10.245-9.358-19.264-10.533 c0.209,0.706,0.359,1.439,0.359,2.215c0,0.09-0.022,0.17-0.028,0.26l0,0c-0.028,0.853-0.195,1.667-0.479,2.429 c9.106,1.282,14.508,3.754,14.508,5.63c0,2.644-10.688,6.486-27.439,6.486c-16.748,0-27.438-3.842-27.438-6.486 c0-2.542,9.904-6.183,25.559-6.459c-0.098-0.396-0.159-0.807-0.2-1.223c0.006,0,0.013,0,0.017,0 c-0.017-0.213-0.063-0.417-0.063-0.636c0-1.084,0.226-2.119,0.628-3.058c-6.788,0.129-30.846,1.299-30.846,11.376 c0,1.083,0.305,2.455,1.411,3.854c-5.276,6.823-8.145,15.149-8.145,23.829c0,11.548,5.187,20.107,14.693,25.115 c-0.902,3.146-1.391,7.056,1.111,8.181c2.626,1.178,5.364-2.139,7.111-5.005c4.73,1.261,10.13,1.923,16.161,1.923 c6.034,0,11.428-0.661,16.158-1.922c1.75,2.865,4.493,6.18,7.112,5.004c2.504-1.123,2.014-5.035,1.113-8.179 C78.889,74.249,84.075,65.689,84.075,54.142z M70.39,31.392c5.43,6.046,8.78,14,8.78,22.75c0,20.919-18.582,25.309-34.171,25.309 c-15.587,0-34.17-4.39-34.17-25.309c0-8.75,3.35-16.7,8.781-22.753c5.561,2.643,15.502,4.009,25.389,4.009 C54.886,35.397,64.829,34.031,70.39,31.392z'/><path d='M50.654,23.374c2.892,0,5.234-2.341,5.234-5.233c0-2.887-2.343-5.23-5.234-5.23c-2.887,0-5.231,2.343-5.231,5.23 C45.423,21.032,47.768,23.374,50.654,23.374z'/>
<circle cx="62.905" cy="10.089" r="3.595"/> <circle cx='62.905' cy='10.089' r='3.595'/>
<circle cx="52.616" cy="5.048" r="2.73"/> <circle cx='52.616' cy='5.048' r='2.73'/>
</svg>; </svg>;
}; };

View File

@@ -1,5 +1,5 @@
var React = require('react'); const React = require('react');
module.exports = function(props){ module.exports = function(props){
return <svg version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enableBackground="new 0 0 100 100"><path d="M80.644,87.982l16.592-41.483c0.054-0.128,0.088-0.26,0.108-0.394c0.006-0.039,0.007-0.077,0.011-0.116 c0.007-0.087,0.008-0.174,0.002-0.26c-0.003-0.046-0.007-0.091-0.014-0.137c-0.014-0.089-0.036-0.176-0.063-0.262 c-0.012-0.034-0.019-0.069-0.031-0.103c-0.047-0.118-0.106-0.229-0.178-0.335c-0.004-0.006-0.006-0.012-0.01-0.018L67.999,3.358 c-0.01-0.013-0.003-0.026-0.013-0.04L68,3.315V4c0,0-0.033,0-0.037,0c-0.403-1-1.094-1.124-1.752-0.976 c0,0.004-0.004-0.012-0.007-0.012C66.201,3.016,66.194,3,66.194,3H66.19h-0.003h-0.003h-0.004h-0.003c0,0-0.004,0-0.007,0 s-0.003-0.151-0.007-0.151L20.495,15.227c-0.025,0.007-0.046-0.019-0.071-0.011c-0.087,0.028-0.172,0.041-0.253,0.083 c-0.054,0.027-0.102,0.053-0.152,0.085c-0.051,0.033-0.101,0.061-0.147,0.099c-0.044,0.036-0.084,0.073-0.124,0.113 c-0.048,0.048-0.093,0.098-0.136,0.152c-0.03,0.039-0.059,0.076-0.085,0.117c-0.046,0.07-0.084,0.145-0.12,0.223 c-0.011,0.023-0.027,0.042-0.036,0.066L2.911,57.664C2.891,57.715,3,57.768,3,57.82v0.002c0,0.186,0,0.375,0,0.562 c0,0.004,0,0.004,0,0.008c0,0,0,0,0,0.002c0,0,0,0,0,0.004v0.004v0.002c0,0.074-0.002,0.15,0.012,0.223 C3.015,58.631,3,58.631,3,58.633c0,0.004,0,0.004,0,0.008c0,0,0,0,0,0.002c0,0,0,0,0,0.004v0.004c0,0,0,0,0,0.002v0.004 c0,0.191-0.046,0.377,0.06,0.545c0-0.002-0.03,0.004-0.03,0.004c0,0.004-0.03,0.004-0.03,0.004c0,0.002,0,0.002,0,0.002 l-0.045,0.004c0.03,0.047,0.036,0.09,0.068,0.133l29.049,37.359c0.002,0.004,0,0.006,0.002,0.01c0.002,0.002,0,0.004,0.002,0.008 c0.006,0.008,0.014,0.014,0.021,0.021c0.024,0.029,0.052,0.051,0.078,0.078c0.027,0.029,0.053,0.057,0.082,0.082 c0.03,0.027,0.055,0.062,0.086,0.088c0.026,0.02,0.057,0.033,0.084,0.053c0.04,0.027,0.081,0.053,0.123,0.076 c0.005,0.004,0.01,0.008,0.016,0.01c0.087,0.051,0.176,0.09,0.269,0.123c0.042,0.014,0.082,0.031,0.125,0.043 c0.021,0.006,0.041,0.018,0.062,0.021c0.123,0.027,0.249,0.043,0.375,0.043c0.099,0,0.202-0.012,0.304-0.027l45.669-8.303 c0.057-0.01,0.108-0.021,0.163-0.037C79.547,88.992,79.562,89,79.575,89c0.004,0,0.004,0,0.004,0c0.021,0,0.039-0.027,0.06-0.035 c0.041-0.014,0.08-0.034,0.12-0.052c0.021-0.01,0.044-0.019,0.064-0.03c0.017-0.01,0.026-0.015,0.033-0.017 c0.014-0.008,0.023-0.021,0.037-0.028c0.14-0.078,0.269-0.174,0.38-0.285c0.014-0.016,0.024-0.034,0.038-0.048 c0.109-0.119,0.201-0.252,0.271-0.398c0.006-0.01,0.016-0.018,0.021-0.029c0.004-0.008,0.008-0.017,0.011-0.026 c0.002-0.004,0.003-0.006,0.005-0.01C80.627,88.021,80.635,88.002,80.644,87.982z M77.611,84.461L48.805,66.453l32.407-25.202 L77.611,84.461z M46.817,63.709L35.863,23.542l43.818,14.608L46.817,63.709z M84.668,40.542l8.926,5.952l-11.902,29.75 L84.668,40.542z M89.128,39.446L84.53,36.38l-6.129-12.257L89.128,39.446z M79.876,34.645L37.807,20.622L65.854,6.599L79.876,34.645 z M33.268,19.107l-6.485-2.162l23.781-6.487L33.268,19.107z M21.92,18.895l8.67,2.891L10.357,47.798L21.92,18.895z M32.652,24.649 l10.845,39.757L7.351,57.178L32.652,24.649z M43.472,67.857L32.969,92.363L8.462,60.855L43.472,67.857z M46.631,69.09l27.826,17.393 l-38.263,6.959L46.631,69.09z"></path></svg>; return <svg version='1.1' x='0px' y='0px' viewBox='0 0 100 100' enableBackground='new 0 0 100 100'><path d='M80.644,87.982l16.592-41.483c0.054-0.128,0.088-0.26,0.108-0.394c0.006-0.039,0.007-0.077,0.011-0.116 c0.007-0.087,0.008-0.174,0.002-0.26c-0.003-0.046-0.007-0.091-0.014-0.137c-0.014-0.089-0.036-0.176-0.063-0.262 c-0.012-0.034-0.019-0.069-0.031-0.103c-0.047-0.118-0.106-0.229-0.178-0.335c-0.004-0.006-0.006-0.012-0.01-0.018L67.999,3.358 c-0.01-0.013-0.003-0.026-0.013-0.04L68,3.315V4c0,0-0.033,0-0.037,0c-0.403-1-1.094-1.124-1.752-0.976 c0,0.004-0.004-0.012-0.007-0.012C66.201,3.016,66.194,3,66.194,3H66.19h-0.003h-0.003h-0.004h-0.003c0,0-0.004,0-0.007,0 s-0.003-0.151-0.007-0.151L20.495,15.227c-0.025,0.007-0.046-0.019-0.071-0.011c-0.087,0.028-0.172,0.041-0.253,0.083 c-0.054,0.027-0.102,0.053-0.152,0.085c-0.051,0.033-0.101,0.061-0.147,0.099c-0.044,0.036-0.084,0.073-0.124,0.113 c-0.048,0.048-0.093,0.098-0.136,0.152c-0.03,0.039-0.059,0.076-0.085,0.117c-0.046,0.07-0.084,0.145-0.12,0.223 c-0.011,0.023-0.027,0.042-0.036,0.066L2.911,57.664C2.891,57.715,3,57.768,3,57.82v0.002c0,0.186,0,0.375,0,0.562 c0,0.004,0,0.004,0,0.008c0,0,0,0,0,0.002c0,0,0,0,0,0.004v0.004v0.002c0,0.074-0.002,0.15,0.012,0.223 C3.015,58.631,3,58.631,3,58.633c0,0.004,0,0.004,0,0.008c0,0,0,0,0,0.002c0,0,0,0,0,0.004v0.004c0,0,0,0,0,0.002v0.004 c0,0.191-0.046,0.377,0.06,0.545c0-0.002-0.03,0.004-0.03,0.004c0,0.004-0.03,0.004-0.03,0.004c0,0.002,0,0.002,0,0.002 l-0.045,0.004c0.03,0.047,0.036,0.09,0.068,0.133l29.049,37.359c0.002,0.004,0,0.006,0.002,0.01c0.002,0.002,0,0.004,0.002,0.008 c0.006,0.008,0.014,0.014,0.021,0.021c0.024,0.029,0.052,0.051,0.078,0.078c0.027,0.029,0.053,0.057,0.082,0.082 c0.03,0.027,0.055,0.062,0.086,0.088c0.026,0.02,0.057,0.033,0.084,0.053c0.04,0.027,0.081,0.053,0.123,0.076 c0.005,0.004,0.01,0.008,0.016,0.01c0.087,0.051,0.176,0.09,0.269,0.123c0.042,0.014,0.082,0.031,0.125,0.043 c0.021,0.006,0.041,0.018,0.062,0.021c0.123,0.027,0.249,0.043,0.375,0.043c0.099,0,0.202-0.012,0.304-0.027l45.669-8.303 c0.057-0.01,0.108-0.021,0.163-0.037C79.547,88.992,79.562,89,79.575,89c0.004,0,0.004,0,0.004,0c0.021,0,0.039-0.027,0.06-0.035 c0.041-0.014,0.08-0.034,0.12-0.052c0.021-0.01,0.044-0.019,0.064-0.03c0.017-0.01,0.026-0.015,0.033-0.017 c0.014-0.008,0.023-0.021,0.037-0.028c0.14-0.078,0.269-0.174,0.38-0.285c0.014-0.016,0.024-0.034,0.038-0.048 c0.109-0.119,0.201-0.252,0.271-0.398c0.006-0.01,0.016-0.018,0.021-0.029c0.004-0.008,0.008-0.017,0.011-0.026 c0.002-0.004,0.003-0.006,0.005-0.01C80.627,88.021,80.635,88.002,80.644,87.982z M77.611,84.461L48.805,66.453l32.407-25.202 L77.611,84.461z M46.817,63.709L35.863,23.542l43.818,14.608L46.817,63.709z M84.668,40.542l8.926,5.952l-11.902,29.75 L84.668,40.542z M89.128,39.446L84.53,36.38l-6.129-12.257L89.128,39.446z M79.876,34.645L37.807,20.622L65.854,6.599L79.876,34.645 z M33.268,19.107l-6.485-2.162l23.781-6.487L33.268,19.107z M21.92,18.895l8.67,2.891L10.357,47.798L21.92,18.895z M32.652,24.649 l10.845,39.757L7.351,57.178L32.652,24.649z M43.472,67.857L32.969,92.363L8.462,60.855L43.472,67.857z M46.631,69.09l27.826,17.393 l-38.263,6.959L46.631,69.09z'></path></svg>;
}; };