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

Compare commits

..

509 Commits

Author SHA1 Message Date
Trevor Buckner
6a2e39355c V2.10.7 (#1215)
* Fix for box-shadow/border-image issues on blockquotes in new columns (#1179)

* Fix for issues with box-shadow and border-images on blockquotes when they appear at the top of columns other than the first.

* Update phb.style.less

Change to `-webkit-transform` IAW Github PR discussion

* Add comment

Co-authored-by: Sean Robertson <srobertson@fqnz.co.nz>
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>

* Title will always return *something*, and not CSS (#1214)

* Bump codemirror from 5.59.1 to 5.59.2 (#1200)

Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.59.1 to 5.59.2.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.59.1...5.59.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump mongoose from 5.11.9 to 5.11.13 (#1199)

Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.11.9 to 5.11.13.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.11.9...5.11.13)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump fs-extra from 9.0.1 to 9.1.0 (#1197)

Bumps [fs-extra](https://github.com/jprichardson/node-fs-extra) from 9.0.1 to 9.1.0.
- [Release notes](https://github.com/jprichardson/node-fs-extra/releases)
- [Changelog](https://github.com/jprichardson/node-fs-extra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jprichardson/node-fs-extra/compare/9.0.1...9.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump eslint from 7.17.0 to 7.18.0 (#1195)

Bumps [eslint](https://github.com/eslint/eslint) from 7.17.0 to 7.18.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.17.0...v7.18.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump nconf from 0.11.0 to 0.11.1 (#1190)

Bumps [nconf](https://github.com/flatiron/nconf) from 0.11.0 to 0.11.1.
- [Release notes](https://github.com/flatiron/nconf/releases)
- [Changelog](https://github.com/indexzero/nconf/blob/master/CHANGELOG.md)
- [Commits](https://github.com/flatiron/nconf/compare/v0.11.0...v0.11.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump googleapis from 66.0.0 to 67.0.0 (#1189)

Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 66.0.0 to 67.0.0.
- [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases)
- [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/v66.0.0...v67.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>

* Bump express-static-gzip from 2.1.0 to 2.1.1 (#1180)

Bumps [express-static-gzip](https://github.com/tkoenig89/express-static-gzip) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/tkoenig89/express-static-gzip/releases)
- [Commits](https://github.com/tkoenig89/express-static-gzip/compare/v2.1.0...v2.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>

* use fa-info-circle instead of fa-bars (#1109)

* use fa-info-circle instead of fa-bars

* Change Metadata button to Info Icon with Text

Co-authored-by: Trevor Buckner <calculuschild@gmail.com>

* Up Version

Co-authored-by: G.Ambatte <sean@robertson-family.nz>
Co-authored-by: Sean Robertson <srobertson@fqnz.co.nz>
Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: ericscheid <ericscheid@users.noreply.github.com>
2021-01-25 21:47:36 -05:00
G.Ambatte
ba600f5da6 FreeBSD installation (#1165)
* Update server.js

Eliminate requirement to CD into project directory prior to running `npm start` or `node server.js`.

* Add rc.d daemon script

Adds a RC.d daemon script to control the HomeBrewery status. Based on Andrew Pearson's Node-RED script for the same purpose.

* Create install.sh

* Update install.sh

* Update install.sh

Switch to latest version of MongoDB

* Add config items

Add config items and default values:
- web_port (8001)
- environment (local)

* Remove environment variables from rc.d

Remove environment variables from rc.d as they are now in config/default.json.

* Update server.js

Change to setting NODE_ENV; default to 'local' if not set via environment variable or in the config file.

* Remove --force option

Remove --force option from `npm audit fix`. While this has not caused any issues to date, there is no guarantee that it will continue to be the case.

* Create README.FREEBSD.md

Initial write up of install instructions. Includes instruction to `wget` from the `naturalcrit/homebrewery` project rather than the FreeBSD fork, on the assumption that the PR will be merged at some point.

* Update install.sh

Change to main project repo, on assumption that the PR will be merged at some point

* Change install directory

Change of install directory to `/usr/local/homebrewery`

* Update homebrewery

Add `dev_mode` to the HomeBrewery service, which starts the HomeBrewery project in live rebuild mode.

* Update server.js

Eliminating unnecessary debugging code, reducing line count and making lint happy :)

* Update server.js

Lint is happy, now attempting to pacify CircleCI

* Move NODE_ENV to service file; remove from server.js and config/default.json

* Remove hanging comma in config.json
Change default port assignment from 8000 to 8001 in config.json and FreeBSD service config

* Add link to FreeBSD install documenation in the main README.md file

* Fix for MongoDB package update breaking install script
Change to enable use of rcvars for NODE_ENV and PORT
2021-01-10 17:44:49 -05:00
G.Ambatte
2a340b7a65 Update coverpage.gen.js (#1173)
Fix for page numbers, footers, and footnotes being on the wrong side of the page after a cover page has been inserted - as per Reddit post: https://redd.it/kncoe7
2021-01-09 02:03:08 -05:00
Alexey Sachkov
2f27aeb77f Update README (#1181)
Added circleci build badge.
Added contribution section.
Refactored the source to fit (mostly) into
80-column limit so it can be nicely read in
terminal applications
2021-01-09 01:24:03 -05:00
dependabot[bot]
1a0f29b6ef Bump ini from 1.3.5 to 1.3.8 (#1153)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-02 18:38:11 -05:00
Trevor Buckner
39b160e202 Update Dependencies (#1178)
* Update Dependencies

* Up version number

* Typo
2021-01-02 18:30:11 -05:00
Trevor Buckner
aa065fa4d8 Do not Server-Side Render the markdown (#1177)
1) Rendering is fast enough on the client we don't need to provide SSR for the brew contents.
2) This leaves our server open to REDOS attacks if users create ridiculously long single lines of text. The Markdown parser slows down with exponential time which becomes noticeable at 10,000+ characters in one line, and at 200,000+ characters will stall the server and eventually crash.
3) This now shows a nice loading circle for the half-second that a page takes to render. If a user tries to load a huge line of text the loading circle will be there instead of a blank white page.
2021-01-02 16:54:50 -05:00
Alexey Sachkov
19ca1db674 Tweak spelling of username on user page (#1175)
Fixes #575
2020-12-31 14:12:16 -05:00
Alexey Sachkov
a58384d8d1 Cleanup unneeded "require" (#1169) 2020-12-26 21:29:49 -05:00
Trevor Buckner
8e1951ba67 Access google files with Service Account
Using API key is triggering "automation" warnings from Google
2020-12-25 15:15:51 -05:00
Trevor Buckner
3dba731dd7 Popup confirmation when switching between google and hb (#1155)
* Popup confirmation when switching between google and hb

* Allow closing error popup.

* Up version.
2020-12-18 23:51:08 -05:00
Trevor Buckner
f8f19efcaa update several dependencies (#1154) 2020-12-11 23:22:02 -05:00
Trevor Buckner
980fdf5ad1 Small bug causing crashes with Tags (#1152)
Also: Force Google to always send to trash instead of fully deleting.

Remove unneeded console.logs to speed up server.
2020-12-11 16:18:28 -05:00
Trevor Buckner
e127855d84 Add detailed MongoDB Install instructions 2020-11-29 23:20:28 -05:00
Trevor Buckner
8ffea70b2f Remove metadata on the Homepage (#1121)
* Remove the metadata button on home page

1) Hopefully make it more clear that the Homepage is meant as a sandbox to test out the site, not a real brew that will be saved.

2) Also, avoid errors resulting from trying to modify the metadata of a brew that doesn't really exist yet.

* Lint
2020-11-27 21:14:15 -05:00
Trevor Buckner
3fbddd2e41 Revert Blockquote list font size
Was technically correct, but messed up some existing brews.
2020-11-27 20:57:01 -05:00
Trevor Buckner
5a17697e7e Merge pull request #1118 from naturalcrit/fixGoogleMetadata
Fix metadata in Google docs
2020-11-25 13:26:14 -05:00
Trevor Buckner
6f66fdc6d6 Bump version to 2.10.4 2020-11-25 13:24:10 -05:00
Trevor Buckner
a29fdb43c9 Merge pull request #1108 from ericscheid/fix-inconsistent-list-item-sizing-#1085
line-heights consistent in bq li (#1085)
2020-11-23 19:21:46 -05:00
Trevor Buckner
7462e66858 Fix metadata in Google docs
Update view counts via service account since modifying another users' file properties requires increased permission scope
2020-11-22 23:53:34 -05:00
Eric Scheid
d9364cf60a line-heights consistent in bq li (#1085) 2020-11-23 03:13:25 +11:00
Trevor Buckner
b0375bddd1 Merge pull request #1099 from naturalcrit/optimize-page-load
Fix broken API calls
2020-11-12 17:31:07 -05:00
Trevor Buckner
56795afabb Fix broken API calls
expressStaticGzip was serving empty index files when it saw requests ending in '/' like 'api/newGoogle/'
2020-11-12 17:17:02 -05:00
Trevor Buckner
acf9f464f0 Merge pull request #1098 from naturalcrit/optimize-page-load
Compress static files
2020-11-11 22:22:45 -05:00
Trevor Buckner
74c615f156 Cleanup 2020-11-11 22:21:27 -05:00
Trevor Buckner
133af4ea2c lint 2020-11-11 22:02:17 -05:00
Trevor Buckner
4182c79354 Use a library instead. Checks that the browser is compatible first. 2020-11-11 22:01:42 -05:00
Trevor Buckner
759d986188 lint 2020-11-11 21:41:48 -05:00
Trevor Buckner
600ca90fc0 Serve compressed static files if available 2020-11-11 21:19:49 -05:00
Trevor Buckner
3b52888877 Merge pull request #1078 from RKuerten/update-b-i
Updated makeBold and makeItalic functions
2020-10-28 22:26:49 -04:00
Trevor Buckner
e23120a4c6 Reduce duplicate code 2020-10-28 22:25:25 -04:00
Trevor Buckner
38d47f6aa1 Remove console.log slowing things down. 2020-10-27 11:24:07 -04:00
Trevor Buckner
3a25123d7b Fix internal section link in iFrame 2020-10-27 11:06:07 -04:00
Rodrigo Kuerten
19c04e125a Linting? 2020-10-26 23:09:36 -03:00
Rodrigo Kuerten
8a13387874 Updated makeBold and makeItalic functions to center cursor when selection is empty 2020-10-26 22:49:09 -03:00
Trevor Buckner
6c813ddab1 Fix links breaking in iFrame 2020-10-26 13:50:37 -04:00
Trevor Buckner
965870f8ed Merge pull request #1075 from RKuerten/fix-brew-title
Changed page title to show the brew title
2020-10-26 13:38:17 -04:00
Trevor Buckner
8add76fb50 Linting 2020-10-26 13:27:37 -04:00
Rodrigo Kuerten
af4ec3d096 Added back the original codeEditor file 2020-10-25 14:13:23 -03:00
Rodrigo Kuerten
b908cd7cbd Changed page title to show homebrew title whenever possible 2020-10-25 13:55:10 -03:00
Rodrigo Kuerten
6309ec0bfa Updated makeBold and makeItalic functions to center cursor when selection is empty 2020-10-25 13:55:10 -03:00
Trevor Buckner
45d1bef302 Update README.md
Couple of extra steps. Need to fill out the MongoDB setup
2020-10-24 23:41:21 -04:00
Trevor Buckner
7d9e1aad83 Merge pull request #1074 from naturalcrit/BrewRendererIframe
Brew renderer iframe
2020-10-24 23:27:17 -04:00
Trevor Buckner
aa2d1f3bc9 Fix missing FontAwesome fonts in iFrame 2020-10-22 12:44:18 -04:00
Trevor Buckner
f6bd1ef513 Update Version number 2020-10-22 10:36:55 -04:00
Trevor Buckner
c75ac3c0f5 Render brew in Iframe to not crash editor 2020-10-21 20:39:43 -04:00
Trevor Buckner
ac2d6fe9a8 Fix share uncompressing views... again 2020-10-19 17:36:58 -04:00
Trevor Buckner
40d120d875 Woops!
Accidentally broke sharing.
2020-10-19 16:56:34 -04:00
Trevor Buckner
5e2fdcf1e9 Merge pull request #1067 from naturalcrit/compress
Fix Shared View uncompressing brew text
2020-10-19 16:44:42 -04:00
Trevor Buckner
57c8c24b20 Lint 2020-10-19 16:38:26 -04:00
Trevor Buckner
460d3fe111 Prevent share view uncompressing text. 2020-10-19 16:37:01 -04:00
Trevor Buckner
1d50cbf684 Update admin.api.js 2020-10-19 16:24:19 -04:00
Trevor Buckner
3a250d3da4 Update admin.api.js 2020-10-19 16:15:41 -04:00
Trevor Buckner
d05b819ff2 Fix recent links when transferring to Google Brews 2020-10-19 15:48:44 -04:00
Trevor Buckner
fcb3f9ca26 Merge pull request #1055 from naturalcrit/updateDependencies
update dependencies
2020-10-12 15:38:22 -04:00
Trevor Buckner
69b42ee6e0 update dependencies 2020-10-12 15:35:01 -04:00
Trevor Buckner
77973f0037 v2.10.1 - Fix brews lost on back button 2020-10-12 14:30:42 -04:00
Trevor Buckner
7f8f39916d Merge pull request #1050 from naturalcrit/googleDriveIntegration
Google drive integration
2020-10-12 14:18:10 -04:00
Trevor Buckner
cc8bf6744b Move Patreon button 2020-10-08 16:12:12 -04:00
Trevor Buckner
bf17d6894f use NanoId as function in Mongoose default
For some reason, Mongoose will reuse the old ID generated if you call nanoid directly. If you wrap it in a function, it will call it new each time a new document is created.

Also updated patreon link :)
2020-10-08 11:03:52 -04:00
Trevor Buckner
900f5b136f Fix login url 2020-10-07 20:22:15 -04:00
Trevor Buckner
194a9c0c40 Typo 2020-10-07 20:07:28 -04:00
Trevor Buckner
f6f9b768cc Ensure share links are updated on transfer to google 2020-10-07 20:01:32 -04:00
Trevor Buckner
8fe0148821 Fix Share links 2020-10-07 17:13:37 -04:00
Trevor Buckner
28ed2fe8f2 Fix Config spelling (must be all lowercase) 2020-10-07 16:17:05 -04:00
Trevor Buckner
131df2d82a Bug Fix 2020-10-07 15:55:07 -04:00
Trevor Buckner
0f5ec6c40c Bug Test... 2020-10-07 15:48:07 -04:00
Trevor Buckner
39cbadb100 Small Typo 2020-10-07 15:39:37 -04:00
Trevor Buckner
0afb503860 Update Version number & cleanup 2020-10-07 15:02:42 -04:00
Trevor Buckner
ed1c589e2d Linting 2020-10-06 14:21:15 -04:00
Trevor Buckner
864cc7a7bb Fix issues making brews when not signed in 2020-10-06 14:20:09 -04:00
Trevor Buckner
657a374895 Code Cleanup
Remove req, res from update, new, and getFolder google actions
2020-10-06 14:08:51 -04:00
Trevor Buckner
35e1ce0df2 Initial Commit. All seems to be working...?
EditPage.jsx and GoogleActions.js need to be cleaned up and shortened...
2020-10-05 23:33:15 -04:00
Trevor Buckner
2065ff80ff Merge pull request #1008 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.11.0
Bump @babel/preset-env from 7.9.6 to 7.11.0
2020-08-28 15:52:52 -04:00
Trevor Buckner
b24bba87d9 Merge pull request #1007 from naturalcrit/dependabot/npm_and_yarn/elliptic-6.5.3
[Security] Bump elliptic from 6.5.2 to 6.5.3
2020-08-28 15:52:32 -04:00
dependabot-preview[bot]
5583fc76f3 Bump @babel/preset-env from 7.9.6 to 7.11.0
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.9.6 to 7.11.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.11.0/packages/babel-preset-env)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-28 19:48:02 +00:00
Trevor Buckner
e810445ac9 Merge pull request #1025 from naturalcrit/updateDependencies
Update dependencies
2020-08-28 15:45:49 -04:00
Trevor Buckner
5afbb4ee4e Update changelog.md 2020-08-28 15:44:43 -04:00
Trevor Buckner
6ae4cd143c update several dependencies 2020-08-28 15:17:59 -04:00
dependabot-preview[bot]
978329fdc9 [Security] Bump elliptic from 6.5.2 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3. **This update includes a security fix.**
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-29 21:27:26 +00:00
Trevor Buckner
f754ecd6c3 Fix invisible paragraphs at top of new columns
Code was old Firefox fix that doesn't seem to do anything anymore anyway.
2020-07-19 02:32:58 -04:00
Trevor Buckner
62a827ce49 Update more dependencies 2020-05-20 10:14:05 -04:00
Trevor Buckner
e971da2b59 Merge pull request #943 from naturalcrit/dependabot/npm_and_yarn/eslint-plugin-react-7.20.0
Bump eslint-plugin-react from 7.19.0 to 7.20.0
2020-05-19 01:54:55 -04:00
Trevor Buckner
2d092cb290 Forgot to update styles for BrewItems 2020-05-19 01:45:53 -04:00
dependabot-preview[bot]
5b66ecb06f Bump eslint-plugin-react from 7.19.0 to 7.20.0
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.19.0 to 7.20.0.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.19.0...v7.20.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-19 05:38:33 +00:00
Trevor Buckner
22a9799674 Update vitreum (#946)
- Updates Vitreum to v6.0.1 + some custom fixes.
  - Stylesheets must be imported with `require('./sheet.less');` but can also can now share stylesheets between jsx components.
  - Should eliminate a lot of security concerns with older nested dependencies.
  - Changed a lot of files to make this work
- Also removes dependency on PicoRouter in favor of React-Router
2020-05-19 01:36:31 -04:00
Trevor Buckner
7c813be13a Revert Marked Update
Too many edge cases still broken. Bah.
2020-05-10 21:11:03 -04:00
Trevor Buckner
e94148b2f0 Merge pull request #938 from naturalcrit/update-Marked-renderer
Update marked renderer
2020-05-09 13:57:40 -04:00
Trevor Buckner
ec4f6e4327 Redundant marked version 2020-05-09 13:38:25 -04:00
Trevor Buckner
fcbd117784 Merge branch 'master' into update-Marked-renderer 2020-05-09 13:33:33 -04:00
Trevor Buckner
dab716a9e0 Lint 2020-05-09 13:16:39 -04:00
Trevor Buckner
9265e25c73 Update popup to notify update
Also point to latest FAQ page on Reddit
2020-05-08 15:32:39 -04:00
Trevor Buckner
4ad63d5bce Change text in RenderWarning popup
Reflect our policy of testing only on Chrome, but remove the specific issue of column-span support as that is now supported by other browsers.
2020-05-08 15:04:38 -04:00
dependabot-preview[bot]
f89c897488 Bump mongoose from 5.9.9 to 5.9.12
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.9.9 to 5.9.12.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.9.9...5.9.12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 14:30:56 -04:00
dependabot-preview[bot]
521ff5e7e3 Bump codemirror from 5.52.2 to 5.53.2
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.52.2 to 5.53.2.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.52.2...5.53.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 14:27:30 -04:00
dependabot-preview[bot]
89d8cb3b0a Bump moment from 2.24.0 to 2.25.3
Bumps [moment](https://github.com/moment/moment) from 2.24.0 to 2.25.3.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-08 14:27:19 -04:00
Trevor Buckner
a0e92b54d0 Fix multiline IMG tags breaking 2020-05-08 13:15:35 -04:00
Trevor Buckner
62f549f038 Fix Markdown rendering right after Divs 2020-05-06 16:30:35 -04:00
Trevor Buckner
e8f3b0c8d0 Fix Tables with uneven columns 2020-05-02 15:03:18 -04:00
Trevor Buckner
587ce78f4e Update robots.txt
This was blocking Google from updating the `noindex` tag.
2020-04-21 22:46:42 -04:00
Trevor Buckner
7ca1dd3c68 Update Marked.js to 1.0.0 2020-04-20 22:43:46 -04:00
Trevor Buckner
58543f0b4d Merge branch 'master' into update-Marked-renderer 2020-04-20 21:40:04 -04:00
Trevor Buckner
e88253f364 Added robots.txt, nofollow in metatags
Also noindex on /print/ pages
2020-04-20 13:46:18 -04:00
Trevor Buckner
bdf37d8fe7 Add robots noindex to edit and share pages 2020-04-20 12:15:27 -04:00
Trevor Buckner
22908207a3 Google verification for testing Robots.txt 2020-04-20 12:03:28 -04:00
Timothy Cyrus
7239b89108 Update metadataEditor.jsx 2020-04-16 20:24:01 -04:00
Timothy Cyrus
0ea80bd758 Update client/homebrew/pages/userPage/brewItem/brewItem.jsx
Co-Authored-By: Trevor Buckner <calculuschild@gmail.com>
2020-04-16 20:24:01 -04:00
Timothy Cyrus
f6d623ace3 Update brewItem.jsx 2020-04-16 20:24:01 -04:00
Timothy Cyrus
63ad8b3411 Update editPage.jsx 2020-04-16 20:24:01 -04:00
Timothy Cyrus
50cc757a5c Update metadataEditor.jsx 2020-04-16 20:24:01 -04:00
Trevor Buckner
70430f84e1 Small tweaks and linting 2020-04-14 11:15:05 -04:00
Timothy Cyrus
80db261c88 API Code Cleanup + Alt Endpoints
Added 2 new API Endpoints as alternatives to existing ones:
- PUT /api/:id (same as PUT /api/update/:id)
- DELETE /api/:id (same as GET /api/remove/:id)
2020-04-14 11:15:05 -04:00
Trevor Buckner
5631ef7be7 Fix HTML right before markdown
Allow raw HTML tags to end without a blank line if markdown is right after
2020-04-14 10:26:42 -04:00
dependabot-preview[bot]
2745a4d6c1 Bump react-dom from 16.13.0 to 16.13.1
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 16.13.0 to 16.13.1.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.1/packages/react-dom)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-14 10:15:37 -04:00
dependabot-preview[bot]
33190b5c89 Bump mongoose from 5.9.7 to 5.9.9
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.9.7 to 5.9.9.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.9.7...5.9.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-14 10:14:57 -04:00
Brandon Fryslie
354a5832e4 Adjust Dockerfile to improve caching 2020-04-03 16:23:13 -04:00
Brandon Fryslie
f57c0f0886 Add basic instructions for running the application via Docker 2020-04-03 16:23:13 -04:00
Brandon Fryslie
954a393fce Do not force SSL when using Docker 2020-04-03 16:23:13 -04:00
dependabot-preview[bot]
01dbac78ce Bump mongoose from 5.9.2 to 5.9.7
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.9.2 to 5.9.7.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.9.2...5.9.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-03 16:22:43 -04:00
dependabot-preview[bot]
594ea8ab59 Bump react from 16.13.0 to 16.13.1
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.13.0 to 16.13.1.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.1/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-03 16:21:08 -04:00
dependabot-preview[bot]
033493a31b Bump cookie-parser from 1.4.4 to 1.4.5
Bumps [cookie-parser](https://github.com/expressjs/cookie-parser) from 1.4.4 to 1.4.5.
- [Release notes](https://github.com/expressjs/cookie-parser/releases)
- [Changelog](https://github.com/expressjs/cookie-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/cookie-parser/compare/1.4.4...1.4.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-03 16:20:36 -04:00
dependabot-preview[bot]
e79b099633 Bump codemirror from 5.52.0 to 5.52.2
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.52.0 to 5.52.2.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.52.0...5.52.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-03 16:18:10 -04:00
Trevor Buckner
5bc948ab0a All fixes seem to be working? 2020-03-25 12:09:50 -04:00
Trevor Buckner
28c5d7d84a Update .gitignore 2020-03-24 10:01:28 -04:00
Trevor Buckner
825702ee1d Forgot version number 2020-03-11 09:41:16 -04:00
Trevor Buckner
61b58032ca Upping changelog to v2.8.2 2020-03-11 09:40:50 -04:00
Trevor Buckner
546cc13c1c Release 2.8.2
Marking this as a stable version since it's been a while, in preparation for adding new features.
2020-03-11 09:29:04 -04:00
dependabot-preview[bot]
44649d7f51 Bump eslint-plugin-react from 7.18.3 to 7.19.0
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.18.3 to 7.19.0.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.18.3...v7.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-10 12:22:35 -04:00
dependabot-preview[bot]
5f9aaba262 Bump react from 16.12.0 to 16.13.0
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.12.0 to 16.13.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.0/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 13:21:23 -05:00
dependabot-preview[bot]
5d5c0b9773 Bump codemirror from 5.51.0 to 5.52.0
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.51.0 to 5.52.0.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.51.0...5.52.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 13:20:24 -05:00
dependabot-preview[bot]
b681edba23 Bump mongoose from 5.9.1 to 5.9.2
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.9.1 to 5.9.2.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.9.1...5.9.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 13:19:33 -05:00
dependabot-preview[bot]
f0c412527b Bump react-dom from 16.12.0 to 16.13.0
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 16.12.0 to 16.13.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.0/packages/react-dom)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 13:17:32 -05:00
dependabot-preview[bot]
fcf6b8d764 Bump babel-preset-env from 1.6.1 to 1.7.0
Bumps [babel-preset-env](https://github.com/babel/babel-preset-env) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/babel/babel-preset-env/releases)
- [Changelog](https://github.com/babel/babel-preset-env/blob/1.x/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-preset-env/compare/v1.6.1...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 10:06:36 -05:00
dependabot-preview[bot]
8607b9dba8 Bump mongoose from 5.8.11 to 5.9.1
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.8.11 to 5.9.1.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.8.11...5.9.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 10:03:20 -05:00
dependabot-preview[bot]
ef4fa89d9a Bump superagent from 5.2.1 to 5.2.2
Bumps [superagent](https://github.com/visionmedia/superagent) from 5.2.1 to 5.2.2.
- [Release notes](https://github.com/visionmedia/superagent/releases)
- [Changelog](https://github.com/visionmedia/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/visionmedia/superagent/compare/v5.2.1...v5.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 10:00:09 -05:00
Trevor Buckner
6d38a633ef Cut out the renderPrivateBrews script 2020-02-07 10:16:58 -05:00
Eric Scheid
b3376435b9 purty apostrophes; containerise brew collections
NB: .brewItem:nth-child(2n+1) {...} counts all children of the contain, not just the .brewItem elements.
This meant if there were an even number of published brews, the style rule got applied to the wrong unpublished brews because there's an <h1> jigging the count.
2020-02-07 10:16:58 -05:00
Trevor Buckner
b9cfc2e6af Fixed need for spaces to render html in a DIV
Marked update does not naturally detect the end of blocked elements like DIVs. The fix is to handle cases where only one half of the Div is detected separately.
2020-02-04 14:10:36 -05:00
Trevor Buckner
3259836964 Remove extra |:--:| in table
This is invalid markdown.
2020-02-04 12:06:36 -05:00
Trevor Buckner
b3387c363f Bump react-dom to v16.12.0 2020-02-04 11:40:41 -05:00
Trevor Buckner
f0d9fcf942 Bump eslint version 2020-02-04 11:09:15 -05:00
Trevor Buckner
928b553b19 Revert Marked update
The markdown spec has changed too much and is breaking old brews.
2020-02-04 10:34:00 -05:00
dependabot-preview[bot]
508f87f117 Bump express from 4.16.3 to 4.17.1
Bumps [express](https://github.com/expressjs/express) from 4.16.3 to 4.17.1.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.16.3...4.17.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:29:53 -05:00
dependabot-preview[bot]
03b389761b Bump body-parser from 1.18.2 to 1.19.0
Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.18.2 to 1.19.0.
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.18.2...1.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:16:25 -05:00
dependabot-preview[bot]
36d0f15960 Bump shortid from 2.2.8 to 2.2.15
Bumps [shortid](https://github.com/dylang/shortid) from 2.2.8 to 2.2.15.
- [Release notes](https://github.com/dylang/shortid/releases)
- [Changelog](https://github.com/dylang/shortid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dylang/shortid/compare/2.2.8...2.2.15)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:09:30 -05:00
dependabot-preview[bot]
b56d4fb773 Bump pico-check from 1.0.3 to 1.3.2
Bumps [pico-check](https://github.com/stolksdorf/pico-check) from 1.0.3 to 1.3.2.
- [Release notes](https://github.com/stolksdorf/pico-check/releases)
- [Commits](https://github.com/stolksdorf/pico-check/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:08:39 -05:00
dependabot-preview[bot]
272b336cd8 Bump cookie-parser from 1.4.3 to 1.4.4
Bumps [cookie-parser](https://github.com/expressjs/cookie-parser) from 1.4.3 to 1.4.4.
- [Release notes](https://github.com/expressjs/cookie-parser/releases)
- [Changelog](https://github.com/expressjs/cookie-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/cookie-parser/compare/1.4.3...1.4.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:04:01 -05:00
dependabot-preview[bot]
ac58833adf Bump marked from 0.3.19 to 0.8.0
Bumps [marked](https://github.com/markedjs/marked) from 0.3.19 to 0.8.0.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](https://github.com/markedjs/marked/compare/v0.3.19...v0.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:01:03 -05:00
dependabot-preview[bot]
c3432a9263 Bump codemirror from 5.36.0 to 5.51.0
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.36.0 to 5.51.0.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.36.0...5.51.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 21:44:09 -05:00
dependabot-preview[bot]
f8f1c99266 Bump jwt-simple from 0.5.1 to 0.5.6
Bumps [jwt-simple](https://github.com/hokaccha/node-jwt-simple) from 0.5.1 to 0.5.6.
- [Release notes](https://github.com/hokaccha/node-jwt-simple/releases)
- [Changelog](https://github.com/hokaccha/node-jwt-simple/blob/master/History.md)
- [Commits](https://github.com/hokaccha/node-jwt-simple/compare/v0.5.1...v0.5.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 21:33:42 -05:00
dependabot-preview[bot]
0e9b50d4e8 Bump eslint-plugin-react from 7.7.0 to 7.18.3
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.7.0 to 7.18.3.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.7.0...v7.18.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 16:05:01 -05:00
dependabot-preview[bot]
e6e995d7f4 Bump moment from 2.22.0 to 2.24.0
Bumps [moment](https://github.com/moment/moment) from 2.22.0 to 2.24.0.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.22.0...2.24.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 16:00:45 -05:00
dependabot-preview[bot]
c9d18be0cb Bump superagent from 3.8.2 to 5.2.1
Bumps [superagent](https://github.com/visionmedia/superagent) from 3.8.2 to 5.2.1.
- [Release notes](https://github.com/visionmedia/superagent/releases)
- [Changelog](https://github.com/visionmedia/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/visionmedia/superagent/compare/v3.8.2...v5.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 15:37:06 -05:00
Trevor Buckner
b75063f936 Try upping Mongoose to 5.7.5 again
Apparently mongoose 5.7.0 has breaking changes.
2020-02-03 15:29:12 -05:00
Trevor Buckner
6ef88e0f1f Revert "Try upping Mongoose to 5.7.5"
This reverts commit 59e0118d8b.
2020-02-03 14:52:06 -05:00
Trevor Buckner
59e0118d8b Try upping Mongoose to 5.7.5
Small fix to connection string to keep Mongoose working with the older MongoDB 3.6 database
2020-02-03 14:49:03 -05:00
Trevor Buckner
09e6766e0d Update .gitignore 2020-02-03 14:16:06 -05:00
Trevor Buckner
ce8cfde211 Revert "[Security] Bump mongoose from 5.0.13 to 5.7.5"
This reverts commit 7fbb51b3f4.
2020-02-03 14:11:47 -05:00
dependabot-preview[bot]
7fbb51b3f4 [Security] Bump mongoose from 5.0.13 to 5.7.5
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.0.13 to 5.7.5. **This update includes a security fix.**
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.0.13...5.7.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 14:03:23 -05:00
dependabot-preview[bot]
1e9b8e679d Bump lodash from 4.17.13 to 4.17.15
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.13 to 4.17.15.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.13...4.17.15)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 13:50:31 -05:00
dependabot-preview[bot]
53a1c4f85d [Security] Bump react-dom from 16.3.1 to 16.3.3
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 16.3.1 to 16.3.3. **This update includes a security fix.**
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/HEAD/packages/react-dom)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 13:50:10 -05:00
dependabot-preview[bot]
b1c252495b Bump react from 16.3.1 to 16.12.0
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.3.1 to 16.12.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.12.0/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 13:47:22 -05:00
dependabot-preview[bot]
5fce35edd7 Bump classnames from 2.2.5 to 2.2.6
Bumps [classnames](https://github.com/JedWatson/classnames) from 2.2.5 to 2.2.6.
- [Release notes](https://github.com/JedWatson/classnames/releases)
- [Changelog](https://github.com/JedWatson/classnames/blob/master/HISTORY.md)
- [Commits](https://github.com/JedWatson/classnames/compare/v2.2.5...v2.2.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 13:32:02 -05:00
Trevor Buckner
debe58ff0b Remove unneeded comments 2020-01-28 10:53:07 -05:00
Trevor Buckner
0018627f82 Process brew compression in batches to reduce server load. 2020-01-28 10:53:07 -05:00
Trevor Buckner
e50f0a1f3b Upping number of compressed brews at once 2020-01-23 15:12:25 -05:00
Trevor Buckner
e862f65166 Up number of brews compressed at once 2020-01-23 12:57:23 -05:00
Trevor Buckner
347575b0ec Lint 2 2020-01-23 12:24:54 -05:00
Trevor Buckner
4ac922482e Lint 2020-01-23 12:24:54 -05:00
Trevor Buckner
27e7af870a Fixed what was breaking 2020-01-23 12:24:54 -05:00
Trevor Buckner
474b2552fd Fix recent crash 2020-01-23 11:47:21 -05:00
Trevor Buckner
a06b29c6f5 Admin controls for compressing old brews 2020-01-23 11:20:31 -05:00
Trevor Buckner
4128670a9f Revert "Merge pull request #867 from naturalcrit/compressBrews"
This reverts commit 94d090277f, reversing
changes made to aeffec1763.
2020-01-23 10:27:30 -05:00
Trevor Buckner
94d090277f Merge pull request #867 from naturalcrit/compressBrews
Adding some admin controls to compress old brews.
2020-01-23 10:06:53 -05:00
Trevor Buckner
0b8889d0b8 Merge branch 'master' into compressBrews 2020-01-23 09:48:47 -05:00
Trevor Buckner
2efb24d692 Lint 2020-01-23 09:47:19 -05:00
Trevor Buckner
bc81e09686 Admin script to compress old brews
Also fully delete the text field after compressed version is saved instead of leaving just an empty field.
2020-01-23 09:38:50 -05:00
Eric Scheid
aeffec1763 minor grammar fixes in error messages 2020-01-21 10:07:35 -05:00
Eric Scheid
462a5608d2 Revert "list-style-type is a property of <li>, not <ul>"
This reverts commit 831bf5bc62.
2020-01-21 10:07:35 -05:00
Eric Scheid
09ae750eec list-style-type is a property of <li>, not <ul> 2020-01-21 10:07:35 -05:00
Trevor Buckner
e8dcb042f8 Typo 2020-01-21 10:05:22 -05:00
Trevor Buckner
ecd25ca49f Changed to encourage environment variable 2020-01-21 10:05:22 -05:00
Christian Brickhouse
3e8551bad6 Fix links to blobs in README.md 2020-01-21 10:05:22 -05:00
Christian Brickhouse
ef325e2617 Updating README.md with more detailed install instructions and reformatting. 2020-01-21 10:05:22 -05:00
Trevor Buckner
81c361bfb8 Make brand new files also save compressed 2020-01-10 16:03:04 -05:00
Trevor Buckner
37eb0d0889 Merge branch 'master' into compressBrews 2020-01-10 15:59:33 -05:00
Trevor Buckner
8adc04a565 Make brand new files also save compressed 2020-01-10 15:55:21 -05:00
Trevor Buckner
486841084f Make compression raw (no wrapper). Otherwise small files are actually larger after compression. 2020-01-10 09:07:28 -05:00
Trevor Buckner
399a6d82f6 lint 2020-01-10 09:07:28 -05:00
Trevor Buckner
becc6b8df0 Add zlib compression to the "text" field. Now, when a file is saved, the original text field is blanked out and the "compressed" binary version is saved instead. Viewing files in Edit page unzips and views them just fine.
Does not compress old files unless they are opened and resaved by someone. Have not tested on the "share" or "print" pages yet, but should work.
2020-01-10 09:07:28 -05:00
Trevor Buckner
1ff3f96f6c Make compression raw (no wrapper). Otherwise small files are actually larger after compression. 2020-01-10 08:59:39 -05:00
Trevor Buckner
b289cb1003 lint 2020-01-05 23:59:42 -05:00
Trevor Buckner
3ea3d273a5 Add zlib compression to the "text" field. Now, when a file is saved, the original text field is blanked out and the "compressed" binary version is saved instead. Viewing files in Edit page unzips and views them just fine.
Does not compress old files unless they are opened and resaved by someone. Have not tested on the "share" or "print" pages yet, but should work.
2020-01-05 23:48:50 -05:00
dependabot[bot]
a007c5f85f Bump lodash from 4.17.5 to 4.17.13
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.5 to 4.17.13.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.5...4.17.13)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-02 13:04:54 -04:00
Trevor Buckner
06d970e61a Notification about the Grey Shadow issue
First Reported 7/24/2019. Think it's a mobile Chrome issue.
2019-07-24 09:35:13 -04:00
Trevor Buckner
f28ed3d52e Try again... 2019-07-01 13:56:27 -04:00
Trevor Buckner
c5867dab91 Undo. Didn't work. 2019-07-01 13:20:24 -04:00
Trevor Buckner
df4bacf890 Allow cleaning function to work for a full minute 2019-07-01 13:16:26 -04:00
Trevor Buckner
6a57542216 Fix links on Home Page
Make github issues go to subreddit, update license link to Naturalcrit instead of stolksdorf
2019-05-02 08:38:48 -04:00
Trevor Buckner
72db7fedfb Remove "Report Issue" from share page
Too many users are reporting typos or brew issues thinking the "Report Issue" button is commenting on the document itself, instead of the website. Hoping this limits bug reports to just users who are actually editing documents.
2019-04-08 09:12:48 -04:00
Christian Brickhouse
892a5f9f1e Fixing links in contributing.md
Links to issue tags point to the project repo's Issues rather than a fork's.
2019-03-12 08:26:41 -04:00
Trevor Buckner
2839846ec0 Lint 2 2019-02-14 13:54:35 -05:00
Trevor Buckner
502ef6ad7c Lint 2019-02-14 13:54:35 -05:00
Trevor Buckner
5b8aa5bb19 Change the popup warning on the metadata editor 2019-02-14 13:54:35 -05:00
Trevor Buckner
a26e828f00 Change popup warning if you are the only editor
If you are the only editor, it will warn you that the document is about to be permanently deleted.
2019-02-14 13:54:35 -05:00
Trevor Buckner
f572e671cf Lint 2019-02-14 13:54:35 -05:00
Trevor Buckner
d18bd500b1 Fixing delete buttons
Delete should now first remove the current user as an editor, and only delete the file if he was the last editor. Currently, anyone who views your brew is added as an editor and if they delete it, the whole brew is deleted for everyone.
2019-02-14 13:54:35 -05:00
Trevor Buckner
14f721d209 Fix spacing on Notification popup for legibility 2019-02-07 12:24:26 -05:00
Trevor Buckner
1219f64cb3 Notification about Chrome PDF missing letters bug
Changed DISMISS KEY to current date to make popup appear again.
2019-02-07 12:19:38 -05:00
Trevor Buckner
b82aac4a5a Make Monster Blocks always generate "abilities"
Originally had a random chance to generate no abilities, which a user might not know how to insert with the proper formatting. Also added some new abilities to select randomly when generated. Also fixes #836 .
2019-02-04 11:26:37 -05:00
Trevor Buckner
b53b5ccf43 More version updates in the changelog 2019-01-29 17:40:39 -05:00
Bobby Wertman
9e7981f05c Fix typo in changelog
Fixes #765
2019-01-29 17:40:39 -05:00
Trevor Buckner
18a238786e Hide scrollbar in share page but KEEP SCROLLBAR IN USER PAGE 2019-01-22 13:42:46 -05:00
Trevor Buckner
507f8e0852 Revert "Revert "Merge branch 'master' of https://github.com/naturalcrit/homebrewery""
This reverts commit 0c70162a78.
2019-01-22 11:26:33 -05:00
Trevor Buckner
0c70162a78 Revert "Merge branch 'master' of https://github.com/naturalcrit/homebrewery"
This reverts commit 077511dfa7, reversing
changes made to facbc5f6dc.
2019-01-22 11:23:35 -05:00
Trevor Buckner
077511dfa7 Merge branch 'master' of https://github.com/naturalcrit/homebrewery 2019-01-22 11:13:53 -05:00
Trevor Buckner
69b25eb03a Fix duplicate scrollbar in "share" page 2019-01-22 10:54:14 -05:00
Trevor Buckner
a56a999920 More Lint 2019-01-19 20:11:16 -05:00
Trevor Buckner
c1d8796807 Lint 2019-01-19 20:11:16 -05:00
Trevor Buckner
71af97e489 Refactored the "Recent Items" navbar component. Greatly simplified the code to about 70% length and made it much easier to read. Results in the same thing.
Oh. And it works again. Recent brews should be showing up fine now.
2019-01-19 20:11:16 -05:00
Trevor Buckner
facbc5f6dc Merge branch 'master' of https://github.com/naturalcrit/homebrewery 2019-01-15 07:35:38 -05:00
Trevor Buckner
63a1ff454f Added a notification popup similar to the "YOU ARE NOT USING CHROME" message. Can be used to notify users of current known issues, updates, etc.
Currently just reminds users to back up documents with a link to the Reddit FAQ.
2019-01-11 16:31:08 -05:00
Trevor Buckner
c634192289 Added a notification popup similar to the "YOU ARE NOT USING CHROME" message. Can be used to notify users of current known issues, updates, etc.
Currently just reminds users to back up documents with a link to the Reddit FAQ.
2019-01-11 16:26:03 -05:00
Scott Tolksdorf
bf21c3d351 lint 2018-12-06 18:19:43 -05:00
Scott Tolksdorf
52c0462a4f Finished the clean up brew ui 2018-12-06 18:19:43 -05:00
Scott Tolksdorf
1184fe86a5 Removing the admin key, switching to full basic auth 2018-12-06 18:19:43 -05:00
Scott Tolksdorf
7656e53606 Finished stats and brew lookup on admin panel 2018-12-06 18:19:43 -05:00
Scott Tolksdorf
4241052952 Updating readme 2018-12-05 18:14:05 -05:00
Scott Tolksdorf
21b83ead88 Added middleware to force all routes to be https 2018-12-05 18:14:05 -05:00
Scott Tolksdorf
448ea5cf5c Making the script tag stripper case insensitive 2018-12-03 09:55:49 +00:00
Scott Tolksdorf
20b719d0de Added in a function to sanatize script tags specifically 2018-12-03 09:55:49 +00:00
Scott Tolksdorf
8b04cc9269 Adding some more text to the default issue title to guide peopel to add an issue title 2018-11-27 10:38:29 +00:00
Marius
96466211c7 Make imgur link https in order to display image
Newer browsers warn and/or do not display content that gets loaded via HTTP if site is visited via HTTPS.
2018-11-26 20:13:25 -05:00
Kurtis Miller
34741291c7 Fix ESLint errors 2018-11-26 20:12:59 -05:00
Kurtis Miller
fcb0cd8ee7 Reset to previous commit 2018-11-26 20:12:59 -05:00
Kurtis Miller
aafac16af2 Add bold/italic shortcuts, reposition renderer warning 2018-11-26 20:12:59 -05:00
Trevor Buckner
911d1d4f9c Hide duplicate scrollbar in brewRenderer 2018-11-13 15:24:46 -05:00
Trevor Buckner
2cd7b44e12 Merge branch 'PRODUCTION' into master 2018-11-11 04:02:12 -05:00
Trevor Buckner
bfccd1d9e4 Wrap the text in the "source" page.
See #332.
2018-11-11 03:10:27 -05:00
Trevor Buckner
bf1bf6c191 Added spaces for lint... 2018-11-08 16:11:10 -05:00
Trevor Buckner
cc5cb677a1 Found the lint-friendly way to do it. Just needed to pass along the event data. 2018-11-08 16:11:10 -05:00
Trevor Buckner
2dcd7101f3 Fix metadata not working. Some earlier linting caused the handleFieldChange and handleSystem functions to not have access to event data. Returning these to their pre-lint state for now, but there might be a more lint-friendly way to do this later. 2018-11-08 16:11:10 -05:00
Trevor Buckner
f4e7e46a04 Added spaces for lint... 2018-11-08 16:04:14 -05:00
Trevor Buckner
8b3b7cb5aa Found the lint-friendly way to do it. Just needed to pass along the event data. 2018-11-08 16:04:14 -05:00
Trevor Buckner
77081b39b4 Fix metadata not working. Some earlier linting caused the handleFieldChange and handleSystem functions to not have access to event data. Returning these to their pre-lint state for now, but there might be a more lint-friendly way to do this later. 2018-11-08 16:04:14 -05:00
Trevor Buckner
a06236e3ff Fix regression with updated Pico-Router
This variable name changed with the v2.0.0 of Pico-Router and it wasn't changed here. This was causing every page to display a  `<div class="homePage page">` rather than the appropriate `editPage` or `sharePage`. It just wasn't immediately noticeable since `homePage` and `editPage` are almost identical. The problem is clearly visible on `sharePage` however.

`homebrew.jsx` was set to always render a defaultUrl of ` ` which creates a `<div class="homePage page">` by default, overwriting the expected `<div>` on for the current url.
2018-11-07 16:11:18 -05:00
Rae Che
c8585775be Change circleci mongo version to one that still exists 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
439cdfa1ea upgrading to mongoose v5 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
4c36f254c4 Removing picofluc for now 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
b6815593fd adding bithoundrc and updating pico-router 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
84054d1aae Updating some packages 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
6cd6b3c1c6 Upping version 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
907dbe4416 Updating to user create-react-class 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
47429d804a Upping the node ver on circleci 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
1d54ab49e6 Updating superagent 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
f634c451ff Adding in the missing repository field 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
778c0ca0b1 Updating build scripts and libraries 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
f4985b68ca Updating build scripts and libraries 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
c8875cff94 lint 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
40f1d4c790 Updating navbar link to go to the new subreddit 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
7818b6db46 Adding mongo image to the circleci config 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
4163e95938 Updating contributing and issue guides, added new verify npm task 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
fed0536673 Moving babelrc into package 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
a4b92af351 Adding in test case runner 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
4584b83c60 adding a circleCI task 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
c57ca0b27f Adding circleCI and eslint 2018-11-07 16:00:24 -05:00
Scott Tolksdorf
c04cd23213 Adding circleCI 2018-11-07 16:00:24 -05:00
Trevor Buckner
140f064786 Re-enable Box Shadows in PDF
Box shadows have been fixed in Chrome. No reason to hide them anymore.
2018-11-07 16:00:24 -05:00
Scott Tolksdorf
643f6f933d Fixing issue#413 class feature duping entire brew 2018-11-07 16:00:24 -05:00
Trevor Buckner
24aec1c649 Fixed typo "is" -> "in" 2018-11-07 15:15:07 -05:00
sprigunn
8caae18a12 Update README.md
Typos and additional license to sync up with http://homebrewery.naturalcrit.com.
2018-11-07 15:15:07 -05:00
Trevor Buckner
a1140f75d8 Fix border-image bug on PDFs
I think this might be caused by a new bug in Chrome or something since it seems to have popped up in GMBinder as well in the last couple weeks. See #683

Anyway, the error only occurs on the Class Table and Descriptive Text Box, which are also the only ones to use `border-image-repeat: round`. I changed it to `border-image-repeat: stretch` which solves the graphical issue and doesn't affect the appearance otherwise in this particular case.
2018-11-07 14:57:52 -05:00
Rae Che
3532d75365 s/let/const/ to satisfy ESLint 2018-08-24 10:12:53 -04:00
Rae2che5
ea81bb5ebc Add missing volume declaration. 2018-08-22 01:44:48 -07:00
Schemen
8d217f8785 Update according to change request 2018-08-22 01:44:48 -07:00
Schemen
d35db1f702 Add dockerization and example compose file 2018-08-22 01:44:48 -07:00
Rae Che
f6b058f3c9 brewRenderer: Capture event properties while still in event loop.
React [pools events](https://github.com/facebook/react/issues/2850),
which means that once the lambda runs later on the properties we want
to read will be null. Instead, we capture the properties in the event
loop.
2018-08-20 14:14:52 -04:00
Rae Che
5ab7c29b9d Change circleci mongo version to one that still exists 2018-08-20 13:20:05 -04:00
Rae Che
c5ecb9d57d ESLint reformatting 2018-05-31 10:53:07 -04:00
Rae Che
d0c473878a BrewSearch: Remove stale piece of React state 2018-05-31 10:53:07 -04:00
Rae Che
796df9a1ac EditPage: Remove stale bit of React state 2018-05-31 10:53:07 -04:00
Rae Che
9e8e403195 HomebrewAdmin: Remove stale piece of React state 2018-05-31 10:53:07 -04:00
Rae Che
a369871a06 ESLint reformatting 2018-05-31 10:53:07 -04:00
Rae Che
f2f45f3657 RenderWarnings: Remove dead code 2018-05-31 10:53:07 -04:00
Rae Che
e86ce5cf06 PrintPage: Fix potentially inconsistent React state update 2018-05-31 10:53:07 -04:00
Rae Che
8d73ff6833 BrewLookup: Mark links as noreferrer 2018-05-31 10:53:07 -04:00
Rae Che
c4397d34f8 BrewItem: Mark links as noreferrer 2018-05-31 10:53:07 -04:00
Rae Che
66c0c96a4f EditPage: Mark link as noreferrer 2018-05-31 10:53:07 -04:00
Rae Che
838b64c589 EditPage: Simplify hasChanges.
In particular, remove unreachable `return` statement.
2018-05-31 10:53:07 -04:00
Rae Che
730dde730c MetadataEditor: Mark link as noreferrer 2018-05-31 10:53:07 -04:00
Rae Che
d867aa7ce1 EditPage: Consistent trySave on handle[Metadata|Text]change
Previously, one of these happened in React's post-state-update callback,
while the other happened directly. They now both use the callback.
2018-05-31 10:53:07 -04:00
Rae Che
761597e71f EditPage: Fix potentially inconsistent React state updates 2018-05-31 10:53:07 -04:00
Rae Che
4bde5fcbf8 HomebrewAdmin: Mark links as noreferrer. 2018-05-31 10:53:07 -04:00
Rae Che
ff0aa56ddc HomebrewAdmin: Remove direct React state mutation. 2018-05-31 10:53:07 -04:00
Rae Che
74f92f3e44 Use noreferrer in recent-brew links. 2018-05-31 10:53:07 -04:00
Rae Che
84285f7359 Remove redundant expression. 2018-05-31 10:53:07 -04:00
Rae Che
ec0de7a408 BrewRenderer: Remove redundant errors state property.
The errors are in fact tracked in `this.props`, not `this.state`.
2018-05-31 10:53:07 -04:00
Rae Che
9ea99236ff Omit redundant property definition in brew renderer 2018-05-31 10:53:07 -04:00
Rae Che
f806328e75 Omit redundant property in eslintrc 2018-05-31 10:53:07 -04:00
Rae Che
15ac397b63 Fix inconsistent React state update.
Flagged by lgtm.com (as js/react/inconsistent-state-update).
2018-05-31 10:53:07 -04:00
Rae Che
b8105eb147 Config: Read MongoDB URL from config files too. 2018-05-29 11:04:57 -04:00
Trevor Buckner
31cbd9ef40 Fix regression with updated Pico-Router
This variable name changed with the v2.0.0 of Pico-Router and it wasn't changed here. This was causing every page to display a  `<div class="homePage page">` rather than the appropriate `editPage` or `sharePage`. It just wasn't immediately noticeable since `homePage` and `editPage` are almost identical. The problem is clearly visible on `sharePage` however.

`homebrew.jsx` was set to always render a defaultUrl of ` ` which creates a `<div class="homePage page">` by default, overwriting the expected `<div>` on for the current url.
2018-05-23 17:41:50 +01:00
Trevor Buckner
eaab6de691 Fix border-image bug on PDFs
I think this might be caused by a new bug in Chrome or something since it seems to have popped up in GMBinder as well in the last couple weeks. See #683

Anyway, the error only occurs on the Class Table and Descriptive Text Box, which are also the only ones to use `border-image-repeat: round`. I changed it to `border-image-repeat: stretch` which solves the graphical issue and doesn't affect the appearance otherwise in this particular case.
2018-05-22 10:01:03 +01:00
Trevor Buckner
d417c76c56 Remove unused event triggers
Removed `onMouseOver` and `onMouseOut` since they are no longer needed to trigger `will-change`
2018-05-14 08:30:19 +01:00
Trevor Buckner
2f15cc5611 Lint 2018-05-14 08:30:19 +01:00
Trevor Buckner
eb08172fb1 Move will-change to stylesheet
This makes `will-change: transform` permanent in the brewRenderer stylesheet rather than trying to load and unload it programatically.
2018-05-14 08:30:19 +01:00
Trevor Buckner
0cc87a4f0f Lint Fixes 2? 2018-05-14 08:30:19 +01:00
Trevor Buckner
004dc79eb2 Lint fixes 2018-05-14 08:30:19 +01:00
Trevor Buckner
a8a70c2d70 Unified spacing 2018-05-14 08:30:19 +01:00
Trevor Buckner
825c259fba Optimize for smooth scrolling of BrewRenderer
The will-change property allows the browser to optimize for smoother animations. This completely eliminates the scrolling stutter.
2018-05-14 08:30:19 +01:00
Rae2che5
cbbb7292d9 Revert "Fix duplicated text from Class Feature snippet"
This reverts commit 9519a0b4e4.
2018-05-11 20:44:53 +01:00
Trevor Buckner
9519a0b4e4 Fix duplicated text from Class Feature snippet
When adding the Class Feature snippet, all curent text in the document is inserted in place of the randomly generated class name, creating several duplicates of the document and generally creating a mess.  See #413 which was closed but the issue was never fixed.

Most of the snippet code generators do not have any input arguments in the `module.exports` function, so they don't have this issue. However, Table of Contents needs to parse the text in the brew, so it is passed a copy of the brew document. Unfortunately, Class Feature (classfeature.gen.js) also has an input parameter for when it is used as part of the Full Class snippet.  Thus, the unintended consequence occurs in snippetbar.jsx in the `execute` function.

This fix simply adds a check to the execute function and only passes in the brew as an argument if the clicked snipped actually is the Table of Contents. Some restructuring might later reveal a cleaner way to do this rather than an awkward special case check like this.
2018-05-11 10:20:18 +01:00
Eric Scheid
9dd16b6dd5 add page-break-inside to support Firefox, the laggard 2018-05-10 14:36:24 -04:00
Eric Scheid
d693301c37 Fix property name break-inside 2018-05-10 14:36:24 -04:00
Rae Che
fd5d142c16 Fix calculation of currently-viewed page.
Previously, this relied on a hard-coded constant determining the
height of a page. However, that's unnecessary -- we know the scroll
height of the window and the number of pages, so we can compute it.
2018-05-10 14:35:03 -04:00
Trevor Buckner
ee63d2d857 Fix duplicate table headers
Fixes a common issue. See #318.
2018-05-10 18:26:46 +01:00
Vincent Durmont
7c3946fb03 Some typos 2018-05-10 13:11:40 -04:00
Trevor Buckner
3e69e8c1aa Revert "Merge pull request #1 from calculuschild/Fix-Duplicated-Table-Headers"
This reverts commit 1a0bc1952c, reversing
changes made to 4f4ef908e0.
2018-05-08 21:33:34 -04:00
Trevor Buckner
1a0bc1952c Merge pull request #1 from calculuschild/Fix-Duplicated-Table-Headers
Fix Duplicated Table Header
2018-05-08 12:45:02 -04:00
Trevor Buckner
7e2c3381ae Fix Duplicated Table Header
Fixes a pretty common issue. See #318.
2018-05-08 12:42:11 -04:00
Trevor Buckner
4f4ef908e0 Merge branch 'master' of https://github.com/calculuschild/homebrewery 2018-05-08 12:39:50 -04:00
Trevor Buckner
3fe2360d92 Revert "Fix duplicated table headers"
This reverts commit 13a2a7efd2.
2018-05-08 12:36:30 -04:00
Trevor Buckner
13a2a7efd2 Fix duplicated table headers
Fixes a pretty common issue. See #318.
2018-05-08 12:33:36 -04:00
Trevor Buckner
073fb73bde Wrap the text in the "source" page.
See #332.
2018-05-07 10:56:23 +01:00
Trevor Buckner
847615ef8e Wrap the text in the "source" page.
See #332.
2018-04-17 21:03:50 -04:00
Scott Tolksdorf
3583c2e776 upgrading to mongoose v5 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
2a753ccc7c Removing picofluc for now 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
55c529473a adding bithoundrc and updating pico-router 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
164f646e08 Updating some packages 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
6f4962926c Upping version 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
f18a181e2e Updating to user create-react-class 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
c4bff6afa0 Upping the node ver on circleci 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
63e8a0d9b7 Updating superagent 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
ce3fda683b Adding in the missing repository field 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
9594b73b0d Updating build scripts and libraries 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
78ad4bea09 Updating build scripts and libraries 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
ed1b5252be lint 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
bf27250990 Updating navbar link to go to the new subreddit 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
bab11d692e Adding mongo image to the circleci config 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
3350b04f64 Updating contributing and issue guides, added new verify npm task 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
5ebba25183 Moving babelrc into package 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
8f77ac9e56 Adding in test case runner 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
3f728e7993 adding a circleCI task 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
3f22572f98 Adding circleCI and eslint 2018-04-09 00:12:56 -04:00
Scott Tolksdorf
a52f628fdf Adding circleCI 2018-04-09 00:12:56 -04:00
Trevor Buckner
fe63133d7c Re-enable Box Shadows in PDF
Box shadows have been fixed in Chrome. No reason to hide them anymore.
2018-04-08 23:23:49 -04:00
Scott Tolksdorf
3247cab214 Fixing issue#413 class feature duping entire brew 2017-06-04 16:07:49 -04:00
Scott Tolksdorf
b68c6a4ad2 Added ability to hide render wanring notification 2017-04-22 12:09:18 -04:00
Scott Tolksdorf
6e0f042b42 Adding quick fix for PPR getting out of sync 2017-03-03 19:25:56 -05:00
Scott Tolksdorf
c47d492ed3 fix 2017-02-18 14:48:45 -05:00
Scott Tolksdorf
18852932e8 Added new delete brew option on user page 2017-02-18 14:46:14 -05:00
Scott Tolksdorf
cc8e874ad1 Disabling zoom check, since it does not play well with many browsers 2017-01-21 20:38:35 -05:00
Scott Tolksdorf
8b148b81b8 Merge branch 'v2.7' 2017-01-19 12:43:48 -05:00
Scott Tolksdorf
063d701a0e Updating the changelog and welcome msg 2017-01-19 12:39:13 -05:00
Scott Tolksdorf
296b066ed3 Added a brew lookup for admin page 2017-01-19 12:28:00 -05:00
Scott Tolksdorf
9fe6fd979b Adding in line highlight for new pages 2017-01-19 12:10:19 -05:00
Scott Tolksdorf
70346ffce7 Fixed saving authors and systems to brews 2017-01-19 12:06:14 -05:00
Scott Tolksdorf
c417c1aa0c Hiding the editor snapping for now 2017-01-19 11:50:01 -05:00
Scott Tolksdorf
5e04e5dc99 Code to brew page jumping working 2017-01-17 23:32:34 -05:00
Scott Tolksdorf
6544d63b23 Updated vitreum, fixed events in render warnings 2017-01-17 00:39:28 -05:00
Scott Tolksdorf
5bfa195dc7 added listner for zoom event 2017-01-15 11:38:22 -05:00
Scott Tolksdorf
9b936f42f3 Added tooltip to metadata editor and upped the version of font awesome 2017-01-14 14:35:36 -08:00
Scott Tolksdorf
5fe7c7a6d8 renamed render warnings and now built into the brewrenderer 2017-01-14 14:21:51 -08:00
Scott Tolksdorf
6e86d9c123 Added a quick build task and created the warnings component 2017-01-14 13:53:09 -08:00
Scott Tolksdorf
306e4f024c Adding the meta editor to home page 2017-01-14 13:34:20 -08:00
Scott Tolksdorf
a2c6940be4 version working again 2017-01-14 13:32:34 -08:00
Scott Tolksdorf
75f83427e0 Added version to model 2017-01-14 13:30:23 -08:00
Scott Tolksdorf
4be9f4a74a Adding in dep for new babel preset 2017-01-14 13:17:41 -08:00
Scott Tolksdorf
9a9e0cc5bc Removing ref to the todo list 2016-12-26 13:43:32 -05:00
Scott Tolksdorf
62e4997fa9 emoved a ton of unsued files 2016-12-26 13:43:18 -05:00
Scott Tolksdorf
b45ec32e44 Removing the need to have codemirror in shared 2016-12-26 13:29:11 -05:00
Scott Tolksdorf
9fd7586726 Fixed renderer crashing with malformed html on load 2016-12-25 23:44:24 -05:00
Scott Tolksdorf
75f0a9f755 Fixed black boxes on print to pdf 2016-12-25 23:14:04 -05:00
Scott Tolksdorf
a35d684773 Changed icon and fixed 249 2016-12-25 23:09:22 -05:00
Scott Tolksdorf
9b80f17564 Lots of doc improvements 2016-12-25 22:07:23 -05:00
Scott Tolksdorf
90a6ac4ef8 removing the architecuter files 2016-12-25 21:46:59 -05:00
Scott Tolksdorf
bd6ba1e497 Removing uneeded files 2016-12-25 21:46:13 -05:00
Scott Tolksdorf
56048ab936 vitreum 4 is looking stable 2016-12-25 21:44:27 -05:00
Scott Tolksdorf
1ec0b2fa91 updating todo 2016-12-25 21:01:12 -05:00
Scott Tolksdorf
fe4a05dc25 Fixed the class table issue 2016-12-16 22:59:44 -05:00
Scott Tolksdorf
1f1bd669fe Added additional protection against debounce saving 2016-12-12 10:28:37 -05:00
Scott Tolksdorf
63aee2dedf Merge branch 'v2.6' 2016-12-03 16:28:07 -05:00
Scott Tolksdorf
e5ffb7c629 Greatly improved the user page UI 2016-12-03 16:27:15 -05:00
Scott Tolksdorf
be783e5f6b Added a table of contents snippet 2016-12-03 15:47:20 -05:00
Scott Tolksdorf
d96ac0f3ca Adding breakavoid to list elements 2016-12-03 14:56:31 -05:00
Scott Tolksdorf
ce5596c489 Making the user page a bit prettier 2016-12-03 14:54:11 -05:00
Scott Tolksdorf
9285e53e55 Added conditional partial page rendering 2016-12-03 14:49:00 -05:00
Scott Tolksdorf
bec417a325 Changed the meta editor icon 2016-12-03 14:02:28 -05:00
Scott Tolksdorf
1eeb1127b1 Added report back to edit 2016-12-03 13:59:18 -05:00
Scott Tolksdorf
8e6fccc969 Added account nav to newpage and added split table snippet 2016-12-01 10:06:23 -05:00
Scott Tolksdorf
a634b76117 Fixed a bug with saving while not logged in 2016-11-27 18:55:50 -05:00
Scott Tolksdorf
30942785d1 Fixed the User page 2016-11-27 13:45:38 -05:00
Scott Tolksdorf
21d3c5bfc8 Fixed a capitalization error 2016-11-25 00:58:28 -05:00
Scott Tolksdorf
7f639a0824 Merge branch 'accounts' 2016-11-25 00:29:46 -05:00
Scott Tolksdorf
a0ca45ce1c Added new account nav item 2016-11-25 00:28:48 -05:00
Scott Tolksdorf
4e5cd914f7 User page now running properly 2016-11-24 23:35:10 -05:00
Scott Tolksdorf
2d6b89c769 Updating changelog 2016-11-24 22:37:34 -05:00
Scott Tolksdorf
6af9dd9325 Added in a brew item component 2016-11-24 00:07:04 -05:00
Scott Tolksdorf
9e14872f06 Stubbed out a userpage 2016-11-23 23:59:57 -05:00
Scott Tolksdorf
750f5c1330 Added in middleware for retriving brews by a user 2016-11-23 23:41:39 -05:00
Scott Tolksdorf
1db24d07bd Adding in the needed libs 2016-11-23 23:00:28 -05:00
Scott Tolksdorf
f4f96253c2 Moved deleting a brew into the emtadata editor 2016-11-23 15:01:46 -05:00
Scott Tolksdorf
97cf2d2ce7 Updating changelog and version 2016-11-23 14:48:54 -05:00
Scott Tolksdorf
e61bf22788 Added in metadata editor 2016-11-23 14:47:28 -05:00
Scott Tolksdorf
ccdb6a3cbd Removing refernces to ver from pages 2016-11-14 23:09:26 -05:00
Scott Tolksdorf
d400c37b6d Updated style to navbar 2016-11-14 23:03:58 -05:00
Scott Tolksdorf
2f2a1c5146 Control s and p now save and print on editor pages 2016-11-14 22:40:37 -05:00
Scott Tolksdorf
8b3f9ac76a Cleaned up the server file and added methodfs and statics to the model 2016-11-14 22:30:28 -05:00
Scott Tolksdorf
6672dff938 Print page can now load from local storage 2016-11-14 21:31:01 -05:00
Scott Tolksdorf
d7463ec28e Navbar now using global scope 2016-11-14 20:15:29 -05:00
Scott Tolksdorf
4dce90ab41 Cleaning up the server file 2016-11-14 20:14:08 -05:00
Scott Tolksdorf
5f6f7ec691 Removing save warning from new page 2016-11-14 19:45:02 -05:00
Scott Tolksdorf
582deb7bf7 Updating todo 2016-11-14 19:39:25 -05:00
Scott Tolksdorf
8c4a6221e2 Fixing spelling mistake 2016-11-10 10:54:00 -05:00
Scott Tolksdorf
58c67d7d4d Update README.md 2016-11-07 19:14:12 -05:00
Scott Tolksdorf
9fe52145e6 Merge branch 'htmlValidate' 2016-11-07 19:10:53 -05:00
Scott Tolksdorf
b671161044 Updating changelog 2016-11-07 19:10:38 -05:00
Scott Tolksdorf
a439c418ef Updating welcome msg and issue template 2016-11-07 19:02:43 -05:00
Scott Tolksdorf
b8effb1ed1 Updated todo 2016-11-07 18:45:37 -05:00
Scott Tolksdorf
e576e6d971 Added QoL, where if errors are present, we run the checker on text input to give quicker feedback 2016-09-24 10:25:10 -04:00
Scott Tolksdorf
9f05556bc5 Error bar added to edit page 2016-09-24 10:25:10 -04:00
Scott Tolksdorf
2a0c06cd3d New error bar made 2016-09-24 10:25:10 -04:00
Scott Tolksdorf
dd1264d2e6 New html validator is working 2016-09-24 10:25:10 -04:00
Scott Tolksdorf
c49321d590 Creating new validator 2016-09-24 10:25:10 -04:00
Scott Tolksdorf
cd2337ff2c Experimenting with validation more 2016-09-24 10:24:38 -04:00
Scott Tolksdorf
9eb26a95f3 IMproved validator logic 2016-09-24 10:24:38 -04:00
Scott Tolksdorf
c7ccf6747c Updating project version 2016-09-24 10:24:38 -04:00
Scott Tolksdorf
9a96eebdb1 Adding my own markdown-html validator, still needs line numbers though 2016-09-24 10:24:01 -04:00
Scott Tolksdorf
54542a8ec1 Merge pull request #203 from lasseborly/minorDocumentationChanges
Minor documentation changes
2016-09-19 18:55:08 -04:00
Lasse Borly
efddfd31ee added the required changes before merge 2016-09-19 23:10:56 +02:00
Lasse Borly
0690216222 Corrected the license 2016-09-18 03:55:51 +02:00
Lasse Borly
9e54fe26d9 corrrecting small readme mistakes 2016-09-18 03:53:06 +02:00
Lasse Borly
01e4e11ef7 got rid of the global dependence on gulp 2016-09-18 03:51:20 +02:00
Scott Tolksdorf
7dc39e962b Merge pull request #202 from lasseborly/missingGulpDependencies
added missing dependencies
2016-09-17 21:25:41 -04:00
Lasse Borly
9adf28171e added missing dependencies 2016-09-18 03:20:22 +02:00
Scott Tolksdorf
152194aba8 Changing the print button to say get PDF. Should be more clear to users 2016-09-14 09:53:27 -04:00
Scott Tolksdorf
a107d59d72 Fixing state and props on navbar 2016-09-14 09:53:27 -04:00
Scott Tolksdorf
e6be1ae12f Updating to allow code folding 2016-08-31 13:33:01 -04:00
Scott Tolksdorf
460585f9c9 Admin page link quickfix 2016-08-20 19:58:33 -04:00
Scott Tolksdorf
143c006298 Merge branch 'various' 2016-08-20 13:44:11 -04:00
Scott Tolksdorf
43ec212be9 Fixed a bad route in the admin page 2016-08-20 13:40:32 -04:00
Scott Tolksdorf
78426135c6 Note blocks now don't overlap titles 2016-08-20 13:33:15 -04:00
Scott Tolksdorf
b134e11a86 Fixed a unclick bug with the splitpane 2016-08-20 13:29:28 -04:00
Scott Tolksdorf
888d3faa4c Editor and renderer redraw when you resize the browser 2016-08-20 13:26:48 -04:00
Scott Tolksdorf
ef8784ccf2 Homebrewery will now try and insert a better title for you if you dont provide one 2016-08-20 13:20:32 -04:00
Scott Tolksdorf
0a7b538216 Added in the new coverpage snippet 2016-08-20 13:02:52 -04:00
Scott Tolksdorf
019383ebdd Improved the html parsing slightly 2016-08-20 12:44:39 -04:00
Scott Tolksdorf
90c695c005 Visitng a deleted brew will now remove it from your recent list 2016-08-20 12:33:11 -04:00
Scott Tolksdorf
6b337b5d69 Added in a much better error page 2016-08-20 12:23:24 -04:00
Scott Tolksdorf
62d70022e7 Package version is now loaded into the navbar 2016-08-20 12:02:48 -04:00
Scott Tolksdorf
cd454e82ef Updating the homepage a bit 2016-08-20 11:50:22 -04:00
Scott Tolksdorf
8a7e441724 Adding license file and changing welcome message extension 2016-08-20 11:43:27 -04:00
Scott
1482501574 Update changelog.md 2016-08-17 13:33:57 -04:00
Scott Tolksdorf
f22fd73162 trying to force backgroudn graphics 2016-08-09 13:37:35 -04:00
Scott Tolksdorf
91c7f45d33 Converting raw mongoose objects to json 2016-08-09 13:32:46 -04:00
Scott Tolksdorf
34b21703e1 Maing staging work again 2016-08-09 13:28:45 -04:00
Scott Tolksdorf
73e561beba Fixed a security issue on share pages 2016-08-09 12:53:47 -04:00
Scott Tolksdorf
388ae933f8 rmeoving the top api call, as it was never finished and opened a security issue. 2016-08-06 21:08:37 -06:00
Scott Tolksdorf
33fd991276 ADding in stlying and snippet for a descriptive text box 2016-07-29 01:26:20 -04:00
Scott Tolksdorf
e31cb003eb Updating the new server creds 2016-07-21 19:10:21 -04:00
Scott Tolksdorf
46581cfcf5 Hot fix to test prod 2016-07-21 18:25:04 -04:00
Scott Tolksdorf
073b547f96 Added a hybrid navitem for both recently edited and viewed brews 2016-07-07 09:43:24 -04:00
Scott Tolksdorf
3ff736b440 Adding a non-chrome warning 2016-06-19 19:31:54 -04:00
Scott Tolksdorf
ead975b605 Adding in page ids to allow for hyperlinking 2016-06-10 12:16:46 -04:00
Scott Tolksdorf
cd97b22067 Fixing auto-incrmetning page numbers with partial page rendering 2016-06-09 08:03:54 -04:00
Scott Tolksdorf
e38850f807 Created new wrapper for my markdown parser, added it to the print page 2016-06-07 09:50:50 -04:00
Scott Tolksdorf
8df4dc56b2 Merge branch 'blockTweaks' 2016-06-05 13:50:55 -04:00
Scott Tolksdorf
235664ec88 updating the standalone css file 2016-06-05 13:49:21 -04:00
Scott Tolksdorf
3e796501e2 Fied lists in stat blocks 2016-06-05 13:47:59 -04:00
Scott Tolksdorf
0d25a972ba Added in new auto-incrementing snippet 2016-06-05 13:09:37 -04:00
Scott Tolksdorf
5969e45087 Updated the class tbale snippets 2016-06-05 12:55:29 -04:00
Scott Tolksdorf
f058814040 Adding in a new classTable block 2016-06-05 12:49:55 -04:00
Scott Tolksdorf
038c89fc20 Updating welcome tet 2016-06-04 18:58:10 -04:00
Scott Tolksdorf
d9bf164010 Updating welcome tet 2016-06-04 18:37:06 -04:00
Scott Tolksdorf
39d5d5c458 Updating more routes, looking good 2016-06-04 18:34:48 -04:00
Scott Tolksdorf
d2a9b3f274 Removing the ability for the changelog to get into the recent brews 2016-06-04 18:30:26 -04:00
Scott Tolksdorf
7ca4e8ffa6 Swapped over all urls and refs to old url scheme 2016-06-04 18:29:25 -04:00
Scott Tolksdorf
60092f404c Switching over project to just be the homebrewery 2016-06-04 17:51:30 -04:00
Scott Tolksdorf
c50de28900 Adding in the extra wide block 2016-05-31 19:47:13 -04:00
Scott Tolksdorf
6611bc9eff Merge branch 'v2.1' 2016-05-29 13:46:47 -04:00
Scott Tolksdorf
802103ff27 Paragrapghs now indent after lists 2016-05-29 13:45:02 -04:00
Scott Tolksdorf
9950c747da Updating the standalone file 2016-05-29 13:31:04 -04:00
Scott Tolksdorf
2b2b6b2793 Cleaning up the brewrender 2016-05-29 13:26:34 -04:00
Scott Tolksdorf
6474825ffb Last tweaks 2016-05-29 13:19:21 -04:00
Scott Tolksdorf
7eb47d7db0 Updated welcome text for v2.1 2016-05-29 12:58:44 -04:00
Scott Tolksdorf
b8c55d72a1 updating changelog and bumping the remembered value to 8 items 2016-05-29 12:53:36 -04:00
Scott Tolksdorf
87af0e8cb7 Recently viewed and edited seem to be working great 2016-05-29 12:50:18 -04:00
Scott Tolksdorf
63dcbfa388 Updating changelog 2016-05-29 11:35:54 -04:00
Scott Tolksdorf
8a7bffce5d Cleaning up the merge 2016-05-29 11:24:26 -04:00
Scott Tolksdorf
715f2f41bb Merge branch 'divRendering' into v2.1 2016-05-29 11:23:27 -04:00
Scott Tolksdorf
b33ef939ed Split the spell gen into a spell and spell list gen 2016-05-29 11:23:03 -04:00
Scott Tolksdorf
00bf12ecb7 Spelllist is rendering nicely 2016-05-29 11:22:30 -04:00
Scott Tolksdorf
c993ae9cf5 Woh, it is working 2016-05-29 11:22:30 -04:00
Scott Tolksdorf
626cba6062 Newlines 2016-05-28 09:41:05 -04:00
Scott Tolksdorf
14b7d60856 Adding in mac support for print hijack 2016-05-27 09:26:45 -04:00
Scott Tolksdorf
5a1041cbb3 styling for the new save current button done 2016-05-27 09:26:40 -04:00
Scott Tolksdorf
56bb0e0ad8 Ctrl p is now hijacked on the edit and share page to auto open to the brews print page 2016-05-27 09:25:38 -04:00
Scott
dce3f224c7 Better issue template and better print pdf instructions 2016-05-27 09:14:30 -04:00
Scott Tolksdorf
01e040d1ab Adding note to the welcome message to try Chrome fro print to PDF 2016-05-26 23:05:30 -04:00
Scott Tolksdorf
fa138499af Ability score improvements in the generators now follow the proper progressions 2016-05-25 09:53:45 -04:00
Scott Tolksdorf
7cc70149da Table generators now follow the proper prof bonus from the phb 2016-05-24 21:50:55 -04:00
Scott Tolksdorf
2ed9395e29 Wrapping up the fix 2016-05-24 21:35:03 -04:00
Scott
53529d14eb Fixed on a4 wide monster stat blocks go single column 2016-05-24 09:02:01 -04:00
Scott Tolksdorf
7501342e85 Updating the version and changelog 2016-05-19 08:52:01 -04:00
Scott Tolksdorf
68fc0f95d1 Save button now will display an error box with a custom link to an github issue 2016-05-19 08:49:55 -04:00
Scott Tolksdorf
52ff2e41e6 new error ahndler nearly done 2016-05-19 08:35:37 -04:00
Scott Tolksdorf
e7f1083edb Upping the express bodyPRasrserjson limit to 25mb 2016-05-19 08:21:26 -04:00
Scott Tolksdorf
6209fcd5cb Brew content will now not server render jsut incase it has malformed html, so it wont break the page 2016-05-18 00:20:07 -04:00
Scott Tolksdorf
fc3587d6cb Adding a search to the admin panel 2016-05-17 22:48:06 -04:00
287 changed files with 14680 additions and 32246 deletions

7
.bithoundrc Normal file
View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"unused-ignores": [
"react-dom"
]
}
}

32
.circleci/config.yml Normal file
View File

@@ -0,0 +1,32 @@
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
docker:
- image: circleci/node:12.16.3
- image: circleci/mongo:3.4-jessie
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: npm install
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
# run tests!
- run: npm run circleci

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
node_modules
npm-debug.log
.git
Dockerfile
docker-compose.yml
tests

78
.eslintrc.js Normal file
View File

@@ -0,0 +1,78 @@
module.exports = {
root : true,
parserOptions : {
ecmaVersion : 9,
sourceType : 'module',
ecmaFeatures : {
jsx : true
}
},
env : {
browser : true,
node : true
},
plugins : ['react'],
rules : {
/** Errors **/
'camelcase' : ['error', { properties: 'never' }],
'func-style' : ['error', 'expression', { allowArrowFunctions: true }],
'no-array-constructor' : 'error',
'no-iterator' : 'error',
'no-nested-ternary' : 'error',
'no-new-object' : 'error',
'no-proto' : 'error',
'react/jsx-no-bind' : ['error', { allowArrowFunctions: true }],
'react/jsx-uses-react' : 'error',
'react/prefer-es6-class' : ['error', 'never'],
/** Warnings **/
'max-lines' : ['warn', {
max : 200,
skipComments : true,
skipBlankLines : true,
}],
'max-depth' : ['warn', { max: 4 }],
'max-params' : ['warn', { max: 4 }],
'no-restricted-syntax' : ['warn', 'ClassDeclaration', 'SwitchStatement'],
'no-unused-vars' : ['warn', {
vars : 'all',
args : 'none',
varsIgnorePattern : 'config|_|cx|createClass'
}],
'react/jsx-uses-vars' : 'warn',
/** Fixable **/
'arrow-parens' : ['warn', 'always'],
'brace-style' : ['warn', '1tbs', { allowSingleLine: true }],
'jsx-quotes' : ['warn', 'prefer-single'],
'no-var' : 'warn',
'prefer-const' : 'warn',
'prefer-template' : 'warn',
'quotes' : ['warn', 'single', { 'allowTemplateLiterals': true }],
'semi' : ['warn', 'always'],
/** Whitespace **/
'array-bracket-spacing' : ['warn', 'never'],
'arrow-spacing' : ['warn', { before: false, after: false }],
'comma-spacing' : ['warn', { before: false, after: true }],
'indent' : ['warn', 'tab', { 'MemberExpression': 'off' }],
'keyword-spacing' : ['warn', {
before : true,
after : true,
overrides : {
if : { 'before': false, 'after': false }
}
}],
'key-spacing' : ['warn', {
multiLine : { beforeColon: true, afterColon: true, align: 'colon' },
singleLine : { beforeColon: false, afterColon: true }
}],
'linebreak-style' : 'off',
'no-trailing-spaces' : 'warn',
'no-whitespace-before-property' : 'warn',
'object-curly-spacing' : ['warn', 'always'],
'react/jsx-indent-props' : ['warn', 'tab'],
'space-in-parens' : ['warn', 'never'],
'template-curly-spacing' : ['warn', 'never'],
}
};

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
package-lock.json binary

View File

@@ -1,14 +1,28 @@
**Browser Type/Version**: [Google Ultron v90.01]
<!-- CLICK "Preview" FOR INSTRUCTIONS IN A MORE READABLE FORMAT -->
**Operating System**: [GLaDOS v34.5.8]
## Before you submit
**Issue Description**: [The thing won't thing]
- Support questions are better asked on the subreddit [r/homebrewery](https://www.reddit.com/r/homebrewery/)
- Read the [contributing guidelines](https://github.com/stolksdorf/homebrewery/blob/master/contributing.md).
- If it's an issue, please make sure it's reproducible
- Ensure the issue isn't already reported.
**Markdown code to reproduce**:
```
# thing
> thing 2
```
*Delete the above section and the instructions in the sections below before submitting*
**Related Images** :
## Description
If this is a *feature request*, explain why it should be added. Specific use-cases are best.
For *bug reports*, please provide as much *relevant* info as possible.
**Share Link** :
or
**Brew code to reproduce** : <details><summary>Click to expand</summary><code><pre>
PASTE BREW CODE HERE
</pre></code></details>

28
.gitignore vendored
View File

@@ -1,16 +1,12 @@
# Logs
logs
*.log
#Ignore our built files
build/*
architecture.json
# Ignore sensitive stuff
/config/*
!/config/default.json
node_modules
storage
.idea
*.swp
node_modules
storage
.idea
*.swp
*.log
build/*
config/local.*
todo.md
startDB.bat
startMViewer.bat

View File

@@ -1,29 +1,19 @@
FROM node:latest
FROM node:8
MAINTAINER David Hudson <jendave@yahoo.com>
ENV NODE_ENV=docker
# System update
RUN apt-get -q -y update
# Create app directory
WORKDIR /usr/src/app
RUN apt-get -q -y install npm
RUN apt-get -q -y install mongodb
# Copy package.json into the image, then run yarn install
# This improves caching so we don't have to download the dependencies every time the code changes
COPY package.json ./
# --ignore-scripts tells yarn not to run postbuild. We run it explicitly later
RUN yarn install --ignore-scripts
RUN apt-get clean && rm -r /var/lib/apt/lists/*
# Bundle app source and build application
COPY . .
RUN yarn build
EXPOSE 22
EXPOSE 8000
ADD start.sh /start.sh
RUN chmod +x /start.sh
VOLUME ["/opt/apps"]
COPY . /opt/apps/naturalcrit/
WORKDIR /opt/apps/naturalcrit/
RUN npm install
RUN npm install -g gulp-cli
RUN npm install gulp
RUN gulp fresh
CMD ["/start.sh"]
CMD [ "yarn", "start" ]

12
README.DOCKER.md Normal file
View File

@@ -0,0 +1,12 @@
# Running Homebrewery via Docker
The repo includes a Dockerfile and a docker-compose.yml file.
To run the application via docker-compose.yml:
`docker-compose up -d`
To stop the application:
`docker-compose down`
To stop the application and remove all data:
`docker-compose down -v`

35
README.FREEBSD.md Normal file
View File

@@ -0,0 +1,35 @@
# FreeBSD/FreeNAS Installation Instructions
## Before Installing
These instructions assume that you are installing to a completely new, fresh FreeBSD/FreeNAS jail. As such, some steps will not be necessary if you are installing to an existing FreeBSD/FreeNAS install.
## Installation instructions
1. Create a new jail, with the appropriate network settings to access the internet.
2. Install wget (`pkg install -y wget`). On a fresh jail, you will be prompted to press 'Y' to set up `pkg`.
3. Download the installation script (`wget --no-check-certificate https://raw.githubusercontent.com/naturalcrit/homebrewery/master/freebsd/install.sh`). The parameter `--no-check-certificate` is required as we haven't set up any trusted certificates/authorities yet.
4. Make the downloaded file executable (`chmod +x install.sh`).
5. Run the script (`./install.sh`). This will automatically download all of the required packages, install both them and HomeBrewery, configure the system and finally start HomeBrewery.
**NOTE:** At this time, the script **ONLY** installs HomeBrewery. It does **NOT** install the NaturalCrit login system, as that is currently a completely separate project.
---
### Testing
These installation instructions have been tested on the following FreeBSD/FreeNAS platforms:
* FreeNAS-11.3-U5; Jail 11.4-RELEASE-p2
## Final Notes
While this installation process works successfully at the time of writing (December 28, 2020), it relies on all of the Node.JS packages used in the HomeBrewery project retaining their cross-platform capabilities to continue to function under FreeBSD. This is one of the inherent advantages of Node.JS, but it is by no means guaranteed and as such, functionality or even installation under FreeBSD may fail without warning at some point in the future.
Regards,
G
December 28, 2020

160
README.md
View File

@@ -1,33 +1,127 @@
# NaturalCrit
A tool suite for DMs to use for D&D. Check it out [here](http://www.naturalcrit.com).
### Getting started
1. Make sure you have [node](https://nodejs.org/en/)
1. Clone down the repo
1. In your terminal, head to the repo
1. Run `npm install` to get all the dependacies
2. Run `npm install -g gulp` to install the gulp build tool
1. Run `gulp fresh`, this will compile and build all the needed libraries (this only has to be done once, unless you add more libs)
1. Run `gulp` to run the project locally. Should be accessible at `localhost:8000`
2. Any changes to files within the proejct will be detected and the propject will automatically re-build
**Notes:** If you'd like to create and edit homebrews, you'll need to have MongoDB installed and running.
Have fun!
### Docker Image
You can use [Docker](https://docs.docker.com) to get up and running with NaturalCrit.
1. Install Docker
1. Clone the repo
1. In the terminal, go to the repo
1. Build the docker image `docker build -t naturalcrit .`
1. Run the docker container `docker run -dit -p 8000:8000 naturalcrit`
1. You can check out the website on your computer on port 8000
1. You may have to use `docker-machine env` to get the IP address of your docker instance
### changelog
You can check out the changelog [here](https://github.com/stolksdorf/NaturalCrit/blob/master/changelog.md)
# The Homebrewery
[![Homebrewery](https://circleci.com/gh/naturalcrit/homebrewery/tree/master.svg?style=svg)](https://app.circleci.com/pipelines/github/naturalcrit/homebrewery?branch=master)
The Homebrewery is a tool for making authentic looking [D&D content][dnd-content-url]
using [Markdown][markdown-url]. It is distributed under the terms of the [MIT License](./license).
[dnd-content-url]: https://dnd.wizards.com/products/tabletop-games/rpg-products/rpg_playershandbook
[markdown-url]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
## Quick Start
The easiest way to get started using the Homebrewery is to use it
[on our website][homebrewery-url]. The code is open source, so feel free to
clone it, tinker with it. If you want to make changes to the code, you can run
your own local version for testing by following the installation instructions
below.
[homebrewery-url]: https://homebrewery.naturalcrit.com
### Installation
First, install three programs that the Homebrewery requires to run and retrieve
updates:
1. install [node](https://nodejs.org/en/)
1. install [mongodb](https://www.mongodb.com/try/download/community) (Community version)
For easiest installation, follow these steps:
1. In the installer, uncheck the option to run as a service
1. You can install MongoDB Compass if you want a GUI to view your database documents
1. Go to the C drive and create a folder called "data"
1. Inside the "data" folder, create a new folder called "db"
1. Open a command prompt or other terminal and navigate to your mongodb install folder (c:program files\mongo\server\4.4\bin)
1. In the command prompt, run "mongod", which will start up your local database server
1. While MongoD is running, open a second command prompt and navigate to the mongodb install folder
1. In the second command prompt, run "mongo", which allows you to edit the database
1. Type `use homebrewery` to create the homebrewery database. You should see `switched to db homebrewery`
1. Type `db.brews.insert({"title":"test"})` to create a blank document. You should see `WriteResult({ "nInserted" : 1 })`
1. Search in Windows for "Advanced system settings" and open it
1. Click "Environment variables", find the "path" variable, and double-click to open it
1. Click "New" and paste in the path to the mongodb "bin" folder
1. Click "OK", "OK", "OK" to close all the windows
1. install [git](https://git-scm.com/downloads) (select the option that allows Git to run from the command prompt)
Checkout the repo ([documentation][github-clone-repo-docs-url]):
```
git clone https://github.com/naturalcrit/homebrewery.git
```
[github-clone-repo-docs-url]: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/cloning-a-repository
Second, you will need to add the environment variable `NODE_ENV=local` to allow
the project to run locally.
You can set this temporarily in your shell of choice:
* Windows Powershell: `$env:NODE_ENV="local"`
* Windows CMD: `set NODE_ENV=local`
* Linux / OSX: `export NODE_ENV=local`
Third, you will need to install the Node dependencies, compile the app, and run
it using the two commands:
1. `npm install`
1. `npm start`
You should now be able to go to [http://localhost:8000](http://localhost:8000)
in your browser and use the Homebrewery offline.
### Running the application via Docker
Please see the docs here: [README.DOCKER.md](./README.DOCKER.md)
### Running the application on FreeBSD or FreeNAS
Please see the docs here: [README.FreeBSD.md](./README.FREEBSD.md)
### Standalone PHB Stylesheet
If you just want the stylesheet that is generated to make pages look like they
are from the Player's Handbook, you will find it in the
[phb.standalone.css](./phb.standalone.css) file.
If you are developing locally and would like to generate your own, follow the
above steps and then run `npm run phb`.
## Issues, Suggestions, and Bugs
If you run into any issues using The Homebrewery or have suggestions for
improvement, please submit an issue [on GitHub][repo-issues-url].
You can also get help for issues on the subreddit [r/homebrewery][subreddit-url]
[repo-issues-url]: https://github.com/naturalcrit/homebrewery/issues
[subreddit-url]: https://www.reddit.com/r/homebrewery
## Changelog
You can check out the [changelog](./changelog.md).
## License
This project is licensed under the [MIT license](./license). Which means you
are free to use The Homebrewery in any way that you want, except for claiming
that you made it yourself.
If you wish to sell or in some way gain profit for what's created on this site,
it's your responsibility to ensure you have the proper licenses/rights for any
images or resources used.
## Contributing
You are welcome to contribute to the development and maintenance of the
project! There are several ways of doing that:
- At the moment, we have a huge backlog of [issues][repo-issues-url] and some
of them are outdated, duplicates or doesn't contain any useful info. In order
to help you can [mark duplicates][github-mark-duplicate-url], try to
reproduce some complex or weird issues, try with finding a workaround for a
reported bug or just mention issue managers team to let them know about
outdated issue via `@naturalcrit/issue-managers`.
- Our [subreddit][subreddit-url] is constantly growing and there are number of
bug reports: any help with sorting them out is very welcome.
- And of course you can contribute by fixing a bug or implementing a new
feature by yourself, we are waiting for your
[pull requests][github-pr-docs-url]!
Anyway, if you would like to get in touch with the team and discuss/coordinate
your contribution to the project, please join our [gitter chat][gitter-url].
[github-mark-duplicate-url]: https://docs.github.com/en/free-pro-team@latest/github/managing-your-work-on-github/about-duplicate-issues-and-pull-requests
[github-pr-docs-url]: https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request
[gitter-url]: https://gitter.im/naturalcrit/Lobby

View File

@@ -1,111 +1,323 @@
# changelog
### Saturday, 14/05/2016 - v2.0.0 (finally!)
I've been working on v2 for a *very* long time. I want to thank you guys for being paitent.
It started rather small, but as more and more features were added, I decided to just wait until everything was polished.
Massive changelog incoming:
#### Brews
- New flow for creating new brews. Before creating a new brew would immedaitely create a brew in the database and let you edit it. Many people would create a new brew just to experiment and close it, which lead to many "abandoned brews" (see the Under the hood section below). This started eating up a ton of database space. You now have to manually save a new brew for the first time, however Homebrewery will store anything you don't have saved in local storage, just in case your browser crashes or whatever, it will load that up when you go back to `/homebrew/new`
- Black blocking edges around notes and stat blocks when printing to PDFs have been fixed
- Borders sometimes not showing up in the second column have been fixed
- All pseudo-element borders have been replaced with reliable border images.
- Chrome can finally print to PDF as good as Chrome Canary! Updating instructions.
- Added a little page number box.
- Added in a new editable Brew Title. This will be shown in the navbar on share pages, and will default to the file name when you save as PDF. All exsisting brews will be defaulted with an empty title.
- Mutliline lists render better now
- Firefox rendering has been slithgly improved. Firefox and Chrome render things **slightly** differently, over the course of a brew, these little changes add up and lead to very noticable rendering differences between the browsers. I'm trying my best to get Firefox rendering better, but it's a difficult problem.
- A bunch of you have wanted to donate to me. I am super flattered by that. I created a [patreon page](https://www.patreon.com/stolksdorf). If you feel like helping out, head here :)
#### Under the Hood Stuff
- Setup a proper staging environment. Will be using this for tests, and hopefully getting the community to help me test future versions
- Server-side prerendering now much faster
- Regular weekly database back-ups. Your brews are safe!
- Database is now uniquely indexed on both editId and shareId, page loads/saving should be much faster
- Improved Admin console. This helps me answer people's questions about issues with their brews
- Added a whole querying/pagniation API that I can use for stats and answering questions
- Clearing out "Abandoned" brews (smaller than a tweet and haven't been viewed for a week). These account for nearly a third of all stored brews.
#### Interface
- Added in a whole new editor with syntax highlighting for markdown
- Built a splitpane! Remembers where you left the split in between sessions
- Re-organized the snippets into a hierarchical groups. Should be much easier to find what you need
- Partial page rendering is working. The Homebrewery will now only load the viewable pages, and any page with `<style>` tags on them. If you are working on a large brew you should notice *significant* performance improvements
- Edit page saving interface has been improved significantly. Auto-saves after 3 seconds on inactivity, now allows user to save at anytime. Will stop the tab from closing witha pop-up if there are unsaved changes.
- Navbar and overall style has been improved and spacing made more consistent
- Elements under the hood are way more organized and should behaviour much more reliably in many sizes.
- Source now opens to it's own route `/source/:sharedId` instead of just a window. Now easier to share, and won't be blocked by some browsers.
- Print page now auto-opens print dialog. If you want to share your print page link, just remove the `?dialog=true` parameter and it won't open the dialog.
\page
### Wednesday, 20/04/2016
- A lot of admin improvements. Pagninated brew table
- Added a searching and removing abandoned brew api endpoints (turns out about 40% of brews are shorter that a tweet!).
- Fixed the require cache being cleared. Pages should render a bit faster now.
- Pulled in `kkragenbrink`s fix for nested lists, Thanks!
### Wednesday, 06/04/2016 - v1.4
* Pages will now partially render. This should greatly speed up *very* large homebrews. The Homebreery will figure out which page you should be looking at and render that page, the page before, and the page after.
* Zooming should be fixed. I've changed the font size units to be cm, which match the units of the page. Zooming in and out now look much better.
### Monday, 29/02/2016 - v1.3.1
* Removng the changelog button from the Share page
* Added a A4 page size snippet (thanks guppy42!)
* Added support for `<sup>` and `<sub>` tags (thanks crashinworld14!)
### Saturday, 20/02/2016
* Fixed h1 headers not going full width (thanks McToomin27)
* Added a github issue template
## v1.3.0
### Friday, 19/02/2016
* Improved the admin panel
* Added ability to clear away old empty brews
* Added delete button to the edit page
* Added a dynamically updating changelog page! Nifty!
* Added stlying for wide monster stat blocks and single column class tables
* Added snippets for wide monster stat blocks and single column class tables
### Tuesday, 16/02/2016
* Paragraphs right after tables now indent (thanks LikeAJi6!)
* Added a `@page` css rule to auto turn off margins when printing
* Added a `page-break` property on each `.phb` page to properly page the pages up when exporting (thanks Jokefury!)
* Improved first character rendering on Firefox
* Improved table spacing a bit
* Changed padding at page bottom for better fit and clipping of elements
* Improved spacing for bold text (thanks nickpunt!)
## v1.2.0
### Sunday, 17/01/2016
* Added a printer friendly snippet that injects some CSS to remove backbrounds and images
* Adjusted the styling specific to spell blocks to give it tighter spacing
* Added a changelog! How meta!
## v1.1.0
### Thursday, 14/01/2016
* Added view source to see the markdown that made the page
* Added print view
* Fixed API issues that were causing the server to crash
* Increased padding on table cells
* Raw html now shows in view source
## v1.0.0 - Release
### Wednesday, 3/01/2016
* Added `phb.standalone.css` plus a build system for creating it
* Added page numbers and footer text
* Page accent now flips each page
# changelog
### Friday, 25/1/2021 - v2.10.7
- Cover Page snippet now flips left-right page numbering.
- Added instructions for [installing on a FreeBSD Jail](https://github.com/naturalcrit/homebrewery/blob/master/README.FREEBSD.md).
- Fix for box-shadows breaking across columns. <br>(Thanks @G-Ambatte for all of these!)
- Small user interface tweaks (Thanks @Ericsheid)
### Friday, 02/1/2021 - v2.10.6
- Fixed punctuation for usernames ending with 's' on the user page. (Thanks @AlexeySachkov)
- Fixed server crashes due to excessive long lines in brews
- Fixed "automated request" lockouts from Google
### Friday, 18/12/2020 - v2.10.5
- Brews now immediately save when transferring between Google Drive and Homebrewery storage.
- Added confirmation popup to clarify the transfer process.
- Brews transferred or deleted from Google will be found in your Google Drive trash.
- Dependency updates.
### Wednesday, 25/11/2020 - v2.10.4
- Fixed Google Drive brews not saving metadata (view count, description, etc.) Note that we are still working on making published Google brews visible to the public when viewing your profile page.
### Thursday, 22/10/2020 - v2.10.3
- Fixed brews with broken code crashing the edit page when loaded (the "blue screen of death" bug).
### Monday, 19/10/2020 - v2.10.2
- Fixed issue with "recent" item links not updating when transferring between Google Drive.
### Monday, 12/10/2020 - v2.10.1
- Fixed issue with users unable to create new brews
- Fixing brews being lost when loaded via back button
### Wednesday, 07/10/2020 - v2.10.0
- Google Drive integration -- Sign in with your Google account to link it with your Homebrewery profile. A new button in the Edit page will let you transfer your file to your personal Google Drive storage, and Google will keep a backup of each version! No more lost work surprises!
```
```
### Friday, 28/08/2020 - v2.9.2
- Many dependency updates
- Finally fixed this changelog page to not run off the edge :P
### Sunday, 19/07/2020 - v2.9.1
- Fixed paragraphs appearing blank on new columns
### Wednesday, 20/05/2020 - v2.9.0
- Major refactoring of site backend to work with updated dependencies for security (should be invisible to users)
### Wednesday, 11/03/2020 - v2.8.2
- Fixed delete button removing everyone's copy for brews with multiple authors
- Compressed homebrew text in database
### Monday, 26/11/2018 - v2.8.1
- Fixed some SSL issues with images in the example page so they appear now
- Fixed duplicate scrollbars in Edit Page
- Fixed issue of being unable to change brew metadata
- Sanitized script tags-javascript typed into the editor was crashing brews
### Sunday, 08/04/2018 - v2.8.0
- Re-enabled box shadows for PDF output
- Added a "contributing guide" for the GitHub
- "Report Issue" navbar button now links to the subreddit
- Refactored background code
### Sunday, 04/06/2017 - v2.7.5
- Fixed the class feature snippet duplicating the entire brew
- Fixed headers in tables being duplicated
- Fixed border-image being scrambled on class tables and descriptive text boxes
- Fixed pages going out of sync in large brews, causing them to be rendered off-page
- Improved performance in the preview window when scrolling through large brews
- Text in the "view source" page now wraps
### Saturday, 22/04/2017 - v2.7.4
- Give ability to hide the render warning notification
### Friday, 03/03/2017 - v2.7.3
- Increasing the range on the Partial Page Rendering for a quick-fix for it getting out of sync on long brews.
### Saturday, 18/02/2017 - v2.7.2
- Adding ability to delete a brew from the user page, incase the user creates a brew that makes the edit page unrender-able. (re:309)
\page
### Thursday, 19/01/2017 - v2.7.1
- Fixed saving multiple authors and multiple systems on brew metadata (thanks u/PalaNolho re:282)
- Adding in line highlight for new pages
- Added in a simple brew lookup for admin
### Saturday, 14/01/2017 - v2.7.0
- Added a new Render Warning overlay. It detects situations where the brew may not be rendering correctly (wrong browser, browser is zoomed in...) and let's the user know
### Sunday, 25/12/2016 - v2.7.0
- Switching over to using Vitreum v4
- Removed gulp, all tasks are run through npm scripts
- Updating docs for local dev
- Removing support for Docker. I have never used it, nor will I ever test for it, so I don't want to continue to explictly support it on this repo. Feel free to make a fork and make it docker-able though :)
- Changed icon for the metadata
- Made links useable in footer (thanks u/Dustfinger1 re:249)
- Added print media queries to remove box shadow on print (thanks u/dmmagic re: 246)
- Fixed realtime renderer not functioning if loaded with malformed html on load (thanks u/RattiganIV re:247)
- Removed a lot of unused files in shared
- vitreum v4 now lets me use codemirror as a pure node dependacy
### Saturday, 03/12/2016 - v2.6.0
- Added report back to the edit page
- Changed metaeditor icon
- Added a button to quickly share your brew to reddit :)
- Disabled Partial Page Rendering unless your brew hits 75 pages or longer
- The brew renderer will now try and use your first page to judge the page size of each of your brews. This allows you now to set landscape and other weird sizes and everthing should work fine :)
- UI on the user page improved (thanks u/PalaNolho)
- Fixed lists not breaking across columns (thanks u/tyson-nw)
- Added a table of contents snippet (thanks u/tullisar)
- Added a multicolumn snippet
### Thursday, 01/12/2016
- Added in a snippet for a split table
- Added an account nav item to new page
### Sunday, 27/11/2016 - v2.5.1
- Fixed the column rendering on the new user page. Really should have tested that better
- Added a hover tooltip to fully read the brew description
- Made the brew items take up only 25% allowing you to view more per row.
```
```
### Wednesday, 23/11/2016 - v2.5.0
- Metadata can now be added to brews
- Added a metadata editor onto the edit and new pages
- Moved deleting a brew into the metadata editor
- Added in account middleware
- Can now search for brews by a specific author
- Editing a brew in anyway while logged in will now add you to the list of authors
- Added a new user page to see others published brews, as well as all of your own brews.
- Added a new nav item for accessing your profile and logging in
### Monday, 14/11/2016
- Updated snippet bar style
- You can now print from a new page without saving
- Added the ability to use ctrl+p and ctrl+s to print and save respectively.
### Monday, 07/11/2016
- Added final touches to the html validator and updating the rest of the branch
- If anyone finds issues with the new HTML validator, please let me know. I hope this will bring a more consistent feel to Homebrewery rendering.
### Friday, 09/09/2016 - v2.4.0
- Adding in a HTML validator that will display warnings whenever you save. This should stop a lot of the issues generated with pages not showing up.
### Saturday, 20/08/2016 - v2.3.0
- Added in a license file
- Updated the welcome text
- Added in a much better Error page
- If you visit a deleted brew, it will now remove it from your recent list. (Thanks u/sIllverback!)
- Improved parsing of embedded html text in brews. (Thanks u/com-charizard!)
- Added in a new coverpage snippet
- Homebrewery will now try and onsert a good title for your brew if you don't provide one
- Homebrewery now re-renders properly when you zoom
- Fixed the noteblock overlapping into titles (thanks u/dsompura!)
- Fixed a bad search route in the admin panel (thanks u/SnappyTom!)
### Friday, 29/07/2016 - v2.2.7
- Adding in descriptive note blocks. (Thanks calculuschild!)
### Thursday, 07/07/2016 - v2.2.6
- Added a new nav item on the homepage for accessing both recently viewed and edited brews (thanks [ChosenSeraph!](https://github.com/stolksdorf/homebrewery/issues/147))
### Friday, 10/06/2016 - v2.2.4
- Added an id to each rendered page
- Allows adding in hyperlinks to specific pages
- Even works after you print to pdf!
\page
### Tuesday, 07/06/2016 - v2.2.2
- Fixed bug with new markdown lexer and aprser not working on print page
### Sunday, 05/06/2016 - v2.2.1
- Adding in a new Class table div block. The old Class table block used weird stacking of HTML elements, resulting is difficult to control behaviour and poor interactiosn with the rest of the page. This new block is much easier to style and work with.
- Added in a new wide table snippet
- Added in a new auto-incremeting page number snippet (thakns u/Ryrok!)
- Lists in monster stat blocks should be fixed now
### Saturday, 04/06/2016 - v2.2.0
- MIgrating The Homebrewery over to hombrewery.naturalcrit.com. It know runs on it's own server, with it's own repo separate from the other tools I'm working on. Makes updating and deploying much easier.
### Sunday, 29/05/2016 - v2.1.0
- Finally added a syntax for doing spell lists. A bit in-depth about why this took so long. Essentially I'm running out of syntax to use in stardard Markdown. There are too many unique elements in the PHB-style to be mapped. I solved this earlier by stacking certain elements together (eg. an `<hr>` before a `blockquote` turns it into moster state block), but those are getting unweildly. I would like to simply wrap these in `div`s with classes, but unfortunately Markdown stops processing when within HTML blocks. To get around this I wrote my own override to the Markdown parser and lexer to process Markdown within a simple div class wrapper. This should open the door for more unique syntaxes in the future. Big step!
- Override Ctrl+P (and cmd+P) to launch to the print page. Many people try to just print either the editing or share page to get a PDF. While this dones;t make much sense, I do get a ton of issues about it. So now if you try to do this, it'll just bring you imediately to the print page. Everybody wins!
- The onboarding flow has also been confusing a few users (Homepage -> new -> save -> edit page). If you edit the Homepage text now, a Call to Action to save your work will pop-up.
- Added a 'Recently Edited' and 'Recently Viewed' nav item to the edit and share page respectively. Each will remember the last 8 items you edited or viewed and when you viewed it. Makes use of the new title attribute of brews to easy navigatation.
- Paragraphs now indent properly after lists (thanks u/slitjen!)
### Friday, 27/05/2016 - v2.0.6
- Updated the issue template for (hopefully) better reporting
- Added suggestion to use chrome while PDF printing
```
```
### Wednesday, 25/05/2016 -v2.0.5
- The class table generators have the proper ability score improvement progression.
### Tuesday, 24/05/2016 - v2.0.4
- Fixed extra wide monster stat blocks sometimes only being one column
- The class table generators now follow the proper progression from the PHB (thakns u/IrishBandit)
### Thursday, 19/05/2016 - v2.0.2
- No longer server-side pre-render brews, just incase the user entered invalid HTML, it might crahsh the server
- Bumped up the allowed entity size for extra-large brew (Thanks for reporting it dickboner93)
- Added a little error box when a save fails with a custom link to reporting the issue on github.
\page
### Saturday, 14/05/2016 - v2.0.0 (finally!)
I've been working on v2 for a *very* long time. I want to thank you guys for being paitent.
It started rather small, but as more and more features were added, I decided to just wait until everything was polished.
Massive changelog incoming:
#### Brews
- New flow for creating new brews. Before creating a new brew would immedaitely create a brew in the database and let you edit it. Many people would create a new brew just to experiment and close it, which lead to many "abandoned brews" (see the Under the hood section below). This started eating up a ton of database space. You now have to manually save a new brew for the first time, however Homebrewery will store anything you don't have saved in local storage, just in case your browser crashes or whatever, it will load that up when you go back to `/homebrew/new`
- Black blocking edges around notes and stat blocks when printing to PDFs have been fixed
- Borders sometimes not showing up in the second column have been fixed
- All pseudo-element borders have been replaced with reliable border images.
- Chrome can finally print to PDF as good as Chrome Canary! Updating instructions.
- Added a little page number box.
- Added in a new editable Brew Title. This will be shown in the navbar on share pages, and will default to the file name when you save as PDF. All exsisting brews will be defaulted with an empty title.
- Mutliline lists render better now
- Firefox rendering has been slithgly improved. Firefox and Chrome render things **slightly** differently, over the course of a brew, these little changes add up and lead to very noticable rendering differences between the browsers. I'm trying my best to get Firefox rendering better, but it's a difficult problem.
- A bunch of you have wanted to donate to me. I am super flattered by that. I created a [patreon page](https://www.patreon.com/stolksdorf). If you feel like helping out, head here :)
#### Under the Hood Stuff
- Setup a proper staging environment. Will be using this for tests, and hopefully getting the community to help me test future versions
- Server-side prerendering now much faster
- Regular weekly database back-ups. Your brews are safe!
- Database is now uniquely indexed on both editId and shareId, page loads/saving should be much faster
- Improved Admin console. This helps me answer people's questions about issues with their brews
- Added a whole querying/pagniation API that I can use for stats and answering questions
- Clearing out "Abandoned" brews (smaller than a tweet and haven't been viewed for a week). These account for nearly a third of all stored brews.
#### Interface
- Added in a whole new editor with syntax highlighting for markdown
- Built a splitpane! Remembers where you left the split in between sessions
- Re-organized the snippets into a hierarchical groups. Should be much easier to find what you need
- Partial page rendering is working. The Homebrewery will now only load the viewable pages, and any page with `<style>` tags on them. If you are working on a large brew you should notice *significant* performance improvements
- Edit page saving interface has been improved significantly. Auto-saves after 3 seconds on inactivity, now allows user to save at anytime. Will stop the tab from closing witha pop-up if there are unsaved changes.
- Navbar and overall style has been improved and spacing made more consistent
- Elements under the hood are way more organized and should behaviour much more reliably in many sizes.
- Source now opens to it's own route `/source/:sharedId` instead of just a window. Now easier to share, and won't be blocked by some browsers.
- Print page now auto-opens print dialog. If you want to share your print page link, just remove the `?dialog=true` parameter and it won't open the dialog.
\page
### Wednesday, 20/04/2016
- A lot of admin improvements. Pagninated brew table
- Added a searching and removing abandoned brew api endpoints (turns out about 40% of brews are shorter that a tweet!).
- Fixed the require cache being cleared. Pages should render a bit faster now.
- Pulled in `kkragenbrink`s fix for nested lists, Thanks!
### Wednesday, 06/04/2016 - v1.4
* Pages will now partially render. This should greatly speed up *very* large homebrews. The Homebreery will figure out which page you should be looking at and render that page, the page before, and the page after.
* Zooming should be fixed. I've changed the font size units to be cm, which match the units of the page. Zooming in and out now look much better.
### Monday, 29/02/2016 - v1.3.1
* Removng the changelog button from the Share page
* Added a A4 page size snippet (thanks guppy42!)
* Added support for `<sup>` and `<sub>` tags (thanks crashinworld14!)
### Saturday, 20/02/2016
* Fixed h1 headers not going full width (thanks McToomin27)
* Added a github issue template
## v1.3.0
### Friday, 19/02/2016
* Improved the admin panel
* Added ability to clear away old empty brews
* Added delete button to the edit page
* Added a dynamically updating changelog page! Nifty!
* Added stlying for wide monster stat blocks and single column class tables
* Added snippets for wide monster stat blocks and single column class tables
### Tuesday, 16/02/2016
* Paragraphs right after tables now indent (thanks LikeAJi6!)
* Added a `@page` css rule to auto turn off margins when printing
* Added a `page-break` property on each `.phb` page to properly page the pages up when exporting (thanks Jokefury!)
* Improved first character rendering on Firefox
* Improved table spacing a bit
* Changed padding at page bottom for better fit and clipping of elements
* Improved spacing for bold text (thanks nickpunt!)
## v1.2.0
### Sunday, 17/01/2016
* Added a printer friendly snippet that injects some CSS to remove backbrounds and images
* Adjusted the styling specific to spell blocks to give it tighter spacing
* Added a changelog! How meta!
## v1.1.0
### Thursday, 14/01/2016
* Added view source to see the markdown that made the page
* Added print view
* Fixed API issues that were causing the server to crash
* Increased padding on table cells
* Raw html now shows in view source
## v1.0.0 - Release
### Wednesday, 3/01/2016
* Added `phb.standalone.css` plus a build system for creating it
* Added page numbers and footer text
* Page accent now flips each page

View File

@@ -1,40 +1,37 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
require('./admin.less');
const React = require('react');
const createClass = require('create-react-class');
var HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx');
var Admin = React.createClass({
getDefaultProps: function() {
return {
url : "",
admin_key : "",
homebrews : [],
};
const BrewCleanup = require('./brewCleanup/brewCleanup.jsx');
const BrewLookup = require('./brewLookup/brewLookup.jsx');
const BrewCompress = require ('./brewCompress/brewCompress.jsx');
const Stats = require('./stats/stats.jsx');
const Admin = createClass({
getDefaultProps : function() {
return {};
},
render : function(){
var self = this;
return(
<div className='admin'>
<header>
<div className='container'>
<i className='fa fa-rocket' />
naturalcrit admin
</div>
</header>
return <div className='admin'>
<header>
<div className='container'>
<a target="_blank" href='https://www.google.com/analytics/web/?hl=en#report/defaultid/a72212009w109843310p114529111/'>Link to Google Analytics</a>
<HomebrewAdmin homebrews={this.props.homebrews} admin_key={this.props.admin_key} />
<i className='fa fa-rocket' />
homebrewery admin
</div>
</header>
<div className='container'>
<Stats />
<hr />
<BrewLookup />
<hr />
<BrewCleanup />
<hr />
<BrewCompress />
</div>
);
</div>;
}
});

View File

@@ -1,39 +1,44 @@
@import 'naturalcrit/styles/reset.less';
@import 'naturalcrit/styles/elements.less';
@import 'naturalcrit/styles/animations.less';
@import 'naturalcrit/styles/colors.less';
@import 'naturalcrit/styles/tooltip.less';
@import 'font-awesome/css/font-awesome.css';
html,body, #reactContainer, .naturalCrit{
min-height : 100%;
}
@sidebarWidth : 250px;
body{
background-color : #eee;
font-family : 'Open Sans', sans-serif;
color : #4b5055;
font-weight : 100;
text-rendering : optimizeLegibility;
margin : 0;
padding : 0;
height : 100%;
}
.admin{
header{
background-color : @red;
font-size: 2em;
padding : 20px 0px;
color : white;
margin-bottom: 30px;
i{
margin-right: 30px;
}
}
}
@import 'naturalcrit/styles/reset.less';
@import 'naturalcrit/styles/elements.less';
@import 'naturalcrit/styles/animations.less';
@import 'naturalcrit/styles/colors.less';
@import 'naturalcrit/styles/tooltip.less';
@import 'font-awesome/css/font-awesome.css';
html,body, #reactContainer, .naturalCrit{
min-height : 100%;
}
@sidebarWidth : 250px;
body{
background-color : #eee;
font-family : 'Open Sans', sans-serif;
color : #4b5055;
font-weight : 100;
text-rendering : optimizeLegibility;
margin : 0;
padding : 0;
height : 100%;
}
.admin{
header{
background-color : @red;
font-size: 2em;
padding : 20px 0px;
color : white;
margin-bottom: 30px;
i{
margin-right: 30px;
}
}
hr{
margin : 30px 0px;
}
}

View File

@@ -0,0 +1,75 @@
require('./brewCleanup.less');
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const BrewCleanup = createClass({
displayName : 'BrewCleanup',
getDefaultProps(){
return {};
},
getInitialState() {
return {
count : 0,
pending : false,
primed : false,
err : null
};
},
prime(){
this.setState({ pending: true });
request.get('/admin/cleanup')
.then((res)=>this.setState({ count: res.body.count, primed: true }))
.catch((err)=>this.setState({ error: err }))
.finally(()=>this.setState({ pending: false }));
},
cleanup(){
this.setState({ pending: true });
request.post('/admin/cleanup')
.then((res)=>this.setState({ count: res.body.count }))
.catch((err)=>this.setState({ error: err }))
.finally(()=>this.setState({ pending: false, primed: false }));
},
renderPrimed(){
if(!this.state.primed) return;
if(!this.state.count){
return <div className='removeBox'>No Matching Brews found.</div>;
}
return <div className='removeBox'>
<button onClick={this.cleanup} className='remove'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
: <span><i className='fa fa-times' /> Remove</span>
}
</button>
<span>Found {this.state.count} Brews that could be removed. </span>
</div>;
},
render(){
return <div className='BrewCleanup'>
<h2> Brew Cleanup </h2>
<p>Removes very short brews to tidy up the database</p>
<button onClick={this.prime} className='query'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
: 'Query Brews'
}
</button>
{this.renderPrimed()}
{this.state.error
&& <div className='error'>{this.state.error.toString()}</div>
}
</div>;
}
});
module.exports = BrewCleanup;

View File

@@ -0,0 +1,10 @@
.BrewCleanup{
.removeBox{
margin-top: 20px;
button{
background-color: @red;
margin-right: 10px;
}
}
}

View File

@@ -0,0 +1,92 @@
require('./brewCompress.less');
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const BrewCompress = createClass({
displayName : 'BrewCompress',
getDefaultProps(){
return {};
},
getInitialState() {
return {
count : 0,
batchRange : 0,
pending : false,
primed : false,
err : null,
ids : null
};
},
prime(){
this.setState({ pending: true });
request.get('/admin/finduncompressed')
.then((res)=>this.setState({ count: res.body.count, primed: true, ids: res.body.ids }))
.catch((err)=>this.setState({ error: err }))
.finally(()=>this.setState({ pending: false }));
},
cleanup(){
const brews = this.state.ids;
const compressBatches = ()=>{
if(brews.length == 0){
this.setState({ pending: false, primed: false });
return;
}
const batch = brews.splice(0, 1000); // Process brews in batches of 1000
this.setState({ batchRange: this.state.count - brews.length });
batch.forEach((id, idx)=>{
request.put(`/admin/compress/${id}`)
.catch((err)=>this.setState({ error: err }));
});
setTimeout(compressBatches, 10000); //Wait 10 seconds between batches
};
this.setState({ pending: true });
compressBatches();
},
renderPrimed(){
if(!this.state.primed) return;
if(!this.state.count){
return <div className='removeBox'>No Matching Brews found.</div>;
}
return <div className='removeBox'>
<button onClick={this.cleanup} className='remove'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
: <span><i className='fa fa-compress' /> compress </span>
}
</button>
{this.state.pending
? <span>Compressing {this.state.batchRange} brews. </span>
: <span>Found {this.state.count} Brews that could be compressed. </span>
}
</div>;
},
render(){
return <div className='BrewCompress'>
<h2> Brew Compression </h2>
<p>Compresses the text in brews to binary</p>
<button onClick={this.prime} className='query'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
: 'Query Brews'
}
</button>
{this.renderPrimed()}
{this.state.error
&& <div className='error'>{this.state.error.toString()}</div>
}
</div>;
}
});
module.exports = BrewCompress;

View File

@@ -0,0 +1,10 @@
.BrewCompress{
.removeBox{
margin-top: 20px;
button{
background-color: @red;
margin-right: 10px;
}
}
}

View File

@@ -0,0 +1,82 @@
require('./brewLookup.less');
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const Moment = require('moment');
const BrewLookup = createClass({
getDefaultProps() {
return {};
},
getInitialState() {
return {
query : '',
foundBrew : null,
searching : false,
error : null
};
},
handleChange(e){
this.setState({ query: e.target.value });
},
lookup(){
this.setState({ searching: true, error: null });
request.get(`/admin/lookup/${this.state.query}`)
.then((res)=>this.setState({ foundBrew: res.body }))
.catch((err)=>this.setState({ error: err }))
.finally(()=>this.setState({ searching: false }));
},
renderFoundBrew(){
const brew = this.state.foundBrew;
return <div className='foundBrew'>
<dl>
<dt>Title</dt>
<dd>{brew.title}</dd>
<dt>Authors</dt>
<dd>{brew.authors.join(', ')}</dd>
<dt>Edit Link</dt>
<dd><a href={`/edit/${brew.editId}`} target='_blank' rel='noopener noreferrer'>/edit/{brew.editId}</a></dd>
<dt>Share Link</dt>
<dd><a href={`/share/${brew.shareId}`} target='_blank' rel='noopener noreferrer'>/share/{brew.shareId}</a></dd>
<dt>Last Updated</dt>
<dd>{Moment(brew.updatedAt).fromNow()}</dd>
<dt>Num of Views</dt>
<dd>{brew.views}</dd>
</dl>
</div>;
},
render(){
return <div className='brewLookup'>
<h2>Brew Lookup</h2>
<input type='text' value={this.state.query} onChange={this.handleChange} placeholder='edit or share id' />
<button onClick={this.lookup}>
<i className={cx('fa', {
'fa-search' : !this.state.searching,
'fa-spin fa-spinner' : this.state.searching,
})} />
</button>
{this.state.error
&& <div className='error'>{this.state.error.toString()}</div>
}
{this.state.foundBrew
? this.renderFoundBrew()
: <div className='noBrew'>No brew found.</div>
}
</div>;
}
});
module.exports = BrewLookup;

View File

@@ -0,0 +1,30 @@
.brewLookup{
input{
height : 33px;
margin-bottom : 20px;
padding : 0px 10px;
font-family : monospace;
}
button{
vertical-align : middle;
height : 37px;
}
dl{
@maxItemWidth : 132px;
dt{
float : left;
clear : left;
width : @maxItemWidth;
text-align : right;
&::after {
content: " : ";
}
}
dd{
height : 1em;
margin-left : @maxItemWidth + 6px;
padding : 0 0 0.5em 0;
}
}
}

View File

@@ -1,153 +0,0 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var request = require('superagent');
var Moment = require('moment');
var HomebrewAdmin = React.createClass({
getDefaultProps: function() {
return {
admin_key : ''
};
},
getInitialState: function() {
return {
page: 0,
count : 20,
brewCache : {},
total : 0,
processingOldBrews : false
};
},
fetchBrews : function(page){
request.get('/homebrew/api/search')
.query({
admin_key : this.props.admin_key,
count : this.state.count,
page : page
})
.end((err, res)=>{
this.state.brewCache[page] = res.body.brews;
this.setState({
brewCache : this.state.brewCache,
total : res.body.total,
count : res.body.count
})
})
},
componentDidMount: function() {
this.fetchBrews(this.state.page);
},
changePageTo : function(page){
if(!this.state.brewCache[page]){
this.fetchBrews(page);
}
this.setState({
page : page
})
},
clearInvalidBrews : function(){
request.get('/homebrew/api/invalid')
.query({admin_key : this.props.admin_key})
.end((err, res)=>{
if(!confirm("This will remove " + res.body.count + " brews. Are you sure?")) return;
request.get('/homebrew/api/invalid')
.query({admin_key : this.props.admin_key, do_it : true})
.end((err, res)=>{
alert("Done!")
});
});
},
deleteBrew : function(brewId){
if(!confirm("Are you sure you want to delete '" + brewId + "'?")) return;
request.get('/homebrew/api/remove/' + brewId)
.query({admin_key : this.props.admin_key})
.end(function(err, res){
window.location.reload();
})
},
handlePageChange : function(dir){
this.changePageTo(this.state.page + dir);
},
renderPagnination : function(){
var outOf;
if(this.state.total){
outOf = this.state.page + ' / ' + Math.round(this.state.total/this.state.count);
}
return <div className='pagnination'>
<i className='fa fa-chevron-left' onClick={this.handlePageChange.bind(this, -1)}/>
{outOf}
<i className='fa fa-chevron-right' onClick={this.handlePageChange.bind(this, 1)}/>
</div>
},
renderBrews : function(){
var brews = this.state.brewCache[this.state.page] || _.times(this.state.count);
return _.map(brews, (brew)=>{
return <tr className={cx('brewRow', {'isEmpty' : brew.text == "false"})} key={brew.sharedId}>
<td><a href={'/homebrew/edit/' + brew.editId} target='_blank'>{brew.editId}</a></td>
<td><a href={'/homebrew/share/' + brew.shareId} target='_blank'>{brew.shareId}</a></td>
<td>{Moment(brew.createdAt).fromNow()}</td>
<td>{Moment(brew.updatedAt).fromNow()}</td>
<td>{Moment(brew.lastViewed).fromNow()}</td>
<td>{brew.views}</td>
<td>
<div className='deleteButton' onClick={this.deleteBrew.bind(this, brew.editId)}>
<i className='fa fa-trash' />
</div>
</td>
</tr>
});
},
renderBrewTable : function(){
return <div className='brewTable'>
<table>
<thead>
<tr>
<th>Edit Id</th>
<th>Share Id</th>
<th>Created At</th>
<th>Last Updated</th>
<th>Last Viewed</th>
<th>Views</th>
</tr>
</thead>
<tbody>
{this.renderBrews()}
</tbody>
</table>
</div>
},
render : function(){
var self = this;
return <div className='homebrewAdmin'>
<h2>
Homebrews - {this.state.total}
</h2>
{this.renderPagnination()}
{this.renderBrewTable()}
<button className='clearOldButton' onClick={this.clearInvalidBrews}>
Clear Old
</button>
</div>
}
});
module.exports = HomebrewAdmin;

View File

@@ -1,53 +0,0 @@
.homebrewAdmin{
margin-bottom: 80px;
.brewTable{
table{
th{
padding : 10px;
font-weight : 800;
}
tr:nth-child(even){
background-color : fade(@green, 10%);
}
tr.isEmpty{
background-color : fade(@red, 30%);
}
td{
min-width : 100px;
padding : 10px;
text-align : center;
&.preview{
position : relative;
&:hover{
.content{
display : block;
}
}
.content{
position : absolute;
display : none;
top : 100%;
left : 0px;
z-index : 1000;
max-height : 500px;
width : 300px;
padding : 30px;
background-color : white;
font-family : monospace;
text-align : left;
pointer-events : none;
}
}
}
}
}
.deleteButton{
cursor: pointer;
}
button.clearOldButton{
float : right;
}
}

View File

@@ -0,0 +1,46 @@
require('./stats.less');
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const Stats = createClass({
displayName : 'Stats',
getDefaultProps(){
return {};
},
getInitialState(){
return {
stats : {
totalBrews : 0
},
fetching : false
};
},
componentDidMount(){
this.fetchStats();
},
fetchStats(){
this.setState({ fetching: true });
request.get('/admin/stats')
.then((res)=>this.setState({ stats: res.body }))
.finally(()=>this.setState({ fetching: false }));
},
render(){
return <div className='Stats'>
<h2> Stats </h2>
<dl>
<dt>Total Brew Count</dt>
<dd>{this.state.stats.totalBrews}</dd>
</dl>
{this.state.fetching
&& <div className='pending'><i className='fa fa-spin fa-spinner' /></div>
}
</div>;
}
});
module.exports = Stats;

View File

@@ -0,0 +1,28 @@
.Stats{
position : relative;
.pending{
position : absolute;
top : 0px;
left : 0px;
height : 100%;
width : 100%;
background-color : rgba(238,238,238, 0.5);
}
dl{
@maxItemWidth : 132px;
dt{
float : left;
clear : left;
width : @maxItemWidth;
text-align : right;
&::after {
content: " : ";
}
}
dd{
margin : 0 0 0 @maxItemWidth + 10px;
padding : 0 0 0.5em 0;
}
}
}

View File

@@ -1,45 +1,85 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
require('./brewRenderer.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
var Markdown = require('marked');
const Markdown = require('naturalcrit/markdown.js');
const ErrorBar = require('./errorBar/errorBar.jsx');
var PAGE_HEIGHT = 1056 + 30;
//TODO: move to the brew renderer
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
const NotificationPopup = require('./notificationPopup/notificationPopup.jsx');
const Frame = require('react-frame-component').default;
var BrewRenderer = React.createClass({
getDefaultProps: function() {
const PAGE_HEIGHT = 1056;
const PPR_THRESHOLD = 50;
const BrewRenderer = createClass({
getDefaultProps : function() {
return {
text : ''
text : '',
errors : []
};
},
getInitialState: function() {
getInitialState : function() {
const pages = this.props.text.split('\\page');
return {
viewablePageNumber: 0,
height : 0
viewablePageNumber : 0,
height : 0,
isMounted : false,
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD,
visibility : 'hidden',
initialContent : `<!DOCTYPE html><html><head>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link href='/homebrew/bundle.css' rel='stylesheet' />
<base target=_blank>
</head><body style='overflow: hidden'><div></div></body></html>`
};
},
totalPages : 0,
height : 0,
height : 0,
lastRender : <div></div>,
componentDidMount: function() {
componentWillUnmount : function() {
window.removeEventListener('resize', this.updateSize);
},
componentWillReceiveProps : function(nextProps) {
const pages = nextProps.text.split('\\page');
this.setState({
height : this.refs.main.parentNode.clientHeight
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD
});
},
updateSize : function() {
this.setState({
height : this.refs.main.parentNode.clientHeight,
});
},
handleScroll : function(e){
this.setState({
viewablePageNumber : Math.floor(e.target.scrollTop / PAGE_HEIGHT)
});
},
//Implement later
scrollToPage : function(pageNumber){
const target = e.target;
this.setState((prevState)=>({
viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * prevState.pages.length)
}));
},
shouldRender : function(pageText, index){
var viewIndex = this.state.viewablePageNumber;
if(!this.state.isMounted) return false;
const viewIndex = this.state.viewablePageNumber;
if(index == viewIndex - 3) return true;
if(index == viewIndex - 2) return true;
if(index == viewIndex - 1) return true;
if(index == viewIndex) return true;
if(index == viewIndex + 1) return true;
if(index == viewIndex + 2) return true;
if(index == viewIndex + 3) return true;
//Check for style tages
if(pageText.indexOf('<style>') !== -1) return true;
@@ -48,45 +88,98 @@ var BrewRenderer = React.createClass({
},
renderPageInfo : function(){
return <div className='pageInfo'>
{this.state.viewablePageNumber + 1} / {this.totalPages}
</div>
return <div className='pageInfo' ref='main'>
{this.state.viewablePageNumber + 1} / {this.state.pages.length}
</div>;
},
renderDummyPage : function(key){
return <div className='phb' key={key}>
renderPPRmsg : function(){
if(!this.state.usePPR) return;
return <div className='ppr_msg'>
Partial Page Renderer enabled, because your brew is so large. May effect rendering.
</div>;
},
renderDummyPage : function(index){
return <div className='phb' id={`p${index + 1}`} key={index}>
<i className='fa fa-spinner fa-spin' />
</div>
</div>;
},
renderPage : function(pageText, index){
return <div className='phb' dangerouslySetInnerHTML={{__html:Markdown(pageText)}} key={index} />
return <div className='phb' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: Markdown.render(pageText) }} key={index} />;
},
renderPages : function(){
var pages = this.props.text.split('\\page');
this.totalPages = pages.length;
return _.map(pages, (page, index)=>{
if(this.shouldRender(page, index)){
if(this.state.usePPR){
return _.map(this.state.pages, (page, index)=>{
if(this.shouldRender(page, index) && typeof window !== 'undefined'){
return this.renderPage(page, index);
} else {
return this.renderDummyPage(index);
}
});
}
if(this.props.errors && this.props.errors.length) return this.lastRender;
this.lastRender = _.map(this.state.pages, (page, index)=>{
if(typeof window !== 'undefined') {
return this.renderPage(page, index);
}else{
} else {
return this.renderDummyPage(index);
}
});
return this.lastRender;
},
frameDidMount : function(){ //This triggers when iFrame finishes internal "componentDidMount"
setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame
this.updateSize();
window.addEventListener('resize', this.updateSize);
this.renderPages(); //Make sure page is renderable before showing
this.setState({
isMounted : true,
visibility : 'visible'
});
}, 100);
},
render : function(){
return <div className='brewRenderer'
onScroll={this.handleScroll}
ref='main'
style={{height : this.state.height}}>
//render in iFrame so broken code doesn't crash the site.
//Also render dummy page while iframe is mounting.
<div className='pages'>
{this.renderPages()}
</div>
{this.renderPageInfo()}
</div>
return (
<React.Fragment>
{!this.state.isMounted
? <div className='brewRenderer' onScroll={this.handleScroll}>
<div className='pages' ref='pages'>
{this.renderDummyPage(1)}
</div>
</div>
: null}
<Frame initialContent={this.state.initialContent} style={{ width: '100%', height: '100%', visibility: this.state.visibility }} contentDidMount={this.frameDidMount}>
<div className='brewRenderer'
onScroll={this.handleScroll}
style={{ height: this.state.height }}>
<ErrorBar errors={this.props.errors} />
<div className='popups'>
<RenderWarnings />
<NotificationPopup />
</div>
<div className='pages' ref='pages'>
{this.state.isMounted
? this.renderPages()
: null}
</div>
</div>
</Frame>
{this.renderPageInfo()}
{this.renderPPRmsg()}
</React.Fragment>
);
}
});

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,72 @@
require('./notificationPopup.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames'); //Unused variable
const DISMISS_KEY = 'dismiss_notification7-10-20';
const NotificationPopup = createClass({
getInitialState : function() {
return {
notifications : {}
};
},
componentDidMount : function() {
this.checkNotifications();
window.addEventListener('resize', this.checkNotifications);
},
componentWillUnmount : function() {
window.removeEventListener('resize', this.checkNotifications);
},
notifications : {
psa : function(){
return <li key='psa'>
<em>Google Drive Integration!</em> <br />
We have added Google Drive integration to the Homebrewery! <a target='_blank' href='http://naturalcrit.com/login'>Sign in</a> with
your Google account to link it with your Homebrewery profile. A new button in the Edit page will let you transfer your file to your personal
Google Drive storage, and Google will keep a backup of each version! No more lost work surprises!
<br /><br />
However, we are aware that there may be uncaught bugs. We encourage you to copy your brew into a text document before transferring to Google
Drive just in case any issues arise as this update is rolled out.
<br /><br />
<b>Note:</b> Transferring an existing brew to Google Drive will change the edit and share links of your document. If you have shared your
document online, remember to update the links there as well.
</li>;
},
faq : function(){
return <li key='faq'>
<em>Protect your work! </em> <br />
If you opt not to use your Google Drive, keep in mind that we do not save a history of your projects. Please make frequent backups of your brews! &nbsp;
<a target='_blank' href='https://www.reddit.com/r/homebrewery/comments/adh6lh/faqs_psas_announcements/'>
See the FAQ
</a> to learn how to avoid losing your work!
</li>;
},
},
checkNotifications : function(){
const hideDismiss = localStorage.getItem(DISMISS_KEY);
if(hideDismiss) return this.setState({ notifications: {} });
this.setState({
notifications : _.mapValues(this.notifications, (fn)=>{ return fn(); }) //Convert notification functions into their return text value
});
},
dismiss : function(){
localStorage.setItem(DISMISS_KEY, true);
this.checkNotifications();
},
render : function(){
if(_.isEmpty(this.state.notifications)) return null;
return <div className='notificationPopup'>
<i className='fa fa-times dismiss' onClick={this.dismiss}/>
<i className='fa fa-info-circle info' />
<h3>Notice</h3>
<small>This website is always improving and we are still adding new features and squashing bugs. Keep the following in mind:</small>
<ul>{_.values(this.state.notifications)}</ul>
</div>;
}
});
module.exports = NotificationPopup;

View File

@@ -0,0 +1,62 @@
.popups{
position : fixed;
top : @navbarHeight;
right : 15px;
z-index : 10001;
width : 350px;
}
.notificationPopup{
position : relative;
float : right;
display : inline-block;
width : 350px;
padding : 15px;
padding-bottom : 10px;
padding-left : 55px;
background-color : @blue;
color : white;
a{
color : @steel;
font-weight : 800;
}
i.info{
position : absolute;
top : 12px;
left : 12px;
opacity : 0.8;
font-size : 2.5em;
}
i.dismiss{
position : absolute;
top : 10px;
right : 10px;
cursor : pointer;
opacity : 0.6;
&:hover{
opacity : 1;
}
}
small{
opacity : 0.7;
font-size : 0.6em;
}
h3{
font-size : 1.1em;
font-weight : 800;
}
ul{
margin-top : 15px;
font-size : 0.8em;
list-style-position : outside;
list-style-type : disc;
li{
font-size : 0.8em;
line-height : 1.4em;
margin-top : 1.4em;
em{
font-weight : 800;
}
}
}
}

View File

@@ -1,35 +1,53 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
require('./editor.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
var CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
var Snippets = require('./snippets/snippets.js');
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
const SnippetBar = require('./snippetbar/snippetbar.jsx');
const MetadataEditor = require('./metadataEditor/metadataEditor.jsx');
var splice = function(str, index, inject){
const splice = function(str, index, inject){
return str.slice(0, index) + inject + str.slice(index);
};
var execute = function(val){
if(_.isFunction(val)) return val();
return val;
}
const SNIPPETBAR_HEIGHT = 25;
var Editor = React.createClass({
getDefaultProps: function() {
const Editor = createClass({
getDefaultProps : function() {
return {
value : "",
onChange : function(){}
value : '',
onChange : ()=>{},
metadata : {},
onMetadataChange : ()=>{},
showMetaButton : true
};
},
getInitialState : function() {
return {
showMetadataEditor : false
};
},
cursorPosition : {
line : 0,
ch : 0
ch : 0
},
componentDidMount: function() {
var paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= this.refs.snippetBar.clientHeight + 1;
componentDidMount : function() {
this.updateEditorSize();
this.highlightPageLines();
window.addEventListener('resize', this.updateEditorSize);
},
componentWillUnmount : function() {
window.removeEventListener('resize', this.updateEditorSize);
},
updateEditorSize : function() {
let paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= SNIPPETBAR_HEIGHT + 1;
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
},
@@ -39,38 +57,71 @@ var Editor = React.createClass({
handleCursorActivty : function(curpos){
this.cursorPosition = curpos;
},
handleSnippetClick : function(injectText){
var lines = this.props.value.split('\n');
handleInject : function(injectText){
const lines = this.props.value.split('\n');
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
this.handleTextChange(lines.join('\n'));
this.refs.codeEditor.setCursorPosition(this.cursorPosition.line, this.cursorPosition.ch + injectText.length);
},
handgleToggle : function(){
this.setState({
showMetadataEditor : !this.state.showMetadataEditor
});
},
getCurrentPage : function(){
const lines = this.props.value.split('\n').slice(0, this.cursorPosition.line + 1);
return _.reduce(lines, (r, line)=>{
if(line.indexOf('\\page') !== -1) r++;
return r;
}, 1);
},
highlightPageLines : function(){
if(!this.refs.codeEditor) return;
const codeMirror = this.refs.codeEditor.codeMirror;
const lineNumbers = _.reduce(this.props.value.split('\n'), (r, line, lineNumber)=>{
if(line.indexOf('\\page') !== -1){
codeMirror.addLineClass(lineNumber, 'background', 'pageLine');
r.push(lineNumber);
}
return r;
}, []);
return lineNumbers;
},
brewJump : function(){
const currentPage = this.getCurrentPage();
window.location.hash = `p${currentPage}`;
},
//Called when there are changes to the editor's dimensions
update : function(){
this.refs.codeEditor.updateSize();
},
renderSnippetGroups : function(){
return _.map(Snippets, (snippetGroup)=>{
return <SnippetGroup
groupName={snippetGroup.groupName}
icon={snippetGroup.icon}
snippets={snippetGroup.snippets}
key={snippetGroup.groupName}
onSnippetClick={this.handleSnippetClick}
/>
})
renderMetadataEditor : function(){
if(!this.state.showMetadataEditor) return;
return <MetadataEditor
metadata={this.props.metadata}
onChange={this.props.onMetadataChange}
/>;
},
render : function(){
return(
this.highlightPageLines();
return (
<div className='editor' ref='main'>
<div className='snippetBar' ref='snippetBar'>
{this.renderSnippetGroups()}
</div>
<SnippetBar
brew={this.props.value}
onInject={this.handleInject}
onToggle={this.handgleToggle}
showmeta={this.state.showMetadataEditor}
showMetaButton={this.props.showMetaButton} />
{this.renderMetadataEditor()}
<CodeEditor
ref='codeEditor'
wrap={true}
@@ -78,52 +129,15 @@ var Editor = React.createClass({
value={this.props.value}
onChange={this.handleTextChange}
onCursorActivity={this.handleCursorActivty} />
{/*
<div className='brewJump' onClick={this.brewJump}>
<i className='fa fa-arrow-right' />
</div>
*/}
</div>
);
}
});
module.exports = Editor;
var SnippetGroup = React.createClass({
getDefaultProps: function() {
return {
groupName : '',
icon : 'fa-rocket',
snippets : [],
onSnippetClick : function(){},
};
},
handleSnippetClick : function(snippet){
this.props.onSnippetClick(execute(snippet.gen));
},
renderSnippets : function(){
return _.map(this.props.snippets, (snippet)=>{
return <div className='snippet' key={snippet.name} onClick={this.handleSnippetClick.bind(this, snippet)}>
<i className={'fa fa-fw ' + snippet.icon} />
{snippet.name}
</div>
})
},
render : function(){
return <div className='snippetGroup'>
<div className='text'>
<i className={'fa fa-fw ' + this.props.icon} />
<span className='groupName'>{this.props.groupName}</span>
</div>
<div className='dropdown'>
{this.renderSnippets()}
</div>
</div>
},
});

View File

@@ -1,56 +1,29 @@
.editor{
position : relative;
width : 100%;
.snippetBar{
display : flex;
padding : 5px;
background-color : #ddd;
align-items : center;
.snippetGroup{
.animate(background-color);
margin : 0px 8px;
padding : 3px;
font-size : 13px;
border-radius : 5px;
&:hover, &.selected{
background-color : #999;
}
.text{
line-height : 20px;
.groupName{
margin-left : 6px;
font-size : 10px;
}
}
&:hover{
.dropdown{
visibility : visible;
}
}
.dropdown{
position : absolute;
visibility : hidden;
z-index : 1000;
padding : 5px;
background-color : #ddd;
.snippet{
.animate(background-color);
padding : 10px;
cursor : pointer;
font-size : 10px;
i{
margin-right: 8px;
font-size : 13px;
}
&:hover{
background-color : #999;
}
}
}
}
}
.codeEditor{
height : 100%;
}
.editor{
position : relative;
width : 100%;
.codeEditor{
height : 100%;
.pageLine{
background-color : fade(#333, 15%);
border-bottom : #333 solid 1px;
}
}
.brewJump{
position: absolute;
background-color: @teal;
cursor: pointer;
width : 30px;
height : 30px;
display : flex;
align-items : center;
bottom : 20px;
right : 20px;
z-index: 1000000;
justify-content:center;
.tooltipLeft("Jump to brew page");
}
}

View File

@@ -0,0 +1,182 @@
require('./metadataEditor.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const request = require('superagent');
const SYSTEMS = ['5e', '4e', '3.5e', 'Pathfinder'];
const MetadataEditor = createClass({
getDefaultProps : function() {
return {
metadata : {
editId : null,
title : '',
description : '',
tags : '',
published : false,
authors : [],
systems : []
},
onChange : ()=>{}
};
},
handleFieldChange : function(name, e){
this.props.onChange(_.merge({}, this.props.metadata, {
[name] : e.target.value
}));
},
handleSystem : function(system, e){
if(e.target.checked){
this.props.metadata.systems.push(system);
} else {
this.props.metadata.systems = _.without(this.props.metadata.systems, system);
}
this.props.onChange(this.props.metadata);
},
handlePublish : function(val){
this.props.onChange(_.merge({}, this.props.metadata, {
published : val
}));
},
handleDelete : function(){
if(this.props.metadata.authors.length <= 1){
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return;
} else {
if(!confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the document.')) return;
if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return;
}
request.delete(`/api/${this.props.metadata.editId}`)
.send()
.end(function(err, res){
window.location.href = '/';
});
},
getRedditLink : function(){
const meta = this.props.metadata;
const title = `${meta.title} [${meta.systems.join(' ')}]`;
const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out.
**[Homebrewery Link](http://homebrewery.naturalcrit.com/share/${meta.shareId})**`;
return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`;
},
renderSystems : function(){
return _.map(SYSTEMS, (val)=>{
return <label key={val}>
<input
type='checkbox'
checked={_.includes(this.props.metadata.systems, val)}
onChange={(e)=>this.handleSystem(val, e)} />
{val}
</label>;
});
},
renderPublish : function(){
if(this.props.metadata.published){
return <button className='unpublish' onClick={()=>this.handlePublish(false)}>
<i className='fa fa-ban' /> unpublish
</button>;
} else {
return <button className='publish' onClick={()=>this.handlePublish(true)}>
<i className='fa fa-globe' /> publish
</button>;
}
},
renderDelete : function(){
if(!this.props.metadata.editId) return;
return <div className='field delete'>
<label>delete</label>
<div className='value'>
<button className='publish' onClick={this.handleDelete}>
<i className='fa fa-trash' /> delete brew
</button>
</div>
</div>;
},
renderAuthors : function(){
let text = 'None.';
if(this.props.metadata.authors.length){
text = this.props.metadata.authors.join(', ');
}
return <div className='field authors'>
<label>authors</label>
<div className='value'>
{text}
</div>
</div>;
},
renderShareToReddit : function(){
if(!this.props.metadata.shareId) return;
return <div className='field reddit'>
<label>reddit</label>
<div className='value'>
<a href={this.getRedditLink()} target='_blank' rel='noopener noreferrer'>
<button className='publish'>
<i className='fa fa-reddit-alien' /> share to reddit
</button>
</a>
</div>
</div>;
},
render : function(){
return <div className='metadataEditor'>
<div className='field title'>
<label>title</label>
<input type='text' className='value'
value={this.props.metadata.title}
onChange={(e)=>this.handleFieldChange('title', e)} />
</div>
<div className='field description'>
<label>description</label>
<textarea value={this.props.metadata.description} className='value'
onChange={(e)=>this.handleFieldChange('description', e)} />
</div>
{/*}
<div className='field tags'>
<label>tags</label>
<textarea value={this.props.metadata.tags}
onChange={(e)=>this.handleFieldChange('tags', e)} />
</div>
*/}
<div className='field systems'>
<label>systems</label>
<div className='value'>
{this.renderSystems()}
</div>
</div>
{this.renderAuthors()}
<div className='field publish'>
<label>publish</label>
<div className='value'>
{this.renderPublish()}
<small>Published homebrews will be publicly viewable and searchable (eventually...)</small>
</div>
</div>
{this.renderShareToReddit()}
{this.renderDelete()}
</div>;
}
});
module.exports = MetadataEditor;

View File

@@ -0,0 +1,79 @@
.metadataEditor{
position : absolute;
z-index : 10000;
box-sizing : border-box;
width : 100%;
padding : 25px;
background-color : #999;
.field{
display : flex;
width : 100%;
margin-bottom : 10px;
&>label{
display : inline-block;
vertical-align : top;
width : 80px;
font-size : 0.7em;
font-weight : 800;
line-height : 1.8em;
text-transform : uppercase;
flex-grow : 0;
}
&>.value{
flex-grow : 1;
}
}
.description.field textarea.value{
resize : none;
height : 5em;
font-family : 'Open Sans', sans-serif;
font-size : 0.8em;
}
.systems.field .value{
label{
vertical-align : middle;
margin-right : 15px;
cursor : pointer;
font-size : 0.7em;
font-weight : 800;
user-select : none;
}
input{
vertical-align : middle;
cursor : pointer;
}
}
.publish.field .value{
position : relative;
margin-bottom: 15px;
button.publish{
.button(@blueLight);
}
button.unpublish{
.button(@silver);
}
small{
position : absolute;
bottom : -15px;
left : 0px;
font-size : 0.6em;
font-style : italic;
}
}
.delete.field .value{
button{
.button(@red);
}
}
.reddit.field .value{
button{
.button(@purple);
}
}
.authors.field .value{
font-size: 0.8em;
line-height : 1.5em;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,42 @@
const _ = require('lodash');
module.exports = function(classname){
classname = _.sample(['archivist', 'fancyman', 'linguist', 'fletcher',
'notary', 'berserker-typist', 'fishmongerer', 'manicurist', 'haberdasher', 'concierge']);
classname = classname.toLowerCase();
const hitDie = _.sample([4, 6, 8, 10, 12]);
const abilityList = ['Strength', 'Dexerity', 'Constitution', 'Wisdom', 'Charisma', 'Intelligence'];
const skillList = ['Acrobatics ', 'Animal Handling', 'Arcana', 'Athletics', 'Deception', 'History', 'Insight', 'Intimidation', 'Investigation', 'Medicine', 'Nature', 'Perception', 'Performance', 'Persuasion', 'Religion', 'Sleight of Hand', 'Stealth', 'Survival'];
return [
'## Class Features',
`As a ${classname}, you gain the following class features`,
'#### Hit Points',
'___',
`- **Hit Dice:** 1d${hitDie} per ${classname} level`,
`- **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`,
'',
'#### Proficiencies',
'___',
`- **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'}`,
`- **Tools:** ${_.sampleSize(['Artian\'s tools', 'one musical instrument', 'Thieve\'s tools'], _.random(0, 2)).join(', ') || 'None'}`,
'',
'___',
`- **Saving Throws:** ${_.sampleSize(abilityList, 2).join(', ')}`,
`- **Skills:** Choose two from ${_.sampleSize(skillList, _.random(4, 6)).join(', ')}`,
'',
'#### Equipment',
'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)* five javelins or *(b)* any simple melee weapon',
`- ${_.sample(['10 lint fluffs', '1 button', 'a cherished lost sock'])}`,
'\n\n\n'
].join('\n');
};

View File

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

View File

@@ -0,0 +1,124 @@
const _ = require('lodash');
const titles = [
'The Burning Gallows',
'The Ring of Nenlast',
'Below the Blind Tavern',
'Below the Hungering River',
'Before Bahamut\'s Land',
'The Cruel Grave from Within',
'The Strength of Trade Road',
'Through The Raven Queen\'s Worlds',
'Within the Settlement',
'The Crown from Within',
'The Merchant Within the Battlefield',
'Ioun\'s Fading Traveler',
'The Legion Ingredient',
'The Explorer Lure',
'Before the Charming Badlands',
'The Living Dead Above the Fearful Cage',
'Vecna\'s Hidden Sage',
'Bahamut\'s Demonspawn',
'Across Gruumsh\'s Elemental Chaos',
'The Blade of Orcus',
'Beyond Revenge',
'Brain of Insanity',
'Breed Battle!, A New Beginning',
'Evil Lake, A New Beginning',
'Invasion of the Gigantic Cat, Part II',
'Kraken War 2020',
'The Body Whisperers',
'The Diabolical Tales of the Ape-Women',
'The Doctor Immortal',
'The Doctor from Heaven',
'The Graveyard',
'Azure Core',
'Core Battle',
'Core of Heaven: The Guardian of Amazement',
'Deadly Amazement III',
'Dry Chaos IX',
'Gate Thunder',
'Guardian: Skies of the Dark Wizard',
'Lute of Eternity',
'Mercury\'s Planet: Brave Evolution',
'Ruby of Atlantis: The Quake of Peace',
'Sky of Zelda: The Thunder of Force',
'Vyse\'s Skies',
'White Greatness III',
'Yellow Divinity',
'Zidane\'s Ghost'
];
const subtitles = [
'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 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 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 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 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 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 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 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 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 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 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.',
'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 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.',
'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 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.',
'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 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 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.',
'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 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 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 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 crime-infested galaxy, in an era of hopelessness and panic, three champions and a grave robber try to solve the ultimate crime.'
];
module.exports = ()=>{
return `<style>
.phb#p1{ text-align:center; }
.phb#p1:after{ display:none; }
.phb#p2 { counter-reset:phb-page-numbers; }
.phb:nth-child(2n) .pageNumber { left: inherit !important; right: 2px !important; }
.phb:nth-child(2n+1) .pageNumber { right: inherit !important; left: 2px !important; }
.phb:nth-child(2n)::after { transform: scaleX(1); }
.phb:nth-child(2n+1)::after { transform: scaleX(-1); }
.phb:nth-child(2n) .footnote { left: inherit; text-align: right; }
.phb:nth-child(2n+1) .footnote { left: 80px; text-align: left; }
</style>
<div style='margin-top:450px;'></div>
# ${_.sample(titles)}
<div style='margin-top:25px'></div>
<div class='wide'>
##### ${_.sample(subtitles)}
</div>
\\page`;
};

View File

@@ -0,0 +1,43 @@
const _ = require('lodash');
const ClassFeatureGen = require('./classfeature.gen.js');
const ClassTableGen = require('./classtable.gen.js');
module.exports = function(){
const classname = _.sample(['Archivist', 'Fancyman', 'Linguist', 'Fletcher',
'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge']);
const image = _.sample(_.map([
'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://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://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://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',
], function(url){
return `<img src = '${url}' style='max-width:8cm;max-height:25cm' />`;
}));
return `${[
image,
'',
'```',
'```',
'<div style=\'margin-top:240px\'></div>\n\n',
`## ${classname}`,
'Cool intro stuff will go here',
'\\page',
ClassTableGen(classname),
ClassFeatureGen(classname),
].join('\n')}\n\n\n`;
};

View File

@@ -0,0 +1,91 @@
const _ = require('lodash');
const spellNames = [
'Astral Rite of Acne',
'Create Acne',
'Cursed Ramen Erruption',
'Dark Chant of the Dentists',
'Erruption of Immaturity',
'Flaming Disc of Inconvenience',
'Heal Bad Hygene',
'Heavenly Transfiguration of the Cream Devil',
'Hellish Cage of Mucus',
'Irritate Peanut Butter Fairy',
'Luminous Erruption of Tea',
'Mystic Spell of the Poser',
'Sorcerous Enchantment of the Chimneysweep',
'Steak Sauce Ray',
'Talk to Groupie',
'Astonishing Chant of Chocolate',
'Astounding Pasta Puddle',
'Ball of Annoyance',
'Cage of Yarn',
'Control Noodles Elemental',
'Create Nervousness',
'Cure Baldness',
'Cursed Ritual of Bad Hair',
'Dispell Piles in Dentist',
'Eliminate Florists',
'Illusionary Transfiguration of the Babysitter',
'Necromantic Armor of Salad Dressing',
'Occult Transfiguration of Foot Fetish',
'Protection from Mucus Giant',
'Tinsel Blast',
'Alchemical Evocation of the Goths',
'Call Fangirl',
'Divine Spell of Crossdressing',
'Dominate Ramen Giant',
'Eliminate Vindictiveness in Gym Teacher',
'Extra-Planar Spell of Irritation',
'Induce Whining in Babysitter',
'Invoke Complaining',
'Magical Enchantment of Arrogance',
'Occult Globe of Salad Dressing',
'Overwhelming Enchantment of the Chocolate Fairy',
'Sorcerous Dandruff Globe',
'Spiritual Invocation of the Costumers',
'Ultimate Rite of the Confetti Angel',
'Ultimate Ritual of Mouthwash',
];
module.exports = {
spellList : function(){
const levels = ['Cantrips (0 Level)', '2nd Level', '3rd Level', '4th Level', '5th Level', '6th Level', '7th Level', '8th Level', '9th Level'];
const content = _.map(levels, (level)=>{
const spells = _.map(_.sampleSize(spellNames, _.random(5, 15)), (spell)=>{
return `- ${spell}`;
}).join('\n');
return `##### ${level} \n${spells} \n`;
}).join('\n');
return `<div class='spellList'>\n${content}\n</div>`;
},
spell : function(){
const level = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th', '8th', '9th'];
const spellSchools = ['abjuration', 'conjuration', 'divination', 'enchantment', 'evocation', 'illusion', 'necromancy', 'transmutation'];
let components = _.sampleSize(['V', 'S', 'M'], _.random(1, 3)).join(', ');
if(components.indexOf('M') !== -1){
components += ` (${_.sampleSize(['a small doll', 'a crushed button worth at least 1cp', 'discarded gum wrapper'], _.random(1, 3)).join(', ')})`;
}
return [
`#### ${_.sample(spellNames)}`,
`*${_.sample(level)}-level ${_.sample(spellSchools)}*`,
'___',
'- **Casting Time:** 1 action',
`- **Range:** ${_.sample(['Self', 'Touch', '30 feet', '60 feet'])}`,
`- **Components:** ${components}`,
`- **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. ',
'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.',
'\n\n\n'
].join('\n');
}
};

View File

@@ -0,0 +1,200 @@
const _ = require('lodash');
const genList = function(list, max){
return _.sampleSize(list, _.random(0, max)).join(', ') || 'None';
};
const getMonsterName = function(){
return _.sample([
'All-devouring Baseball Imp',
'All-devouring Gumdrop Wraith',
'Chocolate Hydra',
'Devouring Peacock',
'Economy-sized Colossus of the Lemonade Stand',
'Ghost Pigeon',
'Gibbering Duck',
'Sparklemuffin Peacock Spider',
'Gum Elemental',
'Illiterate Construct of the Candy Store',
'Ineffable Chihuahua',
'Irritating Death Hamster',
'Irritating Gold Mouse',
'Juggernaut Snail',
'Juggernaut of the Sock Drawer',
'Koala of the Cosmos',
'Mad Koala of the West',
'Milk Djinni of the Lemonade Stand',
'Mind Ferret',
'Mystic Salt Spider',
'Necrotic Halitosis Angel',
'Pinstriped Famine Sheep',
'Ritalin Leech',
'Shocker Kangaroo',
'Stellar Tennis Juggernaut',
'Wailing Quail of the Sun',
'Angel Pigeon',
'Anime Sphinx',
'Bored Avalanche Sheep of the Wasteland',
'Devouring Nougat Sphinx of the Sock Drawer',
'Djinni of the Footlocker',
'Ectoplasmic Jazz Devil',
'Flatuent Angel',
'Gelatinous Duck of the Dream-Lands',
'Gelatinous Mouse',
'Golem of the Footlocker',
'Lich Wombat',
'Mechanical Sloth of the Past',
'Milkshake Succubus',
'Puffy Bone Peacock of the East',
'Rainbow Manatee',
'Rune Parrot',
'Sand Cow',
'Sinister Vanilla Dragon',
'Snail of the North',
'Spider of the Sewer',
'Stellar Sawdust Leech',
'Storm Anteater of Hell',
'Stupid Spirit of the Brewery',
'Time Kangaroo',
'Tomb Poodle',
]);
};
const getType = function(){
return `${_.sample(['Tiny', 'Small', 'Medium', 'Large', 'Gargantuan', 'Stupidly vast'])} ${_.sample(['beast', 'fiend', 'annoyance', 'guy', 'cutie'])}`;
};
const 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'
]);
};
const getStats = function(){
return `>|${_.times(6, function(){
const num = _.random(1, 20);
const mod = Math.ceil(num/2 - 5);
return `${num} (${mod >= 0 ? `+${mod}` : mod})`;
}).join('|')}|`;
};
const genAbilities = function(){
return _.sample([
'> ***Pack Tactics.*** These guys work together. Like super well, you don\'t even know.',
'> ***Fowl Appearance.*** While the creature remains motionless, it is indistinguishable from a normal chicken.',
'> ***Onion Stench.*** Any creatures within 5 feet of this thing develops an irrational craving for onion rings.',
'> ***Enormous Nose.*** This creature gains advantage on any check involving putting things in its nose.',
'> ***Sassiness.*** When questioned, this creature will talk back instead of answering.',
'> ***Big Jerk.*** Thinks he is just *waaaay* better than you.',
]);
};
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 = {
full : function(){
return `${[
'___',
'___',
`> ## ${getMonsterName()}`,
`>*${getType()}, ${getAlignment()}*`,
'> ___',
`> - **Armor Class** ${_.random(10, 20)}`,
`> - **Hit Points** ${_.random(1, 150)}(1d4 + 5)`,
`> - **Speed** ${_.random(0, 50)}ft.`,
'>___',
'>|STR|DEX|CON|INT|WIS|CHA|',
'>|:---:|:---:|:---:|:---:|:---:|:---:|',
getStats(),
'>___',
`> - **Condition Immunities** ${genList(['groggy', 'swagged', 'weak-kneed', 'buzzed', 'groovy', 'melancholy', 'drunk'], 3)}`,
`> - **Senses** passive Perception ${_.random(3, 20)}`,
`> - **Languages** ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}`,
`> - **Challenge** ${_.random(0, 15)} (${_.random(10, 10000)} XP)`,
'> ___',
_.times(_.random(3, 6), function(){
return genAbilities();
}).join('\n>\n'),
'> ### Actions',
_.times(_.random(4, 6), function(){
return genAction();
}).join('\n>\n'),
].join('\n')}\n\n\n`;
},
half : function(){
return `${[
'___',
`> ## ${getMonsterName()}`,
`>*${getType()}, ${getAlignment()}*`,
'> ___',
`> - **Armor Class** ${_.random(10, 20)}`,
`> - **Hit Points** ${_.random(1, 150)}(1d4 + 5)`,
`> - **Speed** ${_.random(0, 50)}ft.`,
'>___',
'>|STR|DEX|CON|INT|WIS|CHA|',
'>|:---:|:---:|:---:|:---:|:---:|:---:|',
getStats(),
'>___',
`> - **Condition Immunities** ${genList(['groggy', 'swagged', 'weak-kneed', 'buzzed', 'groovy', 'melancholy', 'drunk'], 3)}`,
`> - **Senses** passive Perception ${_.random(3, 20)}`,
`> - **Languages** ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}`,
`> - **Challenge** ${_.random(0, 15)} (${_.random(10, 10000)} XP)`,
'> ___',
_.times(_.random(2, 3), function(){
return genAbilities();
}).join('\n>\n'),
'> ### Actions',
_.times(_.random(1, 2), function(){
return genAction();
}).join('\n>\n'),
].join('\n')}\n\n\n`;
}
};

View File

@@ -0,0 +1,268 @@
/* eslint-disable max-lines */
const MagicGen = require('./magic.gen.js');
const ClassTableGen = require('./classtable.gen.js');
const MonsterBlockGen = require('./monsterblock.gen.js');
const ClassFeatureGen = require('./classfeature.gen.js');
const CoverPageGen = require('./coverpage.gen.js');
const TableOfContentsGen = require('./tableOfContents.gen.js');
module.exports = [
{
groupName : 'Editor',
icon : 'fa-pencil',
snippets : [
{
name : 'Column Break',
icon : 'fa-columns',
gen : '```\n```\n\n'
},
{
name : 'New Page',
icon : 'fa-file-text',
gen : '\\page\n\n'
},
{
name : 'Vertical Spacing',
icon : 'fa-arrows-v',
gen : '<div style=\'margin-top:140px\'></div>\n\n'
},
{
name : 'Wide Block',
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'
},
{
name : 'Image',
icon : 'fa-image',
gen : [
'<img ',
' src=\'https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg\' ',
' style=\'width:325px\' />',
'Credit: Kyounghwan Kim'
].join('\n')
},
{
name : 'Background Image',
icon : 'fa-tree',
gen : [
'<img ',
' src=\'http://i.imgur.com/hMna6G0.png\' ',
' style=\'position:absolute; top:50px; right:30px; width:280px\' />'
].join('\n')
},
{
name : 'Page Number',
icon : 'fa-bookmark',
gen : '<div class=\'pageNumber\'>1</div>\n<div class=\'footnote\'>PART 1 | FANCINESS</div>\n\n'
},
{
name : 'Auto-incrementing Page Number',
icon : 'fa-sort-numeric-asc',
gen : '<div class=\'pageNumber auto\'></div>\n'
},
{
name : 'Link to page',
icon : 'fa-link',
gen : '[Click here](#p3) to go to page 3\n'
},
{
name : 'Table of Contents',
icon : 'fa-book',
gen : TableOfContentsGen
},
]
},
/************************* PHB ********************/
{
groupName : 'PHB',
icon : 'fa-book',
snippets : [
{
name : 'Spell',
icon : 'fa-magic',
gen : MagicGen.spell,
},
{
name : 'Spell List',
icon : 'fa-list',
gen : MagicGen.spellList,
},
{
name : 'Class Feature',
icon : 'fa-trophy',
gen : ClassFeatureGen,
},
{
name : 'Note',
icon : 'fa-sticky-note',
gen : function(){
return [
'> ##### Time to Drop Knowledge',
'> Use notes to point out some interesting information. ',
'> ',
'> **Tables and lists** both work within a note.'
].join('\n');
},
},
{
name : 'Descriptive Text Box',
icon : 'fa-sticky-note-o',
gen : function(){
return [
'<div class=\'descriptive\'>',
'##### Time to Drop Knowledge',
'Use notes to point out some interesting information. ',
'',
'**Tables and lists** both work within a note.',
'</div>'
].join('\n');
},
},
{
name : 'Monster Stat Block',
icon : 'fa-bug',
gen : MonsterBlockGen.half,
},
{
name : 'Wide Monster Stat Block',
icon : 'fa-paw',
gen : MonsterBlockGen.full,
},
{
name : 'Cover Page',
icon : 'fa-file-word-o',
gen : CoverPageGen,
},
]
},
/********************* TABLES *********************/
{
groupName : 'Tables',
icon : 'fa-table',
snippets : [
{
name : 'Class Table',
icon : 'fa-table',
gen : ClassTableGen.full,
},
{
name : 'Half Class Table',
icon : 'fa-list-alt',
gen : ClassTableGen.half,
},
{
name : 'Table',
icon : 'fa-th-list',
gen : function(){
return [
'##### Cookie Tastiness',
'| Tastiness | Cookie Type |',
'|:----:|:-------------|',
'| -5 | Raisin |',
'| 8th | Chocolate Chip |',
'| 11th | 2 or lower |',
'| 14th | 3 or lower |',
'| 17th | 4 or lower |\n\n',
].join('\n');
},
},
{
name : 'Wide Table',
icon : 'fa-list',
gen : function(){
return [
'<div class=\'wide\'>',
'##### Cookie Tastiness',
'| Tastiness | Cookie Type |',
'|:----:|:-------------|',
'| -5 | Raisin |',
'| 8th | Chocolate Chip |',
'| 11th | 2 or lower |',
'| 14th | 3 or lower |',
'| 17th | 4 or lower |',
'</div>\n\n'
].join('\n');
},
},
{
name : 'Split Table',
icon : 'fa-th-large',
gen : function(){
return [
'<div style=\'column-count:2\'>',
'| d10 | Damage Type |',
'|:---:|:------------|',
'| 1 | Acid |',
'| 2 | Cold |',
'| 3 | Fire |',
'| 4 | Force |',
'| 5 | Lightning |',
'',
'```',
'```',
'',
'| d10 | Damage Type |',
'|:---:|:------------|',
'| 6 | Necrotic |',
'| 7 | Poison |',
'| 8 | Psychic |',
'| 9 | Radiant |',
'| 10 | Thunder |',
'</div>\n\n',
].join('\n');
},
}
]
},
/**************** PRINT *************/
{
groupName : 'Print',
icon : 'fa-print',
snippets : [
{
name : 'A4 PageSize',
icon : 'fa-file-o',
gen : ['<style>',
' .phb{',
' width : 210mm;',
' height : 296.8mm;',
' }',
'</style>'
].join('\n')
},
{
name : 'Ink Friendly',
icon : 'fa-tint',
gen : ['<style>',
' .phb{ background : white;}',
' .phb img{ display : none;}',
' .phb hr+blockquote{background : white;}',
'</style>',
''
].join('\n')
},
]
},
];

View File

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

View File

@@ -1,42 +0,0 @@
var _ = require('lodash');
module.exports = function(classname){
classname = classname || _.sample(['archivist', 'fancyman', 'linguist', 'fletcher',
'notary', 'berserker-typist', 'fishmongerer', 'manicurist', 'haberdasher', 'concierge'])
classname = classname.toLowerCase();
var hitDie = _.sample([4, 6, 8, 10, 12]);
var 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"];
return [
"## Class Features",
"As a " + classname + ", you gain the following class features",
"#### Hit Points",
"___",
"- **Hit Dice:** 1d" + hitDie + " per " + classname + " level",
"- **Hit Points at 1st Level:** " + hitDie + " + your Constituion modifier",
"- **Hit Points at Higher Levels:** 1d" + hitDie + " (or " + (hitDie/2 + 1) + ") + your Constituion modifier per " + classname + " level after 1st",
"",
"#### Proficiencies",
"___",
"- **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"),
"- **Tools:** " + (_.sampleSize(["Artian's tools", "one musical instrument", "Thieve's tools"], _.random(0,2)).join(', ') || "None"),
"",
"___",
"- **Saving Throws:** " + (_.sampleSize(abilityList, 2).join(', ')),
"- **Skills:** Choose two from " + (_.sampleSize(skillList, _.random(4, 6)).join(', ')),
"",
"#### Equipment",
"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)* five javelins or *(b)* any simple melee weapon",
"- " + (_.sample(["10 lint fluffs", "1 button", "a cherished lost sock"])),
"\n\n\n"
].join('\n');
}

View File

@@ -1,105 +0,0 @@
var _ = require('lodash');
var features = [
"Astrological Botany",
"Astrological Chemistry",
"Biochemical Sorcery",
"Civil Alchemy",
"Consecrated Biochemistry",
"Demonic Anthropology",
"Divinatory Mineralogy",
"Genetic Banishing",
"Hermetic Geography",
"Immunological Incantations",
"Nuclear Illusionism",
"Ritual Astronomy",
"Seismological Divination",
"Spiritual Biochemistry",
"Statistical Occultism",
"Police Necromancer",
"Sixgun Poisoner",
"Pharmaceutical Gunslinger",
"Infernal Banker",
"Spell Analyst",
"Gunslinger Corruptor",
"Torque Interfacer",
"Exo Interfacer",
"Gunpowder Torturer",
"Orbital Gravedigger",
"Phased Linguist",
"Mathematical Pharmacist",
"Plasma Outlaw",
"Malefic Chemist",
"Police Cultist"
];
var classnames = ['Archivist', 'Fancyman', 'Linguist', 'Fletcher',
'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"]
module.exports = {
full : function(classname){
classname = classname || _.sample(classnames)
var maxes = [4,3,3,3,3,2,2,1,1]
var drawSlots = function(Slots){
var slots = Number(Slots);
return _.times(9, function(i){
var max = maxes[i];
if(slots < 1) return "—";
var res = _.min([max, slots]);
slots -= res;
return res;
}).join(' | ')
}
var cantrips = 3;
var spells = 1;
var slots = 2;
return "##### The " + classname + "\n" +
"___\n" +
"| Level | Proficiency Bonus | Features | Cantrips Known | Spells Known | 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th |\n"+
"|:---:|:---:|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n" +
_.map(levels, function(levelName, level){
var res = [
levelName,
"+" + Math.ceil(level/5 + 1),
_.sampleSize(features, _.sample([0,1,1])).join(', ') || "Ability Score Improvement",
cantrips,
spells,
drawSlots(slots)
].join(' | ');
cantrips += _.random(0,1);
spells += _.random(0,1);
slots += _.random(0,2);
return "| " + res + " |";
}).join('\n') +'\n\n';
},
half : function(classname){
classname = classname || _.sample(classnames)
var featureScore = 1
return "##### The " + classname + "\n" +
"___\n" + "___\n" +
"| Level | Proficiency Bonus | Features | " + _.sample(features) + "|\n" +
"|:---:|:---:|:---|:---:|\n" +
_.map(levels, function(levelName, level){
var res = [
levelName,
"+" + Math.ceil(level/5 + 1),
_.sampleSize(features, _.sample([0,1,1])).join(', ') || "Ability Score Improvement",
"+" + featureScore
].join(' | ');
featureScore += _.random(0,1);
return "| " + res + " |";
}).join('\n') +'\n\n';
}
};

View File

@@ -1,43 +0,0 @@
var _ = require('lodash');
var ClassFeatureGen = require('./classfeature.gen.js');
var ClassTableGen = require('./classtable.gen.js');
module.exports = function(){
var classname = _.sample(['Archivist', 'Fancyman', 'Linguist', 'Fletcher',
'Notary', 'Berserker-Typist', 'Fishmongerer', 'Manicurist', 'Haberdasher', 'Concierge'])
var image = _.sample(_.map([
"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://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://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://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",
], function(url){
return "<img src = '" + url + "' style='max-width:8cm;max-height:25cm' />"
}))
return [
image,
"",
"```",
"```",
"<div style='margin-top:240px'></div>\n\n",
"## " + classname,
"Cool intro stuff will go here",
"\\page",
ClassTableGen(classname),
ClassFeatureGen(classname),
].join('\n') + '\n\n\n';
};

View File

@@ -1,196 +0,0 @@
var _ = require('lodash');
var genList = function(list, max){
return _.sampleSize(list, _.random(0,max)).join(', ') || "None";
}
var getMonsterName = function(){
return _.sample([
"All-devouring Baseball Imp",
"All-devouring Gumdrop Wraith",
"Chocolate Hydra",
"Devouring Peacock",
"Economy-sized Colossus of the Lemonade Stand",
"Ghost Pigeon",
"Gibbering Duck",
"Sparklemuffin Peacock Spider",
"Gum Elemental",
"Illiterate Construct of the Candy Store",
"Ineffable Chihuahua",
"Irritating Death Hamster",
"Irritating Gold Mouse",
"Juggernaut Snail",
"Juggernaut of the Sock Drawer",
"Koala of the Cosmos",
"Mad Koala of the West",
"Milk Djinni of the Lemonade Stand",
"Mind Ferret",
"Mystic Salt Spider",
"Necrotic Halitosis Angel",
"Pinstriped Famine Sheep",
"Ritalin Leech",
"Shocker Kangaroo",
"Stellar Tennis Juggernaut",
"Wailing Quail of the Sun",
"Angel Pigeon",
"Anime Sphinx",
"Bored Avalanche Sheep of the Wasteland",
"Devouring Nougat Sphinx of the Sock Drawer",
"Djinni of the Footlocker",
"Ectoplasmic Jazz Devil",
"Flatuent Angel",
"Gelatinous Duck of the Dream-Lands",
"Gelatinous Mouse",
"Golem of the Footlocker",
"Lich Wombat",
"Mechanical Sloth of the Past",
"Milkshake Succubus",
"Puffy Bone Peacock of the East",
"Rainbow Manatee",
"Rune Parrot",
"Sand Cow",
"Sinister Vanilla Dragon",
"Snail of the North",
"Spider of the Sewer",
"Stellar Sawdust Leech",
"Storm Anteater of Hell",
"Stupid Spirit of the Brewery",
"Time Kangaroo",
"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(){
return '>|' + _.times(6, function(){
var num = _.random(1,20);
var mod = Math.ceil(num/2 - 5)
return num + " (" + (mod >= 0 ? '+'+mod : mod ) + ")"
}).join('|') + '|';
}
var 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.",
]);
}
var genAction = function(){
var 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 = {
full : function(){
return [
"___",
"___",
"> ## " + getMonsterName(),
">*" + getType() + ", " + getAlignment() + "*",
"> ___",
"> - **Armor Class** " + _.random(10,20),
"> - **Hit Points** " + _.random(1, 150) + "(1d4 + 5)",
"> - **Speed** " + _.random(0,50) + "ft.",
">___",
">|STR|DEX|CON|INT|WIS|CHA|",
">|:---:|:---:|:---:|:---:|:---:|:---:|",
getStats(),
">___",
"> - **Condition Immunities** " + genList(["groggy", "swagged", "weak-kneed", "buzzed", "groovy", "melancholy", "drunk"], 3),
"> - **Senses** passive Perception " + _.random(3, 20),
"> - **Languages** " + genList(["Common", "Pottymouth", "Gibberish", "Latin", "Jive"], 2),
"> - **Challenge** " + _.random(0, 15) + " (" + _.random(10,10000)+ " XP)",
"> ___",
_.times(_.random(3,6), function(){
return genAbilities()
}).join('\n>\n'),
"> ### Actions",
_.times(_.random(4,6), function(){
return genAction()
}).join('\n>\n'),
].join('\n') + '\n\n\n';
},
half : function(){
return [
"___",
"> ## " + getMonsterName(),
">*" + getType() + ", " + getAlignment() + "*",
"> ___",
"> - **Armor Class** " + _.random(10,20),
"> - **Hit Points** " + _.random(1, 150) + "(1d4 + 5)",
"> - **Speed** " + _.random(0,50) + "ft.",
">___",
">|STR|DEX|CON|INT|WIS|CHA|",
">|:---:|:---:|:---:|:---:|:---:|:---:|",
getStats(),
">___",
"> - **Condition Immunities** " + genList(["groggy", "swagged", "weak-kneed", "buzzed", "groovy", "melancholy", "drunk"], 3),
"> - **Senses** passive Perception " + _.random(3, 20),
"> - **Languages** " + genList(["Common", "Pottymouth", "Gibberish", "Latin", "Jive"], 2),
"> - **Challenge** " + _.random(0, 15) + " (" + _.random(10,10000)+ " XP)",
"> ___",
_.times(_.random(0,2), function(){
return genAbilities()
}).join('\n>\n'),
"> ### Actions",
_.times(_.random(1,2), function(){
return genAction()
}).join('\n>\n'),
].join('\n') + '\n\n\n';
}
}

View File

@@ -1,175 +0,0 @@
var SpellGen = require('./spell.gen.js');
var ClassTableGen = require('./classtable.gen.js');
var MonsterBlockGen = require('./monsterblock.gen.js');
var ClassFeatureGen = require('./classfeature.gen.js');
var FullClassGen = require('./fullclass.gen.js');
module.exports = [
{
groupName : 'Editor',
icon : 'fa-pencil',
snippets : [
{
name : "Column Break",
icon : 'fa-columns',
gen : "```\n```\n\n"
},
{
name : "New Page",
icon : 'fa-file-text',
gen : "\\page\n\n"
},
{
name : "Vertical Spacing",
icon : 'fa-arrows-v',
gen : "<div style='margin-top:140px'></div>\n\n"
},
{
name : "Image",
icon : 'fa-image',
gen : [
"<img ",
" src='https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg' ",
" style='width:325px' />",
"Credit: Kyounghwan Kim"
].join('\n')
},
{
name : "Background Image",
icon : 'fa-tree',
gen : [
"<img ",
" src='http://i.imgur.com/hMna6G0.png' ",
" style='position:absolute; top:50px; right:30px; width:280px' />"
].join('\n')
},
{
name : "Page Number",
icon : 'fa-bookmark',
gen : "<div class='pageNumber'>1</div>\n<div class='footnote'>PART 1 | FANCINESS</div>\n\n"
},
]
},
/************************* PHB ********************/
{
groupName : 'PHB',
icon : 'fa-book',
snippets : [
{
name : 'Spell',
icon : 'fa-magic',
gen : SpellGen,
},
{
name : 'Class Feature',
icon : 'fa-trophy',
gen : ClassFeatureGen,
},
{
name : 'Note',
icon : 'fa-sticky-note',
gen : function(){
return [
"> ##### Time to Drop Knowledge",
"> Use notes to point out some interesting information. ",
"> ",
"> **Tables and lists** both work within a note."
].join('\n');
},
},
{
name : 'Monster Stat Block',
icon : 'fa-bug',
gen : MonsterBlockGen.half,
},
{
name : 'Wide Monster Stat Block',
icon : 'fa-paw',
gen : MonsterBlockGen.full,
}
]
},
/********************* TABLES *********************/
{
groupName : 'Tables',
icon : 'fa-table',
snippets : [
{
name : "Class Table",
icon : 'fa-table',
gen : ClassTableGen.full,
},
{
name : "Half Class Table",
icon : 'fa-list-alt',
gen : ClassTableGen.half,
},
{
name : 'Table',
icon : 'fa-th-list',
gen : function(){
return [
"##### Cookie Tastiness",
"| Tastiness | Cookie Type |",
"|:----:|:-------------|",
"| -5 | Raisin |",
"| 8th | Chocolate Chip |",
"| 11th | 2 or lower |",
"| 14th | 3 or lower |",
"| 17th | 4 or lower |\n\n",
].join('\n');
},
}
]
},
/**************** PRINT *************/
{
groupName : 'Print',
icon : 'fa-print',
snippets : [
{
name : "A4 PageSize",
icon : 'fa-file-o',
gen : ['<style>',
' .phb{',
' width : 210mm;',
' height : 296.8mm;',
' }',
'</style>'
].join('\n')
},
{
name : "Ink Friendly",
icon : 'fa-tint',
gen : ['<style>',
' .phb{ background : white;}',
' .phb img{ display : none;}',
' .phb hr+blockquote{background : white;}',
'</style>',
''
].join('\n')
},
]
},
]

View File

@@ -1,78 +0,0 @@
var _ = require('lodash');
module.exports = function(){
var spellNames = [
"Astral Rite of Acne",
"Create Acne",
"Cursed Ramen Erruption",
"Dark Chant of the Dentists",
"Erruption of Immaturity",
"Flaming Disc of Inconvenience",
"Heal Bad Hygene",
"Heavenly Transfiguration of the Cream Devil",
"Hellish Cage of Mucus",
"Irritate Peanut Butter Fairy",
"Luminous Erruption of Tea",
"Mystic Spell of the Poser",
"Sorcerous Enchantment of the Chimneysweep",
"Steak Sauce Ray",
"Talk to Groupie",
"Astonishing Chant of Chocolate",
"Astounding Pasta Puddle",
"Ball of Annoyance",
"Cage of Yarn",
"Control Noodles Elemental",
"Create Nervousness",
"Cure Baldness",
"Cursed Ritual of Bad Hair",
"Dispell Piles in Dentist",
"Eliminate Florists",
"Illusionary Transfiguration of the Babysitter",
"Necromantic Armor of Salad Dressing",
"Occult Transfiguration of Foot Fetish",
"Protection from Mucus Giant",
"Tinsel Blast",
"Alchemical Evocation of the Goths",
"Call Fangirl",
"Divine Spell of Crossdressing",
"Dominate Ramen Giant",
"Eliminate Vindictiveness in Gym Teacher",
"Extra-Planar Spell of Irritation",
"Induce Whining in Babysitter",
"Invoke Complaining",
"Magical Enchantment of Arrogance",
"Occult Globe of Salad Dressing",
"Overwhelming Enchantment of the Chocolate Fairy",
"Sorcerous Dandruff Globe",
"Spiritual Invocation of the Costumers",
"Ultimate Rite of the Confetti Angel",
"Ultimate Ritual of Mouthwash",
];
var level = ["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th"];
var spellSchools = ["abjuration", "conjuration", "divination", "enchantment", "evocation", "illusion", "necromancy", "transmutation"];
var components = _.sampleSize(["V", "S", "M"], _.random(1,3)).join(', ');
if(components.indexOf("M") !== -1){
components += " (" + _.sampleSize(['a small doll', 'a crushed button worth at least 1cp', 'discarded gum wrapper'], _.random(1,3)).join(', ') + ")"
}
return [
"#### " + _.sample(spellNames),
"*" + _.sample(level) + "-level " + _.sample(spellSchools) + "*",
"___",
"- **Casting Time:** 1 action",
"- **Range:** " + _.sample(["Self", "Touch", "30 feet", "60 feet"]),
"- **Components:** " + components,
"- **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. ",
"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.",
"\n\n\n"
].join('\n');
}

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,56 +1,69 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
require('./homebrew.less');
const React = require('react');
const createClass = require('create-react-class');
const { StaticRouter:Router, Switch, Route } = require('react-router-dom');
const queryString = require('query-string');
var CreateRouter = require('pico-router').createRouter;
const HomePage = require('./pages/homePage/homePage.jsx');
const EditPage = require('./pages/editPage/editPage.jsx');
const UserPage = require('./pages/userPage/userPage.jsx');
const SharePage = require('./pages/sharePage/sharePage.jsx');
const NewPage = require('./pages/newPage/newPage.jsx');
//const ErrorPage = require('./pages/errorPage/errorPage.jsx');
const PrintPage = require('./pages/printPage/printPage.jsx');
var HomePage = require('./pages/homePage/homePage.jsx');
var EditPage = require('./pages/editPage/editPage.jsx');
var SharePage = require('./pages/sharePage/sharePage.jsx');
var NewPage = require('./pages/newPage/newPage.jsx');
var Router;
var Homebrew = React.createClass({
getDefaultProps: function() {
const Homebrew = createClass({
getDefaultProps : function() {
return {
url : "",
welcomeText : "",
changelog : "",
brew : {
title : '',
text : '',
shareId : null,
editId : null,
url : '',
welcomeText : '',
changelog : '',
version : '0.0.0',
account : null,
brew : {
title : '',
text : '',
shareId : null,
editId : null,
createdAt : null,
updatedAt : null,
}
};
},
componentWillMount: function() {
Router = CreateRouter({
'/homebrew/edit/:id' : (args) => {
return <EditPage id={args.id} brew={this.props.brew} />
},
componentWillMount : function() {
global.account = this.props.account;
global.version = this.props.version;
'/homebrew/share/:id' : (args) => {
return <SharePage id={args.id} brew={this.props.brew} />
},
'/homebrew/changelog' : (args) => {
return <SharePage brew={{title : 'Changelog', text : this.props.changelog}} />
},
'/homebrew/new' : (args) => {
return <NewPage />
},
'/homebrew*' : <HomePage welcomeText={this.props.welcomeText} />,
});
},
render : function(){
return(
<div className='homebrew'>
<Router initialUrl={this.props.url}/>
</div>
render : function (){
return (
<Router location={this.props.url}>
<div className='homebrew'>
<Switch>
<Route path='/edit/:id' component={(routeProps)=><EditPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/share/:id' component={(routeProps)=><SharePage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/user/:username' component={(routeProps)=><UserPage username={routeProps.match.params.username} brews={this.props.brews} />}/>
<Route path='/print/:id' component={(routeProps)=><PrintPage brew={this.props.brew} query={queryString.parse(routeProps.location.search)} /> } />
<Route path='/print' exact component={(routeProps)=><PrintPage query={queryString.parse(routeProps.location.search)} /> } />
<Route path='/new' exact component={NewPage}/>
<Route path='/changelog' exact component={()=><SharePage brew={{ title: 'Changelog', text: this.props.changelog }} />}/>
<Route path='/' component={()=><HomePage welcomeText={this.props.welcomeText}/>}/>
</Switch>
</div>
</Router>
);
}
});
module.exports = Homebrew;
//TODO: Nicer Error page instead of just "cant get that"
// '/share/:id' : (args)=>{
// if(!this.props.brew.shareId){
// return <ErrorPage errorId={args.id}/>;
// }
//
// return <SharePage
// id={args.id}
// brew={this.props.brew} />;
// },

View File

@@ -1,17 +1,15 @@
@import 'naturalcrit/styles/core.less';
.homebrew{
height : 100%;
//TODO: Consider making backgroudn color lighter
background-color : @steel;
.page{
display : flex;
height : 100%;
flex-direction : column;
.content{
position : relative;
height : calc(~"100% - 29px"); //Navbar height
flex : auto;
}
}
}
@import 'naturalcrit/styles/core.less';
.homebrew{
height : 100%;
.page{
display : flex;
height : 100%;
background-color : @steel;
flex-direction : column;
.content{
position : relative;
height : calc(~"100% - 29px"); //Navbar height
flex : auto;
}
}
}

View File

@@ -0,0 +1,34 @@
const React = require('react');
const createClass = require('create-react-class');
const Nav = require('naturalcrit/nav/nav.jsx');
const Account = createClass({
getInitialState : function() {
return {
url : ''
};
},
componentDidMount : function(){
if(typeof window !== 'undefined'){
this.setState({
url : window.location.href
});
}
},
render : function(){
if(global.account){
return <Nav.item href={`/user/${global.account.username}`} color='yellow' icon='fa-user'>
{global.account.username}
</Nav.item>;
}
return <Nav.item href={`http://naturalcrit.com/login?redirect=${this.state.url}`} color='teal' icon='fa-sign-in'>
login
</Nav.item>;
}
});
module.exports = Account;

View File

@@ -1,15 +1,15 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var Nav = require('naturalcrit/nav/nav.jsx');
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const Nav = require('naturalcrit/nav/nav.jsx');
const MAX_TITLE_LENGTH = 50;
var EditTitle = React.createClass({
getDefaultProps: function() {
const EditTitle = createClass({
getDefaultProps : function() {
return {
title : '',
title : '',
onChange : function(){}
};
},
@@ -22,12 +22,12 @@ var EditTitle = React.createClass({
return <Nav.item className='editTitle'>
<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}
</div>
</Nav.item>
</Nav.item>;
},
});
module.exports = EditTitle;
module.exports = EditTitle;

View File

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

View File

@@ -1,20 +1,50 @@
var React = require('react');
var _ = require('lodash');
require('./navbar.less');
const React = require('react');
const createClass = require('create-react-class');
var Nav = require('naturalcrit/nav/nav.jsx');
const Nav = require('naturalcrit/nav/nav.jsx');
const PatreonNavItem = require('./patreon.navitem.jsx');
var Navbar = React.createClass({
const Navbar = createClass({
getInitialState : function() {
return {
//showNonChromeWarning : false,
ver : '0.0.0'
};
},
componentDidMount : function() {
//const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
this.setState({
//showNonChromeWarning : !isChrome,
ver : window.version
});
},
/*
renderChromeWarning : function(){
if(!this.state.showNonChromeWarning) return;
return <Nav.item className='warning' icon='fa-exclamation-triangle'>
Optimized for Chrome
<div className='dropdown'>
If you are experiencing rendering issues, use Chrome instead
</div>
</Nav.item>
},
*/
render : function(){
return <Nav.base>
<Nav.section>
<Nav.logo />
<Nav.item href='/homebrew' className='homebrewLogo'>
<Nav.item href='/' className='homebrewLogo'>
<div>The Homebrewery</div>
</Nav.item>
<Nav.item>v2.0.0</Nav.item>
<Nav.item>{`v${this.state.ver}`}</Nav.item>
<PatreonNavItem />
{/*this.renderChromeWarning()*/}
</Nav.section>
{this.props.children}
</Nav.base>
</Nav.base>;
}
});

View File

@@ -1,58 +1,141 @@
.homebrew nav{
.homebrewLogo{
.animate(color);
font-family : CodeBold;
font-size : 12px;
color : white;
div{
margin-top : 2px;
margin-bottom : -2px;
}
&:hover{
color : @blue;
}
}
.editTitle.navItem{
padding : 2px 12px;
input{
margin : 0;
padding : 2px;
width : 250px;
background-color : #444;
font-family : 'Open Sans', sans-serif;
font-size : 12px;
font-weight : 800;
color : white;
text-align : center;
border : 1px solid @blue;
outline : none;
}
.charCount{
display : inline-block;
vertical-align : bottom;
margin-left : 8px;
text-align : right;
color : #666;
&.max{
color : @red;
}
}
}
.brewTitle.navItem{
font-size : 12px;
font-weight : 800;
color : white;
text-align : center;
text-transform: initial;
}
.patreon.navItem{
i{
.animate(color);
&:hover{
color : @red;
}
}
}
}
@navbarHeight : 28px;
@keyframes coloring {
//from {color: white;}
//to {color: red;}
0% {color: pink;}
50% {color: pink;}
75% {color: red;}
100% {color: pink;}
}
.homebrew nav{
.homebrewLogo{
.animate(color);
font-family : CodeBold;
font-size : 12px;
color : white;
div{
margin-top : 2px;
margin-bottom : -2px;
}
&:hover{
color : @blue;
}
}
.editTitle.navItem{
padding : 2px 12px;
input{
width : 250px;
margin : 0;
padding : 2px;
background-color : #444;
font-family : 'Open Sans', sans-serif;
font-size : 12px;
font-weight : 800;
color : white;
text-align : center;
border : 1px solid @blue;
outline : none;
}
.charCount{
display : inline-block;
vertical-align : bottom;
margin-left : 8px;
color : #666;
text-align : right;
&.max{
color : @red;
}
}
}
.brewTitle.navItem{
font-size : 12px;
font-weight : 800;
color : white;
text-align : center;
text-transform : initial;
}
.patreon.navItem{
border-left : 1px solid #666;
border-right : 1px solid #666;
&:hover i {
color: red;
}
i{
.animate(color);
animation-name: coloring;
animation-duration: 2s;
color: pink;
}
}
.recent.navItem{
position : relative;
.dropdown{
position : absolute;
top : 28px;
left : 0px;
z-index : 10000;
width : 100%;
h4{
display : block;
box-sizing : border-box;
padding : 5px 0px;
background-color : #333;
font-size : 0.8em;
color : #bbb;
text-align : center;
border-top : 1px solid #888;
&:nth-of-type(1){ background-color: darken(@teal, 20%); }
&:nth-of-type(2){ background-color: darken(@purple, 30%); }
}
.item{
.animate(background-color);
position : relative;
display : block;
box-sizing : border-box;
padding : 13px 5px;
background-color : #333;
color : white;
text-decoration : none;
border-top : 1px solid #888;
&:hover{
background-color : @blue;
}
.title{
display : inline-block;
overflow : hidden;
width : 100%;
text-overflow : ellipsis;
white-space : nowrap;
}
.time{
position : absolute;
right : 2px;
bottom : 2px;
font-size : 0.7em;
color : #888;
}
}
}
}
.warning.navItem{
position : relative;
background-color : @orange;
color : white;
&:hover>.dropdown{
visibility : visible;
}
.dropdown{
position : absolute;
display : block;
top : 28px;
left : 0px;
visibility : hidden;
z-index : 10000;
box-sizing : border-box;
width : 100%;
padding : 13px 5px;
background-color : #333;
text-align : center;
}
}
}

View File

@@ -1,13 +1,14 @@
var React = require('react');
var Nav = require('naturalcrit/nav/nav.jsx');
const React = require('react');
const createClass = require('create-react-class');
const Nav = require('naturalcrit/nav/nav.jsx');
module.exports = function(props){
return <Nav.item
className='patreon'
newTab={true}
href='https://www.patreon.com/stolksdorf'
href='https://www.patreon.com/NaturalCrit'
color='green'
icon='fa-heart'>
help out
</Nav.item>
};
</Nav.item>;
};

View File

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

View File

@@ -0,0 +1,185 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const Moment = require('moment');
const Nav = require('naturalcrit/nav/nav.jsx');
const EDIT_KEY = 'homebrewery-recently-edited';
const VIEW_KEY = 'homebrewery-recently-viewed';
const RecentItems = createClass({
getDefaultProps : function() {
return {
storageKey : '',
showEdit : false,
showView : false
};
},
getInitialState : function() {
return {
showDropdown : false,
edit : [],
view : []
};
},
componentDidMount : function() {
//== Load recent items list ==//
let edited = JSON.parse(localStorage.getItem(EDIT_KEY) || '[]');
let viewed = JSON.parse(localStorage.getItem(VIEW_KEY) || '[]');
//== Add current brew to appropriate recent items list (depending on storageKey) ==//
if(this.props.storageKey == 'edit'){
let editId = this.props.brew.editId;
if(this.props.brew.googleId){
editId = `${this.props.brew.googleId}${this.props.brew.editId}`;
}
edited = _.filter(edited, (brew)=>{
return brew.id !== editId;
});
edited.unshift({
id : editId,
title : this.props.brew.title,
url : `/edit/${editId}`,
ts : Date.now()
});
}
if(this.props.storageKey == 'view'){
let shareId = this.props.brew.shareId;
if(this.props.brew.googleId){
shareId = `${this.props.brew.googleId}${this.props.brew.shareId}`;
}
viewed = _.filter(viewed, (brew)=>{
return brew.id !== shareId;
});
viewed.unshift({
id : shareId,
title : this.props.brew.title,
url : `/share/${shareId}`,
ts : Date.now()
});
}
//== Store the updated lists (up to 8 items each) ==//
edited = _.slice(edited, 0, 8);
viewed = _.slice(viewed, 0, 8);
localStorage.setItem(EDIT_KEY, JSON.stringify(edited));
localStorage.setItem(VIEW_KEY, JSON.stringify(viewed));
this.setState({
edit : edited,
view : viewed
});
},
componentDidUpdate : function(prevProps) {
if(prevProps.brew && this.props.brew.editId !== prevProps.brew.editId) {
if(this.props.storageKey == 'edit') {
let prevEditId = prevProps.brew.editId;
if(prevProps.brew.googleId){
prevEditId = `${prevProps.brew.googleId}${prevProps.brew.editId}`;
}
edited = _.filter(this.state.edit, (brew)=>{
return brew.id !== prevEditId;
});
let editId = this.props.brew.editId;
if(this.props.brew.googleId){
editId = `${this.props.brew.googleId}${this.props.brew.editId}`;
}
edited.unshift({
id : editId,
title : this.props.brew.title,
url : `/edit/${editId}`,
ts : Date.now()
});
}
//== Store the updated lists (up to 8 items each) ==//
edited = _.slice(edited, 0, 8);
localStorage.setItem(EDIT_KEY, JSON.stringify(edited));
this.setState({
edit : edited
});
}
},
handleDropdown : function(show){
this.setState({
showDropdown : show
});
},
renderDropdown : function(){
if(!this.state.showDropdown) return null;
const makeItems = (brews)=>{
return _.map(brews, (brew)=>{
return <a href={brew.url} className='item' key={brew.id} target='_blank' rel='noopener noreferrer'>
<span className='title'>{brew.title || '[ no title ]'}</span>
<span className='time'>{Moment(brew.ts).fromNow()}</span>
</a>;
});
};
return <div className='dropdown'>
{(this.props.showEdit && this.props.showView) ?
<h4>edited</h4> : null }
{this.props.showEdit ?
makeItems(this.state.edit) : null }
{(this.props.showEdit && this.props.showView) ?
<h4>viewed</h4> : null }
{this.props.showView ?
makeItems(this.state.view) : null }
</div>;
},
render : function(){
return <Nav.item icon='fa-clock-o' color='grey' className='recent'
onMouseEnter={()=>this.handleDropdown(true)}
onMouseLeave={()=>this.handleDropdown(false)}>
{this.props.text}
{this.renderDropdown()}
</Nav.item>;
}
});
module.exports = {
edited : (props)=>{
return <RecentItems
brew={props.brew}
storageKey={props.storageKey}
text='recently edited'
showEdit={true}
/>;
},
viewed : (props)=>{
return <RecentItems
brew={props.brew}
storageKey={props.storageKey}
text='recently viewed'
showView={true}
/>;
},
both : (props)=>{
return <RecentItems
brew={props.brew}
storageKey={props.storageKey}
text='recent brews'
showEdit={true}
showView={true}
/>;
}
};

View File

@@ -1,22 +1,19 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
//var striptags = require('striptags');
var Nav = require('naturalcrit/nav/nav.jsx');
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const Nav = require('naturalcrit/nav/nav.jsx');
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({
getDefaultProps: function() {
const RedditShare = createClass({
getDefaultProps : function() {
return {
brew : {
title : '',
title : '',
sharedId : '',
text : ''
text : ''
}
};
},
@@ -27,13 +24,10 @@ var RedditShare = React.createClass({
handleClick : function(){
var url = [
const url = [
MAIN_URL,
'title=' + encodeURIComponent(this.props.brew.title ? this.props.brew.title : 'Check out my brew!'),
'text=' + encodeURIComponent(this.props.brew.text)
`title=${encodeURIComponent(this.props.brew.title ? this.props.brew.title : 'Check out my brew!')}`,
`text=${encodeURIComponent(this.props.brew.text)}`
].join('&');
window.open(url, '_blank');
@@ -43,9 +37,9 @@ var RedditShare = React.createClass({
render : function(){
return <Nav.item icon='fa-reddit-alien' color='red' onClick={this.handleClick}>
share on reddit
</Nav.item>
</Nav.item>;
},
});
module.exports = RedditShare;
module.exports = RedditShare;

View File

@@ -1,171 +1,384 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var request = require("superagent");
/* eslint-disable max-lines */
require('./editPage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const request = require('superagent');
const { Meta } = require('vitreum/headtags');
var Nav = require('naturalcrit/nav/nav.jsx');
var Navbar = require('../../navbar/navbar.jsx');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
var EditTitle = require('../../navbar/editTitle.navitem.jsx');
var ReportIssue = require('../../navbar/issue.navitem.jsx');
var PrintLink = require('../../navbar/print.navitem.jsx');
const ReportIssue = require('../../navbar/issue.navitem.jsx');
const PrintLink = require('../../navbar/print.navitem.jsx');
const Account = require('../../navbar/account.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
var SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
var Editor = require('../../editor/editor.jsx');
var BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const Markdown = require('naturalcrit/markdown.js');
const googleDriveActive = require('../../googleDrive.png');
const googleDriveInactive = require('../../googleDriveMono.png');
const SAVE_TIMEOUT = 3000;
var EditPage = React.createClass({
getDefaultProps: function() {
const EditPage = createClass({
getDefaultProps : function() {
return {
id : null,
brew : {
title : '',
text : '',
shareId : null,
editId : null,
text : '',
shareId : null,
editId : null,
createdAt : null,
updatedAt : null,
gDrive : false,
title : '',
description : '',
tags : '',
published : false,
authors : [],
systems : []
}
};
},
getInitialState: function() {
getInitialState : function() {
return {
title : this.props.brew.title,
text: this.props.brew.text,
isSaving : false,
isPending : false,
errors : null,
lastUpdated : this.props.brew.updatedAt
brew : this.props.brew,
isSaving : false,
isPending : false,
saveGoogle : this.props.brew.googleId ? true : false,
confirmGoogleTransfer : false,
errors : null,
htmlErrors : Markdown.validate(this.props.brew.text),
url : ''
};
},
savedBrew : null,
componentDidMount: function(){
this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT);
componentDidMount : function(){
this.setState({
url : window.location.href
});
this.trySave();
window.onbeforeunload = ()=>{
if(this.state.isSaving || this.state.isPending){
return 'You have unsaved changes!';
}
}
};
this.setState((prevState)=>({
htmlErrors : Markdown.validate(prevState.brew.text)
}));
document.addEventListener('keydown', this.handleControlKeys);
},
componentWillUnmount: function() {
componentWillUnmount : function() {
window.onbeforeunload = function(){};
document.removeEventListener('keydown', this.handleControlKeys);
},
handleControlKeys : function(e){
if(!(e.ctrlKey || e.metaKey)) return;
const S_KEY = 83;
const P_KEY = 80;
if(e.keyCode == S_KEY) this.save();
if(e.keyCode == P_KEY) window.open(`/print/${this.processShareId()}?dialog=true`, '_blank').focus();
if(e.keyCode == P_KEY || e.keyCode == S_KEY){
e.stopPropagation();
e.preventDefault();
}
},
handleSplitMove : function(){
this.refs.editor.update();
},
handleTitleChange : function(title){
this.setState({
title : title,
isPending : true
});
handleMetadataChange : function(metadata){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, metadata),
isPending : true,
}), ()=>this.trySave());
(this.hasChanges() ? this.debounceSave() : this.debounceSave.cancel());
},
handleTextChange : function(text){
this.setState({
text : text,
isPending : true
});
(this.hasChanges() ? this.debounceSave() : this.debounceSave.cancel());
},
//If there are errors, run the validator on everychange to give quick feedback
let htmlErrors = this.state.htmlErrors;
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
handleDelete : function(){
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;
request.get('/homebrew/api/remove/' + this.props.brew.editId)
.send()
.end(function(err, res){
window.location.href = '/homebrew';
});
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { text: text }),
isPending : true,
htmlErrors : htmlErrors
}), ()=>this.trySave());
},
hasChanges : function(){
if(this.savedBrew){
if(this.state.text !== this.savedBrew.text) return true;
if(this.state.title !== this.savedBrew.title) return true;
}else{
if(this.state.text !== this.props.brew.text) return true;
if(this.state.title !== this.props.brew.title) return true;
}
return false;
const savedBrew = this.savedBrew ? this.savedBrew : this.props.brew;
return !_.isEqual(this.state.brew, savedBrew);
},
save : function(){
this.debounceSave.cancel();
this.setState({
isSaving : true
});
trySave : function(){
if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT);
if(this.hasChanges()){
this.debounceSave();
} else {
this.debounceSave.cancel();
}
},
handleGoogleClick : function(){
this.setState((prevState)=>({
confirmGoogleTransfer : !prevState.confirmGoogleTransfer
}));
this.clearErrors();
},
toggleGoogleStorage : function(){
this.setState((prevState)=>({
saveGoogle : !prevState.saveGoogle,
isSaving : false,
errors : null
}), ()=>this.save());
},
clearErrors : function(){
this.setState({
errors : null,
isSaving : false
});
},
save : async function(){
if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel();
this.setState((prevState)=>({
isSaving : true,
errors : null,
htmlErrors : Markdown.validate(prevState.brew.text)
}));
const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId);
if(this.state.saveGoogle) {
if(transfer) {
const res = await request
.post('/api/newGoogle/')
.send(this.state.brew)
.catch((err)=>{
console.log(err.status === 401
? 'Not signed in!'
: 'Error Saving to Google!');
this.setState({ errors: err, saveGoogle: false });
});
if(!res) { return; }
console.log('Deleting Local Copy');
await request.delete(`/api/${this.state.brew.editId}`)
.send()
.catch((err)=>{
console.log('Error deleting Local Copy');
});
request
.put('/homebrew/api/update/' + this.props.brew.editId)
.send({
text : this.state.text,
title : this.state.title
})
.end((err, res) => {
this.savedBrew = res.body;
this.setState({
isPending : false,
isSaving : false,
lastUpdated : res.body.updatedAt
})
})
history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID
} else {
const res = await request
.put(`/api/updateGoogle/${this.state.brew.editId}`)
.send(this.state.brew)
.catch((err)=>{
console.log(err.status === 401
? 'Not signed in!'
: 'Error Saving to Google!');
this.setState({ errors: err, saveGoogle: false });
return;
});
this.savedBrew = res.body;
}
} else {
if(transfer) {
const res = await request.post('/api')
.send(this.state.brew)
.catch((err)=>{
console.log('Error creating Local Copy');
this.setState({ errors: err });
return;
});
await request.get(`/api/removeGoogle/${this.state.brew.googleId}${this.state.brew.editId}`)
.send()
.catch((err)=>{
console.log('Error Deleting Google Brew');
});
this.savedBrew = res.body;
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID
} else {
const res = await request
.put(`/api/update/${this.state.brew.editId}`)
.send(this.state.brew)
.catch((err)=>{
console.log('Error Updating Local Brew');
this.setState({ errors: err });
return;
});
this.savedBrew = res.body;
}
}
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, {
googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null,
editId : this.savedBrew.editId,
shareId : this.savedBrew.shareId
}),
isPending : false,
isSaving : false,
}));
},
renderGoogleDriveIcon : function(){
if(this.state.saveGoogle) {
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
<img src={googleDriveActive} alt='googleDriveActive' />
{this.state.confirmGoogleTransfer &&
<div className='errorContainer'>
Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?<br />
<div className='confirm' onClick={this.toggleGoogleStorage}>
Yes
</div>
<div className='deny'>
No
</div>
</div>
}
</Nav.item>;
} else {
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
<img src={googleDriveInactive} alt='googleDriveInactive' />
{this.state.confirmGoogleTransfer &&
<div className='errorContainer'>
Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?<br />
<div className='confirm' onClick={this.toggleGoogleStorage}>
Yes
</div>
<div className='deny'>
No
</div>
</div>
}
</Nav.item>;
}
},
renderSaveButton : function(){
if(this.state.isSaving){
return <Nav.item className='save' icon="fa-spinner fa-spin">saving...</Nav.item>
if(this.state.errors){
let errMsg = '';
try {
errMsg += `${this.state.errors.toString()}\n\n`;
errMsg += `\`\`\`\n${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``;
} catch (e){}
if(this.state.errors.status == '401'){
return <Nav.item className='save error' icon='fa-warning'>
Oops!
<div className='errorContainer' onClick={this.clearErrors}>
You must be signed in to a Google account
to save this to<br />Google Drive!<br />
<a target='_blank' rel='noopener noreferrer'
href={`http://naturalcrit.com/login?redirect=${this.state.url}`}>
<div className='confirm' onClick={this.toggleGoogleStorage}>
Sign In
</div>
</a>
<div className='deny'>
Not Now
</div>
</div>
</Nav.item>;
}
return <Nav.item className='save error' icon='fa-warning'>
Oops!
<div className='errorContainer'>
Looks like there was a problem saving. <br />
Report the issue <a target='_blank' rel='noopener noreferrer'
href={`https://github.com/naturalcrt/naturalcrit/issues/new?body=${encodeURIComponent(errMsg)}`}>
here
</a>.
</div>
</Nav.item>;
}
if(!this.state.isPending && !this.state.isSaving){
return <Nav.item className='save saved'>saved.</Nav.item>
if(this.state.isSaving){
return <Nav.item className='save' icon='fa-spinner fa-spin'>saving...</Nav.item>;
}
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){
return <Nav.item className='save saved'>saved.</Nav.item>;
}
},
processShareId : function() {
return this.state.brew.googleId ?
this.state.brew.googleId + this.state.brew.shareId :
this.state.brew.shareId;
},
renderNavbar : function(){
return <Navbar>
<Nav.section>
<EditTitle title={this.state.title} onChange={this.handleTitleChange} />
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
{this.renderGoogleDriveIcon()}
{this.renderSaveButton()}
<Nav.item newTab={true} href={'/homebrew/share/' + this.props.brew.shareId} color='teal' icon='fa-share-alt'>
<ReportIssue />
<Nav.item newTab={true} href={`/share/${this.processShareId()}`} color='teal' icon='fa-share-alt'>
Share
</Nav.item>
<PrintLink shareId={this.props.brew.shareId} />
<Nav.item color='red' icon='fa-trash' onClick={this.handleDelete}>
Delete
</Nav.item>
<PrintLink shareId={this.processShareId()} />
<RecentNavItem brew={this.state.brew} storageKey='edit' />
<Account />
</Nav.section>
</Navbar>
</Navbar>;
},
render : function(){
return <div className='editPage page'>
<Meta name='robots' content='noindex, nofollow' />
{this.renderNavbar()}
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor value={this.state.text} onChange={this.handleTextChange} ref='editor'/>
<BrewRenderer text={this.state.text} />
<Editor
ref='editor'
value={this.state.brew.text}
onChange={this.handleTextChange}
metadata={this.state.brew}
onMetadataChange={this.handleMetadataChange}
/>
<BrewRenderer text={this.state.brew.text} errors={this.state.htmlErrors} />
</SplitPane>
</div>
</div>
</div>;
}
});

View File

@@ -1,12 +1,87 @@
.editPage{
.navItem.save{
width : 75px;
text-align: center;
&.saved{
color : #666;
cursor : initial;
}
}
}
.editPage{
.navItem.save{
width : 106px;
text-align : center;
&.saved{
cursor : initial;
color : #666;
}
&.error{
position : relative;
background-color : @red;
}
}
.googleDriveStorage {
position : relative;
}
.googleDriveStorage img{
height : 20px;
padding : 0px;
margin : -5px;
}
.errorContainer{
position : absolute;
top : 100%;
left : 50%;
z-index : 1000;
width : 140px;
padding : 3px;
background-color : #333;
border : 3px solid #444;
border-radius : 5px;
transform : translate(-50% + 3px, 10px);
text-align : center;
a{
color : @teal;
}
&:before {
content: "";
width: 0px;
height: 0px;
position: absolute;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid transparent;
border-bottom: 10px solid #444;
left: 53px;
top: -23px;
}
&:after {
content: "";
width: 0px;
height: 0px;
position: absolute;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid transparent;
border-bottom: 10px solid #333;
left: 53px;
top: -19px;
}
.deny {
width : 48%;
margin : 1px;
padding : 5px;
background-color : #333;
display : inline-block;
border-left : 1px solid #666;
.animate(background-color);
&:hover{
background-color : red;
}
}
.confirm {
width : 48%;
margin : 1px;
padding : 5px;
background-color : #333;
display : inline-block;
color : white;
.animate(background-color);
&:hover{
background-color : teal;
}
}
}
}

View File

@@ -0,0 +1,48 @@
require('./errorPage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const PatreonNavItem = require('../../navbar/patreon.navitem.jsx');
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const ErrorPage = createClass({
getDefaultProps : function() {
return {
ver : '0.0.0',
errorId : ''
};
},
text : '# Oops \n We could not find a brew with that id. **Sorry!**',
render : function(){
return <div className='errorPage page'>
<Navbar ver={this.props.ver}>
<Nav.section>
<Nav.item className='errorTitle'>
Crit Fail!
</Nav.item>
</Nav.section>
<Nav.section>
<PatreonNavItem />
<IssueNavItem />
<RecentNavItem />
</Nav.section>
</Navbar>
<div className='content'>
<BrewRenderer text={this.text} />
</div>
</div>;
}
});
module.exports = ErrorPage;

View File

@@ -0,0 +1,5 @@
.errorPage{
.errorTitle{
background-color: @orange;
}
}

View File

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

View File

@@ -1,28 +1,47 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
require('./homePage.less');
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const { Meta } = require('vitreum/headtags');
var Nav = require('naturalcrit/nav/nav.jsx');
var Navbar = require('../../navbar/navbar.jsx');
var PatreonNavItem = require('../../navbar/patreon.navitem.jsx');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const AccountNavItem = require('../../navbar/account.navitem.jsx');
var SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
var Editor = require('../../editor/editor.jsx');
var BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
var HomePage = React.createClass({
getDefaultProps: function() {
const HomePage = createClass({
getDefaultProps : function() {
return {
welcomeText : ""
welcomeText : '',
ver : '0.0.0'
};
},
getInitialState : function() {
return {
text : this.props.welcomeText
};
},
getInitialState: function() {
return {
text: this.props.welcomeText
};
handleSave : function(){
request.post('/api')
.send({
text : this.state.text
})
.end((err, res)=>{
if(err) return;
const brew = res.body;
window.location = `/edit/${brew.editId}`;
});
},
handleSplitMove : function(){
this.refs.editor.update();
@@ -33,37 +52,38 @@ var HomePage = React.createClass({
});
},
renderNavbar : function(){
return <Navbar>
return <Navbar ver={this.props.ver}>
<Nav.section>
<PatreonNavItem />
<Nav.item newTab={true} href='https://github.com/stolksdorf/naturalcrit/issues' color='red' icon='fa-bug'>
report issue
</Nav.item>
<Nav.item newTab={true} href='/homebrew/changelog' color='purple' icon='fa-file-text-o'>
<IssueNavItem />
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-file-text-o'>
Changelog
</Nav.item>
<Nav.item href='/homebrew/new' color='green' icon='fa-external-link'>
New Brew
</Nav.item>
<RecentNavItem />
<AccountNavItem />
</Nav.section>
</Navbar>
</Navbar>;
},
render : function(){
return <div className='homePage page'>
<Meta name='google-site-verification' content='NwnAQSSJZzAT7N-p5MY6ydQ7Njm67dtbu73ZSyE5Fy4' />
{this.renderNavbar()}
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor value={this.state.text} onChange={this.handleTextChange} ref='editor'/>
<Editor value={this.state.text} onChange={this.handleTextChange} showMetaButton={false} ref='editor'/>
<BrewRenderer text={this.state.text} />
</SplitPane>
</div>
<a href='/homebrew/new' className='floatingNewButton'>
<div className={cx('floatingSaveButton', { show: this.props.welcomeText != this.state.text })} onClick={this.handleSave}>
Save current <i className='fa fa-save' />
</div>
<a href='/new' className='floatingNewButton'>
Create your own <i className='fa fa-magic' />
</a>
</div>
</div>;
}
});

View File

@@ -1,23 +1,43 @@
.homePage{
position : relative;
a.floatingNewButton{
.animate(background-color);
position : absolute;
display : block;
right : 70px;
bottom : 70px;
z-index : 100;
padding : 1em;
background-color : @orange;
font-size : 1.5em;
color : white;
text-decoration : none;
box-shadow : 3px 3px 15px black;
&:hover{
background-color : darken(@orange, 20%);
}
}
.homePage{
position : relative;
a.floatingNewButton{
.animate(background-color);
position : absolute;
display : block;
right : 70px;
bottom : 70px;
z-index : 100;
z-index : 5001;
padding : 1em;
background-color : @orange;
font-size : 1.5em;
color : white;
text-decoration : none;
box-shadow : 3px 3px 15px black;
&:hover{
background-color : darken(@orange, 20%);
}
}
.floatingSaveButton{
.animateAll();
position : absolute;
display : block;
right : 200px;
bottom : 90px;
z-index : 100;
z-index : 5000;
padding : 0.8em;
cursor : pointer;
background-color : @blue;
font-size : 0.8em;
color : white;
text-decoration : none;
box-shadow : 3px 3px 15px black;
&:hover{
background-color : darken(@blue, 20%);
}
&.show{
right : 350px;
}
}
}

View File

@@ -1,107 +1,100 @@
# The Homebrewery
Welcome traveler from an antique land. Please sit and tell us of what you have seen. The unheard of monsters, who slither and bite. Tell us of the wondrous items and and artifacts you have found, their mysteries yet to be unlocked. Of the vexing vocations and surprising skills you have seen.
### Homebrew D&D made easy
The Homebrewery makes the creation and sharing of authentic looking Fifth-Edition homebrews easy. It uses [Markdown](https://help.github.com/articles/markdown-basics/) with a little CSS magic to make your brews come to life.
**Try it! **Simply edit the text on the left and watch it *update live* on the right.
#### Features
* Monster Stat Blocks
* Full class tables
* Notes and Tables
* Images
* Page numbering and footers
* Vertical spacing, column breaks, and multiple pages
### Editing and Sharing
When you create your own homebrew you will be given a *edit url* and a *share url*. Any changes you make will be automatically saved to the database within a few seconds. Anyone with the edit url will be able to make edits to your homebrew. So be careful about who you share it with.
Anyone with the *share url* will be able to access a read-only version of your homebrew.
## Helping out
Like this tool? Want to buy me a beer? [Head here](https://www.patreon.com/stolksdorf) to help me keep the servers running.
This tool will **always** be free, never have ads, and I will never offer any "premium" features or whatever.
### Bugs, Issues, Suggestions?
Have an idea of how to make The Homebrewery better? Or did you find something that wasn't quite right? Head [here](https://github.com/stolksdorf/NaturalCrit/issues/new) and let me know!.
```
```
## New Things in v2.0.0!
What's new in the latest update? Check out the full changelog [here](/homebrew/changelog)
* **A whole new look** The site has been re-built from the ground up!
* **Better editor and Split Pane** Syntax highlighting will make writing your brews even easier, and now you can customize how large your editor is.
* **More reliable rendering** Lots of work has been put into making the rendering more reliable, not just for web, but also for PDFs
* **PDF Printing on Chrome** You don't need to use Chrome Canary anymore!
* ** Performance Improvements** The site should load faster, save faster, and render large brews *much* faster.
* **Patreon page** If you like this tool and want to show some thanks you can [head here](https://www.patreon.com/stolksdorf).
>##### PDF Exporting
> After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up
> * Set the **Destination** to "Save as PDF"
> * Set **Paper Size** to "Letter"
> * If you are printing on A4 paper, make sure to have the "A4 page size snippet" in your brew
> * In **Options** make sure "Background Images" is selected.
> * Hit print and enjoy! You're done!
>
> If you want to save ink or have a monochrome printer, add the **Ink Friendly** snippet to your brew before you print
<img src='http://i.imgur.com/hMna6G0.png' style='position:absolute;bottom:50px;right:30px;width:280px' />
<div class='pageNumber'>1</div>
<div class='footnote'>PART 1 | FANCINESS</div>
\page
# Appendix
### Not quite Markdown
Although the Homebrewery uses Markdown, to get all the styling features from the PHB, we had to get a little creative. Some base HTML elements are not used as expected and I've had to include a few new keywords.
___
* **Horizontal Rules** are generally used to *modify* existing elements into a different style. For example, a horizontal rule before a blockquote will give it the style of a Monster Stat Block instead of a note.
* **New Pages** are controlled by the author. It's impossible for the site to detect when the end of a page is reached, so indicate you'd like to start a new page, use the new page snippet to get the syntax.
* **Code Blocks** are used only to indicate column breaks. Since they don't allow for styling within them, they weren't that useful to use.
* **HTML** can be used to get *just* the right look for your homebrew. I've included some examples in the snippet icons above the editor.
### Images
Images can be included 'inline' with the text using Markdown-style images. However for background images more control is needed.
Background images should be included as HTML-style img tags. Using inline CSS you can precisely position your image where you'd like it to be. I have added both a inflow image snippet and a background image snippet to give you exmaples of how to do it.
```
```
### Legal Junk
You are free to use The Homebrewery is any way that you want, except for claiming that you made it yourself. If you wish to sell or in some way gain profit for what's created on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used.
### Crediting Me
If you'd like to credit The Homebrewery in your brew, I'd be flattered! Just reference that you made it with The Homebrewery.
<div class='pageNumber'>2</div>
<div class='footnote'>PART 2 | BORING STUFF</div>
# The Homebrewery
Welcome traveler from an antique land. Please sit and tell us of what you have seen. The unheard of monsters, who slither and bite. Tell us of the wondrous items and and artifacts you have found, their mysteries yet to be unlocked. Of the vexing vocations and surprising skills you have seen.
### Homebrew D&D made easy
The Homebrewery makes the creation and sharing of authentic looking Fifth-Edition homebrews easy. It uses [Markdown](https://help.github.com/articles/markdown-basics/) with a little CSS magic to make your brews come to life.
**Try it! **Simply edit the text on the left and watch it *update live* on the right.
### Editing and Sharing
When you create your own homebrew you will be given a *edit url* and a *share url*. Any changes you make will be automatically saved to the database within a few seconds. Anyone with the edit url will be able to make edits to your homebrew. So be careful about who you share it with.
Anyone with the *share url* will be able to access a read-only version of your homebrew.
## Helping out
Like this tool? Want to buy me a beer? [Head here](https://www.patreon.com/stolksdorf) to help me keep the servers running.
This tool will **always** be free, never have ads, and I will never offer any "premium" features or whatever.
>##### PDF Exporting
> PDF Printing works best in Chrome. If you are having quality/consistency issues, try using Chrome to print instead.
>
> After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up.
> * Set the **Destination** to "Save as PDF"
> * Set **Paper Size** to "Letter"
> * If you are printing on A4 paper, make sure to have the "A4 page size snippet" in your brew
> * In **Options** make sure "Background Images" is selected.
> * Hit print and enjoy! You're done!
>
> If you want to save ink or have a monochrome printer, add the **Ink Friendly** snippet to your brew before you print
```
```
## Big things coming in v3.0.0
With the next major release of Homebrewery, v3.0.0, this tool *will no longer support raw HTML input for brew code*. All brews made previous to the release of v3.0.0 will still render normally.
## New Things All The Time!
What's new in the latest update? Check out the full changelog [here](/changelog)
### Bugs, Issues, Suggestions?
Have an idea of how to make The Homebrewery better? Or did you find something that wasn't quite right? Head [here](https://www.reddit.com/r/homebrewery/submit?selftext=true&title=%5BIssue%5D%20Describe%20Your%20Issue%20Here) and let me know!.
### Legal Junk
The Homebrewery is licensed using the [MIT License](https://github.com/naturalcrit/homebrewery/blob/master/license). Which means you are free to use The Homebrewery is any way that you want, except for claiming that you made it yourself.
If you wish to sell or in some way gain profit for what's created on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used.
### More Resources
If you are looking for more 5e Homebrew resources check out [r/UnearthedArcana](https://www.reddit.com/r/UnearthedArcana/) and their list of useful resources [here](https://www.reddit.com/r/UnearthedArcana/comments/3uwxx9/resources_open_to_the_community/).
<img src='https://i.imgur.com/hMna6G0.png' style='position:absolute;bottom:50px;right:30px;width:280px' />
<div class='pageNumber'>1</div>
<div class='footnote'>PART 1 | FANCINESS</div>
\page
# Appendix
### Not quite Markdown
Although the Homebrewery uses Markdown, to get all the styling features from the PHB, we had to get a little creative. Some base HTML elements are not used as expected and I've had to include a few new keywords.
___
* **Horizontal Rules** are generally used to *modify* existing elements into a different style. For example, a horizontal rule before a blockquote will give it the style of a Monster Stat Block instead of a note.
* **New Pages** are controlled by the author. It's impossible for the site to detect when the end of a page is reached, so indicate you'd like to start a new page, use the new page snippet to get the syntax.
* **Code Blocks** are used only to indicate column breaks. Since they don't allow for styling within them, they weren't that useful to use.
* **HTML** can be used to get *just* the right look for your homebrew. I've included some examples in the snippet icons above the editor.
```
```
### Images
Images must be hosted online somewhere, like imgur. You use the address to that image to reference it in your brew. Images can be included 'inline' with the text using Markdown-style images. However for background images more control is needed.
Background images should be included as HTML-style img tags. Using inline CSS you can precisely position your image where you'd like it to be. I have added both a inflow image snippet and a background image snippet to give you exmaples of how to do it.
### Crediting Me
If you'd like to credit The Homebrewery in your brew, I'd be flattered! Just reference that you made it with The Homebrewery.
<div class='pageNumber'>2</div>
<div class='footnote'>PART 2 | BORING STUFF</div>

View File

@@ -1,76 +1,115 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var request = require("superagent");
require('./newPage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const request = require('superagent');
var Nav = require('naturalcrit/nav/nav.jsx');
var Navbar = require('../../navbar/navbar.jsx');
var EditTitle = require('../../navbar/editTitle.navitem.jsx');
const Markdown = require('naturalcrit/markdown.js');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const AccountNavItem = require('../../navbar/account.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
var SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
var Editor = require('../../editor/editor.jsx');
var BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const KEY = 'homebrewery-new';
const KEY = 'naturalCrit-homebrew-new';
var NewPage = React.createClass({
getInitialState: function() {
const NewPage = createClass({
getInitialState : function() {
return {
title : 'My Awesome Brew v99',
text: '',
isSaving : false
metadata : {
gDrive : false,
title : '',
description : '',
tags : '',
published : false,
authors : [],
systems : []
},
text : '',
isSaving : false,
saveGoogle : (global.account && global.account.googleId ? true : false),
errors : []
};
},
componentDidMount: function() {
var storage = localStorage.getItem(KEY);
componentDidMount : function() {
const storage = localStorage.getItem(KEY);
if(storage){
this.setState({
text : storage
})
});
}
window.onbeforeunload = (e)=>{
if(this.state.text == '') return;
return "Your homebrew isn't saved. Are you sure you want to leave?";
};
document.addEventListener('keydown', this.handleControlKeys);
},
componentWillUnmount : function() {
document.removeEventListener('keydown', this.handleControlKeys);
},
componentWillUnmount: function() {
window.onbeforeunload = function(){};
handleControlKeys : function(e){
if(!(e.ctrlKey || e.metaKey)) return;
const S_KEY = 83;
const P_KEY = 80;
if(e.keyCode == S_KEY) this.save();
if(e.keyCode == P_KEY) this.print();
if(e.keyCode == P_KEY || e.keyCode == S_KEY){
e.stopPropagation();
e.preventDefault();
}
},
handleSplitMove : function(){
this.refs.editor.update();
},
handleTitleChange : function(title){
handleMetadataChange : function(metadata){
this.setState({
title : title
metadata : _.merge({}, this.state.metadata, metadata)
});
},
handleTextChange : function(text){
this.setState({
text : text
text : text,
errors : Markdown.validate(text)
});
localStorage.setItem(KEY, text);
},
handleSave : function(){
save : async function(){
this.setState({
isSaving : true
});
request.post('/homebrew/api')
.send({
title : this.state.title,
text : this.state.text
})
.end((err, res)=>{
console.log('saving new brew');
if(this.state.saveGoogle) {
const res = await request
.post('/api/newGoogle/')
.send(_.merge({}, this.state.metadata, { text: this.state.text }))
.catch((err)=>{
console.log(err.status === 401
? 'Not signed in!'
: 'Error Creating New Google Brew!');
this.setState({ isSaving: false });
return;
});
const brew = res.body;
localStorage.removeItem(KEY);
window.location = `/edit/${brew.googleId}${brew.editId}`;
} else {
request.post('/api')
.send(_.merge({}, this.state.metadata, {
text : this.state.text
}))
.end((err, res)=>{
if(err){
this.setState({
isSaving : false
@@ -78,52 +117,70 @@ var NewPage = React.createClass({
return;
}
window.onbeforeunload = function(){};
var brew = res.body;
const brew = res.body;
localStorage.removeItem(KEY);
window.location = '/homebrew/edit/' + brew.editId;
})
window.location = `/edit/${brew.editId}`;
});
}
},
renderSaveButton : function(){
if(this.state.isSaving){
return <Nav.item icon='fa-spinner fa-spin' className='saveButton'>
save...
</Nav.item>
}else{
return <Nav.item icon='fa-save' className='saveButton' onClick={this.handleSave}>
</Nav.item>;
} else {
return <Nav.item icon='fa-save' className='saveButton' onClick={this.save}>
save
</Nav.item>
</Nav.item>;
}
},
print : function(){
localStorage.setItem('print', this.state.text);
window.open('/print?dialog=true&local=print', '_blank');
},
renderLocalPrintButton : function(){
return <Nav.item color='purple' icon='fa-file-pdf-o' onClick={this.print}>
get PDF
</Nav.item>;
},
renderNavbar : function(){
return <Navbar>
<Nav.section>
<EditTitle title={this.state.title} onChange={this.handleTitleChange} />
<Nav.item className='brewTitle'>{this.state.metadata.title}</Nav.item>
</Nav.section>
<Nav.section>
{this.renderSaveButton()}
<Nav.item newTab={true} href='https://github.com/stolksdorf/naturalcrit/issues' color='red' icon='fa-bug'>
report issue
</Nav.item>
{this.renderLocalPrintButton()}
<IssueNavItem />
<RecentNavItem />
<AccountNavItem />
</Nav.section>
</Navbar>
</Navbar>;
},
render : function(){
return <div className='newPage page'>
{this.renderNavbar()}
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor value={this.state.text} onChange={this.handleTextChange} ref='editor'/>
<BrewRenderer text={this.state.text} />
<Editor
ref='editor'
value={this.state.text}
onChange={this.handleTextChange}
metadata={this.state.metadata}
onMetadataChange={this.handleMetadataChange}
/>
<BrewRenderer text={this.state.text} errors={this.state.errors} />
</SplitPane>
</div>
</div>
</div>;
}
});

View File

@@ -1,10 +1,10 @@
.newPage{
.saveButton{
background-color: @orange;
&:hover{
background-color: @green;
}
}
.newPage{
.saveButton{
background-color: @orange;
&:hover{
background-color: @green;
}
}
}

View File

@@ -0,0 +1,53 @@
require('./printPage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const { Meta } = require('vitreum/headtags');
const Markdown = require('naturalcrit/markdown.js');
const PrintPage = createClass({
getDefaultProps : function() {
return {
query : {},
brew : {
text : '',
}
};
},
getInitialState : function() {
return {
brewText : this.props.brew.text
};
},
componentDidMount : function() {
if(this.props.query.local){
this.setState((prevState, prevProps)=>({
brewText : localStorage.getItem(prevProps.query.local)
}));
}
if(this.props.query.dialog) window.print();
},
renderPages : function(){
return _.map(this.state.brewText.split('\\page'), (page, index)=>{
return <div
className='phb'
id={`p${index + 1}`}
dangerouslySetInnerHTML={{ __html: Markdown.render(page) }}
key={index} />;
});
},
render : function(){
return <div>
<Meta name='robots' content='noindex, nofollow' />
{this.renderPages()}
</div>;
}
});
module.exports = PrintPage;

View File

@@ -0,0 +1,3 @@
.printPage{
}

View File

@@ -1,47 +1,76 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
require('./sharePage.less');
const React = require('react');
const createClass = require('create-react-class');
const { Meta } = require('vitreum/headtags');
var Nav = require('naturalcrit/nav/nav.jsx');
var Navbar = require('../../navbar/navbar.jsx');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const PrintLink = require('../../navbar/print.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const Account = require('../../navbar/account.navitem.jsx');
var PrintLink = require('../../navbar/print.navitem.jsx');
var BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
var SharePage = React.createClass({
getDefaultProps: function() {
const SharePage = createClass({
getDefaultProps : function() {
return {
brew : {
title : '',
text : '',
shareId : null,
title : '',
text : '',
shareId : null,
createdAt : null,
updatedAt : null,
views : 0
views : 0
}
};
},
componentDidMount : function() {
document.addEventListener('keydown', this.handleControlKeys);
},
componentWillUnmount : function() {
document.removeEventListener('keydown', this.handleControlKeys);
},
handleControlKeys : function(e){
if(!(e.ctrlKey || e.metaKey)) return;
const P_KEY = 80;
if(e.keyCode == P_KEY){
window.open(`/print/${this.props.brew.shareId}?dialog=true`, '_blank').focus();
e.stopPropagation();
e.preventDefault();
}
},
processShareId : function() {
return this.props.brew.googleId ?
this.props.brew.googleId + this.props.brew.shareId :
this.props.brew.shareId;
},
render : function(){
return <div className='sharePage page'>
<Meta name='robots' content='noindex, nofollow' />
<Navbar>
<Nav.section>
<Nav.item className='brewTitle'>{this.props.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
<PrintLink shareId={this.props.brew.shareId} />
<Nav.item href={'/homebrew/source/' + this.props.brew.shareId} color='teal' icon='fa-code'>
<PrintLink shareId={this.processShareId()} />
<Nav.item href={`/source/${this.processShareId()}`} color='teal' icon='fa-code'>
source
</Nav.item>
<RecentNavItem brew={this.props.brew} storageKey='view' />
<Account />
</Nav.section>
</Navbar>
<div className='content'>
<BrewRenderer text={this.props.brew.text} />
</div>
</div>
</div>;
}
});

View File

@@ -1,3 +1,5 @@
.sharePage{
.sharePage{
.content{
overflow-y : hidden;
}
}

View File

@@ -0,0 +1,118 @@
require('./brewItem.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const moment = require('moment');
const request = require('superagent');
const googleDriveIcon = require('../../../googleDrive.png');
const BrewItem = createClass({
getDefaultProps : function() {
return {
brew : {
title : '',
description : '',
authors : []
}
};
},
deleteBrew : function(){
if(this.props.brew.authors.length <= 1){
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return;
} else {
if(!confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the document.')) return;
if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return;
}
if(this.props.brew.googleId) {
request.get(`/api/removeGoogle/${this.props.brew.googleId}${this.props.brew.editId}`)
.send()
.end(function(err, res){
location.reload();
});
} else {
request.delete(`/api/${this.props.brew.editId}`)
.send()
.end(function(err, res){
location.reload();
});
}
},
renderDeleteBrewLink : function(){
if(!this.props.brew.editId) return;
return <a onClick={this.deleteBrew}>
<i className='fa fa-trash' />
</a>;
},
renderEditLink : function(){
if(!this.props.brew.editId) return;
let editLink = this.props.brew.editId;
if(this.props.brew.googleId) {
editLink = this.props.brew.googleId + editLink;
}
return <a href={`/edit/${editLink}`} target='_blank' rel='noopener noreferrer'>
<i className='fa fa-pencil' />
</a>;
},
renderShareLink : function(){
if(!this.props.brew.shareId) return;
let shareLink = this.props.brew.shareId;
if(this.props.brew.googleId) {
shareLink = this.props.brew.googleId + shareLink;
}
return <a href={`/share/${shareLink}`} target='_blank' rel='noopener noreferrer'>
<i className='fa fa-share-alt' />
</a>;
},
renderGoogleDriveIcon : function(){
if(!this.props.brew.gDrive) return;
return <span>
<img className='googleDriveIcon' src={googleDriveIcon} alt='googleDriveIcon' />
</span>;
},
render : function(){
const brew = this.props.brew;
return <div className='brewItem'>
<h2>{brew.title}</h2>
<p className='description' >{brew.description}</p>
<hr />
<div className='info'>
<span>
<i className='fa fa-user' /> {brew.authors.join(', ')}
</span>
<span>
<i className='fa fa-eye' /> {brew.views}
</span>
<span>
<i className='fa fa-refresh' /> {moment(brew.updatedAt).fromNow()}
</span>
{this.renderGoogleDriveIcon()}
</div>
<div className='links'>
{this.renderShareLink()}
{this.renderEditLink()}
{this.renderDeleteBrewLink()}
</div>
</div>;
}
});
module.exports = BrewItem;

View File

@@ -0,0 +1,69 @@
.brewItem{
position : relative;
display : inline-block;
vertical-align : top;
box-sizing : border-box;
box-sizing : border-box;
overflow : hidden;
width : 48%;
min-height : 80px;
margin-right : 15px;
margin-bottom : 15px;
padding : 5px 15px 5px 8px;
padding-right : 15px;
border : 1px solid #c9ad6a;
border-radius : 5px;
-webkit-column-break-inside : avoid;
page-break-inside : avoid;
break-inside : avoid;
h4{
margin-bottom : 5px;
font-size : 2.2em;
}
.info{
font-family : ScalySans;
font-size : 1.2em;
&>span{
margin-right : 12px;
}
}
&:hover{
.links{
opacity : 1;
}
}
&:nth-child(2n + 1){
margin-right : 0px;
}
.links{
.animate(opacity);
position : absolute;
top : 0px;
right : 0px;
height : 100%;
width : 2em;
opacity : 0;
background-color : fade(black, 60%);
text-align : center;
a{
.animate(opacity);
display : block;
margin : 8px 0px;
opacity : 0.6;
font-size : 1.3em;
color : white;
&:hover{
opacity : 1;
}
i{
cursor : pointer;
}
}
}
.googleDriveIcon {
height : 20px;
padding : 0px;
margin : -5px;
}
}

View File

@@ -0,0 +1,78 @@
require('./userPage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const Account = require('../../navbar/account.navitem.jsx');
const BrewItem = require('./brewItem/brewItem.jsx');
// const brew = {
// title : 'SUPER Long title woah now',
// authors : []
// };
//const BREWS = _.times(25, ()=>{ return brew;});
const UserPage = createClass({
getDefaultProps : function() {
return {
username : '',
brews : []
};
},
getUsernameWithS : function() {
if(this.props.username.endsWith('s'))
return `${this.props.username}'`;
return `${this.props.username}'s`;
},
renderBrews : function(brews){
if(!brews || !brews.length) return <div className='noBrews'>No Brews.</div>;
const sortedBrews = _.sortBy(brews, (brew)=>{ return brew.title; });
return _.map(sortedBrews, (brew, idx)=>{
return <BrewItem brew={brew} key={idx}/>;
});
},
getSortedBrews : function(){
return _.groupBy(this.props.brews, (brew)=>{
return (brew.published ? 'published' : 'private');
});
},
render : function(){
const brews = this.getSortedBrews();
return <div className='userPage page'>
<Navbar>
<Nav.section>
<RecentNavItem />
<Account />
</Nav.section>
</Navbar>
<div className='content'>
<div className='phb'>
<div>
<h1>{this.getUsernameWithS()} brews</h1>
{this.renderBrews(brews.published)}
</div>
<div>
<h1>{this.getUsernameWithS()} unpublished brews</h1>
{this.renderBrews(brews.private)}
</div>
</div>
</div>
</div>;
}
});
module.exports = UserPage;

View File

@@ -0,0 +1,33 @@
.noColumns(){
column-count : auto;
column-fill : auto;
column-gap : auto;
column-width : auto;
-webkit-column-count : auto;
-moz-column-count : auto;
-webkit-column-width : auto;
-moz-column-width : auto;
-webkit-column-gap : auto;
-moz-column-gap : auto;
}
.userPage{
.content{
overflow-y : scroll;
.phb{
.noColumns();
height : auto;
min-height : 279.4mm;
margin : 20px auto;
&::after{
display : none;
}
.noBrews{
margin : 10px 0px;
font-size : 1.3em;
font-style : italic;
}
}
}
}

View File

Before

Width:  |  Height:  |  Size: 327 B

After

Width:  |  Height:  |  Size: 327 B

View File

Before

Width:  |  Height:  |  Size: 530 B

After

Width:  |  Height:  |  Size: 530 B

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
@import (less) 'shared/naturalcrit/styles/reset.less';
@import (less) 'shared/naturalcrit/phbStyle/phb.fonts.css';
@import (less) 'shared/naturalcrit/phbStyle/phb.assets.less';
@import (less) './client/homebrew/phbStyle/phb.fonts.css';
@import (less) './client/homebrew/phbStyle/phb.assets.less';
@import (less) './client/homebrew/phbStyle/phb.depricated.less';
//Colors
@background : #EEE5CE;
@noteGreen : #e0e5c1;
@@ -10,6 +10,12 @@
@headerText : #58180D;
@monsterStatBackground : #FDF1DC;
@page { margin: 0; }
body {
counter-reset : phb-page-numbers;
}
*{
-webkit-print-color-adjust : exact;
}
.useSansSerif(){
font-family : ScalySans;
em{
@@ -22,20 +28,21 @@
letter-spacing : -0.02em;
}
}
.useColumns(){
.useColumns(@multiplier : 1){
column-count : 2;
column-fill : auto;
column-gap : 1cm;
column-width : 8cm;
column-width : 8cm * @multiplier;
-webkit-column-count : 2;
-moz-column-count : 2;
-webkit-column-width : 8cm;
-moz-column-width : 8cm;
-webkit-column-width : 8cm * @multiplier;
-moz-column-width : 8cm * @multiplier;
-webkit-column-gap : 1cm;
-moz-column-gap : 1cm;
}
.phb{
.useColumns();
counter-increment : phb-page-numbers;
position : relative;
z-index : 15;
box-sizing : border-box;
@@ -58,23 +65,26 @@
padding-bottom : 0.8em;
line-height : 1.3em;
&+p{
margin-top : -0.8em;
text-indent : 1em;
margin-top : -0.8em;
}
}
ul{
margin-bottom : 0.8em;
padding-left : 1.4em;
line-height : 1.3em;
list-style-position : outside;
list-style-type : disc;
padding-left: 1.4em;
}
ol{
margin-bottom : 0.8em;
padding-left : 1.4em;
line-height : 1.3em;
list-style-position : outside;
list-style-type : decimal;
padding-left: 1.4em;
}
//Indents after p or lists
p+p, ul+p, ol+p{
text-indent : 1em;
}
img{
z-index : -1;
@@ -145,6 +155,7 @@
margin-bottom : 1em;
font-size : 10pt;
thead{
display: table-row-group;
font-weight : 800;
th{
vertical-align : bottom;
@@ -169,23 +180,24 @@
// *****************************/
blockquote{
.useSansSerif();
box-sizing : border-box;
margin-bottom : 1em;
padding : 5px 10px;
background-color : @noteGreen;
border-style: solid;
border-width: 11px;
border-image: @noteBorderImage 11;
border-image-outset: 9px 0px;
box-shadow : 1px 4px 14px #888;
box-sizing : border-box;
margin-bottom : 1em;
padding : 5px 10px;
background-color : @noteGreen;
border-style : solid;
border-width : 11px;
border-image : @noteBorderImage 11;
border-image-outset : 9px 0px;
box-shadow : 1px 4px 14px #888;
-webkit-transform : translateZ(0); //Prevents shadows from breaking across columns
p, ul{
font-size : 0.352cm;
line-height : 1.1em;
}
}
//If a note starts a column, give it space at the top to render border
pre+blockquote{
margin-top: 11px;
pre+blockquote, h2+blockquote, h3+blockquote, h4+blockquote, h5+blockquote {
margin-top : 13px;
}
//*****************************
// * MONSTER STAT BLOCK
@@ -209,21 +221,21 @@
font-weight : 400;
border-bottom : 1px solid @headerText;
}
hr+ul{
color : @headerText;
}
ul{
.useSansSerif();
padding-left : 1em;
font-size : 0.352cm;
color : @headerText;
text-indent : -1em;
list-style-type : none;
padding-left : 1em;
font-size : 0.352cm;
}
// Monster Ability table
hr+table{
margin : 0;
column-span : 1;
background-color : transparent;
border-style : none;
border-image : none;
border-style : none;
-webkit-column-span : 1;
tbody{
tr:nth-child(odd), tr:nth-child(even){
@@ -251,29 +263,7 @@
}
//Full Width
hr+hr+blockquote{
.useColumns();
}
//*****************************
// * CLASS TABLE
// *****************************/
hr+table{
margin-top : -5px;
margin-bottom : 50px;
padding-top : 10px;
border-collapse : separate;
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 37px 17px;
border-image-repeat : round;
border-image-slice : 150 200 150 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
}
h5+hr+table{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
.useColumns(0.96);
}
//*****************************
// * FOOTER
@@ -283,6 +273,7 @@
position : absolute;
bottom : 0px;
left : 0px;
z-index : 100;
height : 50px;
width : 100%;
background-image : @footerAccentImage;
@@ -308,11 +299,15 @@
font-size : 0.9em;
color : #c9ad6a;
text-align : center;
&.auto::after {
content : counter(phb-page-numbers);
}
}
.footnote{
position : absolute;
right : 80px;
bottom : 32px;
z-index : 150;
width : 200px;
font-size : 0.8em;
color : #c9ad6a;
@@ -332,25 +327,19 @@
text-indent : -1em;
list-style-type : none;
}
//Double hr for full width elements
hr+hr+blockquote{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
//Column Break
pre{
pre, code{
visibility : hidden;
-webkit-column-break-after : always;
break-after : always;
-moz-column-break-after : always;
}
//Avoid breaking up
p,ul,blockquote,table{
p,blockquote,table{
z-index : 15;
-webkit-column-break-inside : avoid;
column-break-inside : avoid;
overflow: hidden; /* Firefox fix */
page-break-inside : avoid;
break-inside : avoid;
}
//Better spacing for spell blocks
h4+p+hr+ul{
@@ -365,12 +354,118 @@
margin-bottom : 0px;
margin-left : 1.5em;
}
li{
-webkit-column-break-inside : avoid;
page-break-inside : avoid;
break-inside : avoid;
}
}
//*****************************
// * PRINT
// * SPELL LIST
// *****************************/
.phb.print{
blockquote{
box-shadow : none;
.phb .spellList{
.useSansSerif();
column-count : 4;
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
ul+h5{
margin-top : 15px;
}
}
p, ul{
font-size : 0.352cm;
line-height : 1.3em;
}
ul{
margin-bottom : 0.5em;
padding-left : 1em;
text-indent : -1em;
list-style-type : none;
-webkit-column-break-inside : auto;
page-break-inside : auto;
break-inside : auto;
}
}
//*****************************
// * WIDE
// *****************************/
.phb .wide{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
//*****************************
// * CLASS TABLE
// *****************************/
.phb .classTable{
margin-top : 25px;
margin-bottom : 40px;
border-collapse : separate;
background-color : white;
border : initial;
border-style : solid;
border-image-outset : 25px 17px;
border-image-repeat : stretch;
border-image-slice : 150 200 150 200;
border-image-source : @frameBorderImage;
border-image-width : 47px;
h5{
margin-bottom : 10px;
}
}
//************************************
// * DESCRIPTIVE TEXT BOX
// ************************************/
.phb .descriptive{
display : block-inline;
margin-bottom : 1em;
background-color : #faf7ea;
font-family : ScalySans;
border-style : solid;
border-width : 7px;
border-image : @descriptiveBoxImage 12 stretch;
border-image-outset : 4px;
box-shadow : 0px 0px 6px #faf7ea;
p{
display : block;
padding-bottom : 0px;
line-height : 1.5em;
}
p + p {
padding-top : .8em;
}
em {
font-family : ScalySans;
font-style : italic;
}
strong {
font-family : ScalySans;
font-weight : 800;
letter-spacing : -0.02em;
}
}
.phb pre+.descriptive{
margin-top : 8px;
}
//*****************************
// * TABLE OF CONTENTS
// *****************************/
.phb .toc{
-webkit-column-break-inside : avoid;
page-break-inside : avoid;
break-inside : avoid;
a{
color : black;
text-decoration : none;
&:hover{
text-decoration : underline;
}
}
ul{
padding-left : 0;
list-style-type : none;
}
&>ul>li{
margin-bottom : 10px;
}
}

View File

Before

Width:  |  Height:  |  Size: 864 B

After

Width:  |  Height:  |  Size: 864 B

View File

@@ -1,86 +0,0 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var Router = require('pico-router');
var NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
var HomebrewIcon = require('naturalcrit/svg/homebrew.svg.jsx');
var Main = React.createClass({
getDefaultProps: function() {
return {
tools : [
{
id : 'homebrew',
path : '/homebrew',
name : 'The Homebrewery',
icon : <HomebrewIcon />,
desc : 'Make authentic-looking 5e homebrews using Markdown',
show : true,
beta : false
},
{
id : 'spellsort',
path : '/spellsort',
name : 'Spellsort',
icon : <HomebrewIcon />,
desc : 'Sort and search through spells',
show : true,
beta : true
},
{
id : 'homebrewfg2',
path : '/homebrew',
name : 'The Homebrewery',
icon : <HomebrewIcon />,
desc : 'Make authentic-looking 5e homebrews using Markdown',
show : false,
beta : false
}
]
};
},
renderTool : function(tool){
if(!tool.show) return null;
return <a href={tool.path} className={cx('tool', tool.id, {beta : tool.beta})} key={tool.id}>
<div className='content'>
{tool.icon}
<h2>{tool.name}</h2>
<p>{tool.desc}</p>
</div>
</a>;
},
renderTools : function(){
return _.map(this.props.tools, (tool)=>{
return this.renderTool(tool);
});
},
render : function(){
return <div className='main'>
<div className='top'>
<div className='logo'>
<NaturalCritIcon />
<span className='name'>
Natural
<span className='crit'>Crit</span>
</span>
</div>
<p>Top-tier tools for the discerning DM</p>
</div>
<div className='tools'>
{this.renderTools()}
</div>
</div>
}
});
module.exports = Main;

View File

@@ -1,136 +0,0 @@
@import 'naturalcrit/styles/core.less';
.main{
height : 100vh;
background-color : white;
.top{
.fadeInTop(1s);
.delay(0.5);
margin-bottom : 100px;
padding-top : 100px;
text-align : center;
.logo{
font-size : 4em;
color : black;
svg{
height : .9em;
margin-right : .2em;
cursor : pointer;
fill : black;
}
.name{
font-family : 'CodeLight';
.crit{
font-family : 'CodeBold';
}
}
}
p{
margin-top : 10px;
font-size : 1.3em;
font-style : italic;
color : @grey;
}
}
.tools{
width : 100%;
text-align : center;
.tool{
.sequentialDelay(0.5s, 1s);
.fadeInDown(1s);
.keep();
display : inline-block;
cursor : pointer;
opacity : 0;
color : black;
text-align : center;
text-decoration : none;
&+.tool{
border-left : 1px solid #666;
}
.content{
.addSketch(360px);
.animateAll(0.5s);
position : relative;
width : 320px;
padding : 35px;
&:hover{
svg, h2{
.transform(scale(1.3));
}
}
h2{
.animateAll(0.5s);
font-family : 'CodeBold';
font-size : 2em;
}
p{
max-width : 300px;
margin : 20px auto;
line-height : 1.5em;
}
svg{
.animateAll(0.5s);
height : 10em;
}
}
.content:hover{
background-color : fade(@teal, 20%);
}
//Beta styles
&.beta{
cursor : initial;
.content{
&:hover{
svg, h2{
.transform(scale(1.0));
}
}
svg, h2{
opacity : 0.3;
}
&:after{
.animateAll();
content : "beta!";
position : absolute;
display : block;
top : 120px;
left : 0px;
width : 100%;
padding : 10px 0px;
//opacity : 0;
background-color : fade(@grey, 50%);
font-size : 2em;
font-weight : 800;
text-align : center;
text-transform : uppercase;
}
}
}
}
}
}
.addSketch(@length, @color : black){
path, line, polyline, circle, rect, polygon {
.sketch(@length, @color, 4s);
stroke-dasharray : @length;
stroke-dashoffset : 0px;
stroke : @color;
stroke-width : 0.5px;
fill : @color;
//.animateAll(3s);
}
}
.sketch(@length, @color : black, @duration : 3s, @easing : @defaultEasing){
.createAnimation(sketch, @duration, @easing);
.sketchKeyFrames(){
0% { stroke-dashoffset : @length; fill: transparent;}
50% { stroke-dashoffset : @length; fill: transparent;}
80% { stroke-dashoffset : 0px; fill: transparent;}
100% { stroke-dashoffset : 0px; fill:@color;}
}
@-webkit-keyframes sketch {.sketchKeyFrames();}
@-moz-keyframes sketch {.sketchKeyFrames();}
@-ms-keyframes sketch {.sketchKeyFrames();}
@-o-keyframes sketch {.sketchKeyFrames();}
@keyframes sketch {.sketchKeyFrames();}
}

View File

@@ -1,21 +0,0 @@
var React = require('react');
var _ = require('lodash');
var Nav = require('naturalcrit/nav/nav.jsx');
var Navbar = React.createClass({
render : function(){
return <Nav.base>
<Nav.section>
<Nav.logo />
<Nav.item href='/spellsort' className='spellsortLogo'>
<div>Spellsort</div>
</Nav.item>
<Nav.item>v0.0.0</Nav.item>
</Nav.section>
{this.props.children}
</Nav.base>
}
});
module.exports = Navbar;

View File

@@ -1,16 +0,0 @@
.spellsort nav{
.spellsortLogo{
.animate(color);
font-family : CodeBold;
font-size : 12px;
color : white;
div{
margin-top : 2px;
margin-bottom : -2px;
}
&:hover{
color : @teal;
}
}
}

View File

@@ -1,32 +0,0 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var Sorter = React.createClass({
getDefaultProps: function() {
return {
spells : []
};
},
renderSpell : function(spell){
return <div className='spell' key={spell.id}>
{spell.name}
</div>
},
renderSpells : function(){
return _.map(this.props.spells, (spell)=>{
return this.renderSpell(spell)
});
},
render : function(){
return <div className='sorter'>
{this.renderSpells()}
</div>
}
});
module.exports = Sorter;

View File

@@ -1,3 +0,0 @@
.COM{
}

View File

@@ -1,97 +0,0 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var Markdown = require('marked');
var SpellRenderer = React.createClass({
getDefaultProps: function() {
return {
spells : []
};
},
//TODO: Add in ritual tag
getSubtitle : function(spell){
if(spell.level == 0) return <p><em>{spell.school} cantrip</em></p>;
if(spell.level == 1) return <p><em>{spell.level}st-level {spell.school}</em></p>;
if(spell.level == 2) return <p><em>{spell.level}nd-level {spell.school}</em></p>;
if(spell.level == 3) return <p><em>{spell.level}rd-level {spell.school}</em></p>;
return <p><em>{spell.level}th-level {spell.school}</em></p>;
},
getComponents : function(spell){
var result = [];
if(spell.components.v) result.push('V');
if(spell.components.s) result.push('S');
if(spell.components.m) result.push('M ' + spell.components.m);
return result.join(', ');
},
getHigherLevels : function(spell){
if(!spell.scales) return null;
return <p>
<strong><em>At Higher Levels. </em></strong>
<span dangerouslySetInnerHTML={{__html: Markdown(spell.scales)}} />
</p>;
},
getClasses : function(spell){
if(!spell.classes || !spell.classes.length) return null;
var classes = _.map(spell.classes, (cls)=>{
return _.capitalize(cls);
}).join(', ');
return <li>
<strong>Classes:</strong> {classes}
</li>
},
renderSpell : function(spell){
console.log('rendering', spell);
return <div className='spell' key={spell.id}>
<h4>{spell.name}</h4>
{this.getSubtitle(spell)}
<hr />
<ul>
<li>
<strong>Casting Time:</strong> {spell.casting_time}
</li>
<li>
<strong>Range:</strong> {spell.range}
</li>
<li>
<strong>Components:</strong> {this.getComponents(spell)}
</li>
<li>
<strong>Duration:</strong> {spell.duration}
</li>
{this.getClasses(spell)}
</ul>
<span dangerouslySetInnerHTML={{__html: Markdown(spell.description)}} />
{this.getHigherLevels(spell)}
</div>
},
renderSpells : function(){
return _.map(this.props.spells, (spell)=>{
return this.renderSpell(spell);
})
},
render : function(){
return <div className='spellRenderer'>
<div className='phb'>
{this.renderSpells()}
</div>
</div>
}
});
module.exports = SpellRenderer;

View File

@@ -1,40 +0,0 @@
@import (less) 'naturalcrit/phbStyle/phb.style.less';
.pane{
position : relative;
}
.spellRenderer{
overflow-y : scroll;
&>.phb{
margin-right : auto;
margin-bottom : 30px;
margin-left : auto;
box-shadow : 1px 4px 14px #000;
height : auto;
width : 100%;
display: flex;
flex-direction: column;
flex-wrap: wrap;
column-count : initial;
column-fill : initial;
column-gap : initial;
column-width : initial;
-webkit-column-count : initial;
-moz-column-count : initial;
-webkit-column-width : initial;
-moz-column-width : initial;
-webkit-column-gap : initial;
-moz-column-gap : initial;
.spell{
display: inline;
width : 8cm;
}
}
}

View File

@@ -1,45 +0,0 @@
var React = require('react');
var _ = require('lodash');
var cx = require('classnames');
var Nav = require('naturalcrit/nav/nav.jsx');
var Navbar = require('./navbar/navbar.jsx');
var SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
var SpellRenderer = require('./spellRenderer/spellRenderer.jsx');
var Sorter = require('./sorter/sorter.jsx');
var SpellSort = React.createClass({
getDefaultProps: function() {
return {
spells : []
};
},
handleSplitMove : function(){
},
render : function(){
console.log(this.props.spells);
return <div className='spellsort page'>
<Navbar>
<Nav.section>
yo
</Nav.section>
</Navbar>
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Sorter spells={this.props.spells} />
<SpellRenderer spells={this.props.spells} />
</SplitPane>
</div>
</div>
}
});
module.exports = SpellSort;

View File

@@ -1,4 +0,0 @@
@import 'naturalcrit/styles/core.less';
.spellsort{
}

View File

@@ -1,30 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script>global=window</script>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.6.2/css/font-awesome.min.css" rel="stylesheet" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link rel="icon" href="/assets/main/favicon.ico" type="image/x-icon" />
{{=vitreum.css}}
{{=vitreum.globals}}
<title>Natural Crit - D&D Tools</title>
</head>
<body>
<div id="reactContainer">{{=vitreum.component}}</div>
</body>
{{=vitreum.libs}}
{{=vitreum.js}}
{{=vitreum.reactRender}}
{{? vitreum.inProduction}}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','http://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-72212009-1', 'auto');
ga('send', 'pageview');
</script>
{{?}}
</html>

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