0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-23 14:23:21 +00:00

Compare commits

...

495 Commits

Author SHA1 Message Date
Trevor Buckner
0a742e8c2f Merge pull request #1513 from G-Ambatte/fix#1512
Confirm `Brew.Title` exists before ordering by it
2021-08-03 23:49:38 -04:00
G.Ambatte
e14c42761d Check brew.title exists before sorting by it 2021-08-04 12:24:29 +12:00
Trevor Buckner
6f6c4acf7e Merge pull request #1505 from naturalcrit/fixMonsterMac
Fix monster blocks breaking on Mac
2021-08-01 23:09:08 -04:00
Trevor Buckner
30745c2be3 Update 5ePhb.style.less 2021-08-01 23:07:45 -04:00
Trevor Buckner
4d087f4aa9 Fix spaces not working in mustache divs / spans 2021-08-01 00:20:58 -04:00
Trevor Buckner
874c8a9fd1 Merge pull request #1494 from naturalcrit/v2.13.2
up version to 2.13.2
2021-07-31 12:17:32 -04:00
Trevor Buckner
3cb50bc7fc up version to 2.13.2 2021-07-30 23:30:34 -04:00
Trevor Buckner
213ef9d94b Fix cursor getting stuck on divider in Meta panel view 2021-07-30 22:44:00 -04:00
Trevor Buckner
046b6266b3 Merge pull request #1483 from Gazook89/pageNumber-v3
v3 Snippets: Page Numbers
2021-07-30 22:35:58 -04:00
Trevor Buckner
8a03062e3d Fix indent and vertical spacing 2021-07-30 22:32:17 -04:00
Trevor Buckner
2a40f05e90 Revert "Update css for classTables v3"
This reverts commit 3c2feeb2aa.
2021-07-30 20:33:34 -04:00
Trevor Buckner
ce73e9293d Revert "Update to v3 moustache syntax"
This reverts commit fdb294bad9.
2021-07-30 20:33:24 -04:00
Gazook89
f469a7e360 Update page number snippet in snippet.js 2021-07-30 20:28:48 -04:00
Gazook89
3c2feeb2aa Update css for classTables v3
- add "- spell slots per spell level -" header to full class tables
- add rules for `.classTables.full` which is similar to `wide` but adds the previously mentioned header as well.
- add css variable `--row-color` so the odd row colors can be set in the inline moustache syntax easily.
2021-07-30 20:28:48 -04:00
Gazook89
fdb294bad9 Update to v3 moustache syntax
- Added moustache syntax
- replaced class `wide` with `full` so that new styling is applied, including adding a "spell slots per spell level" header for only full class tables.
- included a quick-change css variable to the snippet to control row color.  **can be updated to use hex code or whichever is preferred for the snippet**
2021-07-30 20:28:48 -04:00
Gazook89
56975f9375 Delete userPage3D.css 2021-07-30 20:28:48 -04:00
Gazook89
cb74c0d389 Update userPage3D.css 2021-07-30 20:28:48 -04:00
Gazook89
33abe05737 Create userPage3D.css 2021-07-30 20:28:48 -04:00
Trevor Buckner
61ca7fd0f6 Merge pull request #1457 from G-Ambatte/fixUndefinedCSS
Fix issue with undefined Style tab data
2021-07-30 18:53:16 -04:00
Trevor Buckner
21223cbcd4 Merge branch 'master' into fixUndefinedCSS 2021-07-30 18:05:02 -04:00
Trevor Buckner
d02d51717d Merge pull request #1480 from Gazook89/Gazook89-HB-Meta-Editor-Tweaks
Update metadataEditor.less for CSS adjustments
2021-07-30 17:56:14 -04:00
Trevor Buckner
004f3f184f Merge pull request #1455 from G-Ambatte/reduceWelcomeTextUsage
Make WelcomeText and ChangeLogText conditional
2021-07-30 17:50:26 -04:00
Trevor Buckner
99d2f6d48d avoid sending two copies of welcome text in the same brew 2021-07-30 17:47:11 -04:00
Trevor Buckner
11d1f5c00e Merge pull request #1453 from G-Ambatte/modifyTitleOfClonedBrew
Prepend 'CLONE:- ' to title of cloned brew
2021-07-30 17:30:26 -04:00
Trevor Buckner
ebd28f41a2 replace :- with just - 2021-07-30 17:29:36 -04:00
Trevor Buckner
2397fcaa21 Merge pull request #1485 from naturalcrit/MustacheSyntaxUsesColons
Mustache syntax to use colons
2021-07-30 12:32:28 -04:00
Trevor Buckner
5b039b82a3 Update snippets 2021-07-30 12:31:30 -04:00
Trevor Buckner
264f5d5068 Merge pull request #1475 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.13.4
Bump mongoose from 5.13.3 to 5.13.4
2021-07-30 03:17:54 -04:00
Trevor Buckner
eff5660f12 Merge pull request #1484 from naturalcrit/FixMustacheInjectConflicts
Fix conflicts with Mustache divs and injected style tags
2021-07-30 02:12:07 -04:00
Trevor Buckner
98915e158d Fix editor highlighting 2021-07-30 02:11:26 -04:00
Trevor Buckner
9be71a5159 Mustache syntax now uses : instead of =. Single words don't need quotes 2021-07-30 02:08:08 -04:00
Trevor Buckner
6b61bb05c0 Update markdown.js
- Fix "start" function for Divs and BlockInject, which were being generated mid-line (must start only after a newline)
- Fix Divs consuming part of Spans above them.
- Add % and # as valid characters for inline-styles
2021-07-30 01:27:05 -04:00
Gazook89
c4c5e21ce0 Vertically center button labels
Set labels as inline-flex containers and vertically align children to center.
2021-07-29 12:28:02 -05:00
Gazook89
0c0ba0b6ca Update metadataEditor.less
- Fix Publish button and following small text so there is no overlap
- set `nowrap` on checkbox inputs/labels so that the labels do not wrap to the next line from their input.
- set min-width on values so that text boxes shrink to the same width; and can flex to any larger size.
2021-07-29 11:26:41 -05:00
Gazook89
295a4cd1cd Update magic.gen.js
Add "1st Level" to levels in spell list.  convert spell list to moustache syntax.
2021-07-29 10:13:31 -05:00
G.Ambatte
db3bec9e2b Combine setState calls in componentDidMount 2021-07-29 21:05:55 +12:00
G.Ambatte
577a434e17 Slight change to isMounted logic 2021-07-29 19:24:36 +12:00
G.Ambatte
cac5aa2475 Combine renderStyle and renderPages logic 2021-07-29 19:23:27 +12:00
G.Ambatte
85fa73b9bf Remove welcomText+changelogText from default props 2021-07-29 19:02:48 +12:00
G.Ambatte
fdfea36614 Shift HomePage to use a minimal brew 2021-07-29 19:00:26 +12:00
Trevor Buckner
2f663e0ea7 Merge pull request #1468 from G-Ambatte/fixSharePage-#1467
Fix issue 1467 - Source on Change Log doesn't work
2021-07-29 00:11:20 -04:00
Trevor Buckner
5d05af089b Merge pull request #1476 from naturalcrit/InjectMustacheStyle
Inject mustache style
2021-07-28 23:58:58 -04:00
Trevor Buckner
e237cd8be4 Remove artificial spacing between mustache divs. 2021-07-28 23:55:56 -04:00
Trevor Buckner
8bd09e58cb Smartypants setting to show left and right quote marks. 2021-07-28 23:55:22 -04:00
Trevor Buckner
4e2a3cc5be Update image snippets to be pure markdown 2021-07-28 23:42:51 -04:00
Trevor Buckner
d9c83379fe Add "inject style" syntax, for use with images, headers, etc. 2021-07-28 23:26:12 -04:00
dependabot[bot]
0818a3485a Bump mongoose from 5.13.3 to 5.13.4
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.13.3 to 5.13.4.
- [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.13.3...5.13.4)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-29 03:00:58 +00:00
G.Ambatte
7fa1e16b5a Create minimal Changelog brew 2021-07-28 17:51:35 +12:00
G.Ambatte
acb750c18a Fix logic for enabling Source and Get PDF 2021-07-28 17:15:38 +12:00
G.Ambatte
72d8b5ea16 Linter fixes; correction of condtional JSX element 2021-07-28 17:10:53 +12:00
Sean Robertson
6238ed6b77 Further simplification of code. 2021-07-28 16:04:28 +12:00
Sean Robertson
fa5bd92406 Shift check to presence of this.props.brew.shareId. Revert changes to Homebrew.jsx. 2021-07-28 15:37:36 +12:00
Sean Robertson
189fdb4555 Missed a vital comma. 2021-07-28 15:29:04 +12:00
Sean Robertson
caf151a0dd Initial code pass to not display "Source" and "Get PDF" NavBar buttons on the /changelog page. 2021-07-28 15:25:17 +12:00
Trevor Buckner
d35769dceb Merge pull request #1459 from G-Ambatte/sortUserBrews
Add options for ordering brews on User page
2021-07-27 18:12:32 -04:00
G.Ambatte
1031e8a55a Changed from Radios to Buttons 2021-07-27 23:14:05 +12:00
Trevor Buckner
a71dca1487 Merge pull request #1456 from G-Ambatte/cleanTemplate
Clean up template.js
2021-07-26 17:47:24 -04:00
G.Ambatte
b80a249cf7 Reduce code duplication in sorting radio options 2021-07-26 22:40:01 +12:00
G.Ambatte
54d0e2c483 Add Lodash deburr to sorting by title 2021-07-26 22:08:54 +12:00
G.Ambatte
c91e5784ac Change to Unicode characters for asc/desc arrows 2021-07-26 21:38:22 +12:00
G.Ambatte
48e80803f7 Ignore case when ordering by title ( a > B ) 2021-07-26 21:33:20 +12:00
G.Ambatte
495a68893d Initial pass at sorting User brews 2021-07-26 20:52:23 +12:00
G.Ambatte
41e1ed7bd1 Fix issue with undefined Style tab data 2021-07-26 10:29:31 +12:00
G.Ambatte
7eb63db502 Clean up link validation in template 2021-07-26 10:05:43 +12:00
G.Ambatte
c6d0a2e2ad Make WelcomeText and ChangeLogText conditional 2021-07-26 09:55:04 +12:00
Trevor Buckner
1a2da712ed Merge pull request #1452 from G-Ambatte/fixIssue1438
Fix for Issue #1438 - style data not cloning to `/new`
2021-07-25 17:05:36 -04:00
Trevor Buckner
36627bc188 code block style 2021-07-25 16:48:59 -04:00
G.Ambatte
f31fe6cbf0 Prepend 'CLONE:- ' to title of cloned brew 2021-07-25 16:54:24 +12:00
G.Ambatte
9f8a857cef Fix for Issue #1438 - style data not cloning to /new 2021-07-25 16:39:29 +12:00
Trevor Buckner
fbf053ac2b Merge pull request #1450 from naturalcrit/dependabot/npm_and_yarn/codemirror-5.62.2
Bump codemirror from 5.62.1 to 5.62.2
2021-07-22 00:40:56 -04:00
dependabot[bot]
c77338c65e Bump codemirror from 5.62.1 to 5.62.2
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.62.1 to 5.62.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.62.1...5.62.2)

---
updated-dependencies:
- dependency-name: codemirror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-22 03:00:58 +00:00
Trevor Buckner
42b0ea173d Make toc two-columns 2021-07-21 16:05:29 -04:00
Trevor Buckner
7c9defb85c Put theme in user page. 2021-07-21 15:09:37 -04:00
Trevor Buckner
6e5d183bf6 Merge pull request #1448 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.14.8
Bump @babel/preset-env from 7.14.7 to 7.14.8
2021-07-21 00:02:42 -04:00
Trevor Buckner
0ab00c24c5 Merge pull request #1447 from naturalcrit/dependabot/npm_and_yarn/codemirror-5.62.1
Bump codemirror from 5.62.0 to 5.62.1
2021-07-20 23:54:20 -04:00
dependabot[bot]
c23763a2cf Bump @babel/preset-env from 7.14.7 to 7.14.8
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.7 to 7.14.8.
- [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.14.8/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-21 03:45:53 +00:00
Trevor Buckner
84b2d86054 Merge pull request #1446 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.14.8
Bump @babel/core from 7.14.6 to 7.14.8
2021-07-20 23:44:27 -04:00
Trevor Buckner
ba766254f8 typo 2021-07-20 23:39:34 -04:00
Trevor Buckner
a02e36e13f Merge pull request #1449 from naturalcrit/DefinitionListsToMarkedExtension
Definition lists to marked extension
2021-07-20 23:34:19 -04:00
Trevor Buckner
8f34e8bb2d Change note block to div, restyle notes and descript boxes 2021-07-20 23:32:49 -04:00
dependabot[bot]
38cca54b7f Bump codemirror from 5.62.0 to 5.62.1
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.62.0 to 5.62.1.
- [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.62.0...5.62.1)

---
updated-dependencies:
- dependency-name: codemirror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-21 03:01:03 +00:00
dependabot[bot]
7b44b5b7db Bump @babel/core from 7.14.6 to 7.14.8
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.6 to 7.14.8.
- [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.14.8/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-21 03:00:52 +00:00
Trevor Buckner
3ed4ceb7a3 Fix description box snippet 2021-07-20 19:50:23 -04:00
Trevor Buckner
76e4023b37 Add theme CSS file to print page 2021-07-20 15:44:42 -04:00
Trevor Buckner
7ff6d9e825 Fix TOC generation for level 3 entries not inside level 2 2021-07-20 15:40:32 -04:00
Trevor Buckner
64d133f8f6 Definition List to Markdown Extension. New syntax. 2021-07-19 20:04:25 -04:00
Trevor Buckner
324d0e265e Merge pull request #1443 from naturalcrit/dependabot/npm_and_yarn/eslint-7.31.0
Bump eslint from 7.30.0 to 7.31.0
2021-07-18 23:33:06 -04:00
Trevor Buckner
cec4addcad Merge pull request #1442 from naturalcrit/dependabot/npm_and_yarn/googleapis-82.0.0
Bump googleapis from 81.0.0 to 82.0.0
2021-07-18 23:32:54 -04:00
Trevor Buckner
43605df266 Merge pull request #1441 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.13.3
Bump mongoose from 5.13.2 to 5.13.3
2021-07-18 23:32:44 -04:00
dependabot[bot]
4f03df097c Bump eslint from 7.30.0 to 7.31.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.30.0 to 7.31.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.30.0...v7.31.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-19 03:01:34 +00:00
dependabot[bot]
72dc62e5dd Bump googleapis from 81.0.0 to 82.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 81.0.0 to 82.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/googleapis-v81.0.0...googleapis-v82.0.0)

---
updated-dependencies:
- dependency-name: googleapis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-19 03:01:17 +00:00
dependabot[bot]
3520c03797 Bump mongoose from 5.13.2 to 5.13.3
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.13.2 to 5.13.3.
- [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.13.2...5.13.3)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-19 03:01:07 +00:00
Trevor Buckner
fcbbe46861 Merge pull request #1432 from naturalcrit/dependabot/npm_and_yarn/googleapis-81.0.0
Bump googleapis from 79.0.0 to 81.0.0
2021-07-13 22:46:41 -04:00
Trevor Buckner
4a398143e3 Merge pull request #1428 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.13.2
Bump mongoose from 5.13.0 to 5.13.2
2021-07-13 22:45:46 -04:00
Trevor Buckner
bbaaf74302 Merge pull request #1434 from naturalcrit/SplitThemeCSSOut
Themes are now compiled into separate css files
2021-07-13 00:35:05 -04:00
Trevor Buckner
d3dd3c3d5d Merge pull request #1427 from naturalcrit/dependabot/npm_and_yarn/eslint-7.30.0
Bump eslint from 7.29.0 to 7.30.0
2021-07-12 21:49:05 -04:00
Trevor Buckner
4f2ddfa020 Themes are now compiled into separate css files and can be hot-swapped in the renderer as needed 2021-07-12 19:37:10 -04:00
Trevor Buckner
428ec8412f Merge pull request #1433 from naturalcrit/moreV3Snippets
More v3 snippets
2021-07-11 16:59:13 -04:00
Trevor Buckner
50991dfe92 MustacheDivs to Marked.js extension 2021-07-11 00:33:47 -04:00
Trevor Buckner
63ba9f4fb9 Change {{ span to Marked.js extension 2021-07-10 19:01:27 -04:00
Trevor Buckner
efd0fd1f4a Table of Contents CSS and snippet 2021-07-10 19:01:12 -04:00
dependabot[bot]
5a7767cf0e Bump googleapis from 79.0.0 to 81.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 79.0.0 to 81.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/googleapis-v79.0.0...googleapis-v81.0.0)

---
updated-dependencies:
- dependency-name: googleapis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-09 03:00:35 +00:00
Trevor Buckner
3948e17da2 Merge pull request #1424 from G-Ambatte/fixRecentNavItem
Fix crash caused by Recent Brews NavItem
2021-07-07 12:32:01 -04:00
dependabot[bot]
4e1e6bd69a Bump mongoose from 5.13.0 to 5.13.2
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.13.0 to 5.13.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.13.0...5.13.2)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-05 03:00:51 +00:00
dependabot[bot]
9333bc73ea Bump eslint from 7.29.0 to 7.30.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.29.0 to 7.30.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.29.0...v7.30.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-05 03:00:42 +00:00
G.Ambatte
3540a35a6c Fix for white screen issue when transferring Brews between Storage options. 2021-07-04 20:54:31 +12:00
Trevor Buckner
ee67ba729a V & H spacing, wide block, image snippets 2021-07-01 23:43:01 -04:00
Trevor Buckner
8414961b15 Merge pull request #1419 from naturalcrit/dependabot/npm_and_yarn/googleapis-79.0.0
Bump googleapis from 78.0.0 to 79.0.0
2021-06-30 00:59:08 -04:00
Trevor Buckner
f8de983e2b Merge pull request #1418 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.13.0
Bump mongoose from 5.12.15 to 5.13.0
2021-06-30 00:58:54 -04:00
dependabot[bot]
d40afa619b Bump googleapis from 78.0.0 to 79.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 78.0.0 to 79.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/googleapis-v78.0.0...googleapis-v79.0.0)

---
updated-dependencies:
- dependency-name: googleapis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-30 03:00:35 +00:00
dependabot[bot]
55e1d0fb6e Bump mongoose from 5.12.15 to 5.13.0
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.12.15 to 5.13.0.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/commits/5.13.0)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-29 03:00:41 +00:00
Trevor Buckner
2661e2cfa0 Update all naturalcrit links to https 2021-06-28 20:58:00 -04:00
Trevor Buckner
d4cb5c73aa Merge pull request #1417 from naturalcrit/2.13.1
up version to v2.13.1
2021-06-28 12:11:08 -04:00
Trevor Buckner
9a2d7d1a19 Merge pull request #1416 from G-Ambatte/fixGoogleBrews_v2_13
Attempt to fix issue with saving Google brews.
2021-06-28 12:10:43 -04:00
Trevor Buckner
017bccc937 replace trailing slash 2021-06-28 12:07:56 -04:00
Trevor Buckner
dea683da7c up version to v2.13.1 2021-06-28 11:59:59 -04:00
Trevor Buckner
496ab26972 Use let instead of const 2021-06-28 11:50:12 -04:00
G.Ambatte
c18eb948b4 Accidentally was overzealous with replacing brew with saveBrew. 2021-06-28 19:43:15 +12:00
G.Ambatte
0cd4b730d7 Attempt to fix issue with saving Google brews. 2021-06-28 19:37:06 +12:00
Trevor Buckner
63ea5a3e5f up version to 2.13.0 2021-06-26 12:16:27 -04:00
Trevor Buckner
33f5e8838b Revert react-frame-component to 4.1.3
5.0.0 has issues with the iFrame not mounting if you refresh the page.
2021-06-25 23:59:53 -04:00
Trevor Buckner
3660f3827f Merge pull request #1408 from G-Ambatte/addShareDropDown
Add dropdown to `/share` for `source` to unify options:
2021-06-25 23:30:25 -04:00
Trevor Buckner
ac4cce1f9b update more dependencies 2021-06-25 23:04:27 -04:00
Trevor Buckner
532d2428b7 dependency updates 2021-06-25 22:45:37 -04:00
G.Ambatte
205ed8e30e Padding fix for dropdown items. 2021-06-25 20:50:40 +12:00
G.Ambatte
4119626cb7 Merge branch 'addShareDropDown' of https://github.com/G-Ambatte/homebrewery into addShareDropDown 2021-06-25 20:46:13 +12:00
G.Ambatte
94fdca084a Horizontal and vertical alignment adjustments. 2021-06-25 20:45:47 +12:00
Trevor Buckner
599c69c9bb Merge pull request #1410 from G-Ambatte/fixStyleInPrintFromNew
Style data not applied at Print from New Page
2021-06-25 00:55:20 -04:00
Trevor Buckner
7843691c4b add "page" class to print page
With the style panel we added a `page` css class alongside the `phb` and `phb3` classes so users can write CSS that targets all pages no matter the base CSS loaded. This wasn't applied to the print page.

Funnily enough, the rest of the site uses `.page` just to display the website, and I didn't realize there was a conflict until now because otherwise, the brew is usually hidden in an iFrame.
2021-06-25 00:53:25 -04:00
Trevor Buckner
d9effacb20 Merge branch 'master' into pr/1410 2021-06-25 00:37:53 -04:00
Trevor Buckner
3efb0bf189 Merge branch 'master' into pr/1408 2021-06-25 00:18:27 -04:00
Trevor Buckner
00eb927538 Merge pull request #1401 from G-Ambatte/addStyleToLocalStorage
Add local storage key for Brew Style data
2021-06-25 00:17:11 -04:00
Trevor Buckner
0616ce62eb Rearrange so loading from one doesn't override the other 2021-06-25 00:15:49 -04:00
Trevor Buckner
a171de32d8 Merge branch 'master' into pr/1401 2021-06-25 00:03:49 -04:00
Trevor Buckner
cf7680bc86 Merge pull request #1397 from G-Ambatte/splitCSSatNewSave
Split codefenced CSS from in New Brews from Content to Style tab
2021-06-24 23:35:26 -04:00
Trevor Buckner
e07bb1b3c2 Don't save style tab if user never put anything in it 2021-06-24 23:29:01 -04:00
G.Ambatte
1f830b96b5 Inital pass at getting brew.style to apply when accessing /print from /new without saving. 2021-06-24 21:05:32 +12:00
G.Ambatte
ff7585b69d Clean up unnecessary code. 2021-06-23 21:16:33 +12:00
G.Ambatte
715ee88f38 Add dropdown to /share for source to unify options:
* "view" - View Source
* "download" - Download Source
* "clone to new" - Create New Brew from this Brew
2021-06-23 18:20:02 +12:00
G.Ambatte
142c9ad3b7 Fix loading Style data from local storage, and add fix for Linter. 2021-06-21 20:14:58 +12:00
G.Ambatte
e2280197b9 Unwinding hanging new line accidentally left after removing other code changes. 2021-06-21 19:49:35 +12:00
G.Ambatte
a74916d593 Removed CSS splitting from homebrew.api.js and back to newPage.jsx. 2021-06-21 19:46:54 +12:00
G.Ambatte
ad0e4a2099 Lint fixes. 2021-06-21 19:39:30 +12:00
G.Ambatte
2613d43f3c Shift CSS processing from newPage.jsx/save to homebrew.api.js/newBrew, as this function is only called when saving new Brews. 2021-06-21 19:38:02 +12:00
Sean Robertson
09c7f45c69 Clear data from new key when appropriate 2021-06-21 16:27:18 +12:00
Sean Robertson
0eaeb748f4 Add local storage key to store Brew Style data
Add local storage key for storing Brew Style data.
2021-06-21 16:15:37 +12:00
Sean Robertson
b72191ae68 Increase Linter max lines for newPage.jsx
Increase the maximum number of lines permitted by the Linter for the newPage.jsx file to 300 (was 200).
2021-06-21 13:04:29 +12:00
Sean Robertson
cf4bfc35ea Add new line before appending style data
Add a new line between the existing style data and the appended codefenced style data.
2021-06-21 13:00:12 +12:00
Sean Robertson
69231ba57a Initial pass at appending codefenced CSS to Style data at save time, only on /new brews (so only once per brew). 2021-06-21 12:34:51 +12:00
Trevor Buckner
d61fda9cff Fix regression with saving. 2021-06-20 16:05:36 -04:00
Trevor Buckner
6ecf546baf Fix github link in Edit Page error popup. 2021-06-20 15:04:23 -04:00
Trevor Buckner
ea8aa84009 Merge pull request #1392 from naturalcrit/dependabot/npm_and_yarn/marked-2.1.1
Bump marked from 2.0.6 to 2.1.1
2021-06-20 14:34:51 -04:00
Trevor Buckner
353f1ca42c Merge pull request #1370 from naturalcrit/dependabot/npm_and_yarn/eslint-plugin-react-7.24.0
Bump eslint-plugin-react from 7.23.2 to 7.24.0
2021-06-20 14:34:29 -04:00
Trevor Buckner
20053ad548 Merge pull request #1393 from G-Ambatte/updateSourcePage
Incorporate Brew.Style into Source + Download pages
2021-06-20 14:33:29 -04:00
Trevor Buckner
9b97e0dd87 redundant variable 2021-06-20 14:32:23 -04:00
Trevor Buckner
8e304fa483 tab 2021-06-20 14:30:00 -04:00
Trevor Buckner
0f5e2e5a60 Merge pull request #1353 from G-Ambatte/fixRedditLink
Fix 'Share to Reddit' link for Google brews
2021-06-20 14:21:28 -04:00
Trevor Buckner
f5bd7db388 Merge branch 'master' into pr/1353 2021-06-20 14:19:51 -04:00
Trevor Buckner
70832be810 Fix brews failing to sanitize on user page 2021-06-20 13:53:26 -04:00
G.Ambatte
68ed6019f6 Add new access type (raw) and simplify adding other types in the future. Add functionality for raw access to skip splitting Style data from Brew Content. 2021-06-19 15:50:48 +12:00
Sean Robertson
4638c3e1d9 Update server.js
Initial commit of incorporating `brew.style` into /source and /download pages
2021-06-18 11:24:20 +12:00
dependabot[bot]
53cb9a35ee Bump marked from 2.0.6 to 2.1.1
Bumps [marked](https://github.com/markedjs/marked) from 2.0.6 to 2.1.1.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/release.config.js)
- [Commits](https://github.com/markedjs/marked/compare/v2.0.6...v2.1.1)

---
updated-dependencies:
- dependency-name: marked
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-17 03:00:59 +00:00
Trevor Buckner
9d80f21ae7 Fix empty Google Brews crashing user page 2021-06-11 13:46:03 -04:00
Trevor Buckner
4d5653854a fix version number 2021-06-10 15:48:29 -04:00
Trevor Buckner
70cc8577e8 Fixing Heroku to work with NPM 7 2021-06-10 15:31:26 -04:00
Trevor Buckner
f80d5e6b52 Fix sanitizing brews in user page, hide own G brews on other profiles 2021-06-10 14:22:12 -04:00
Trevor Buckner
19456e8be0 Merge pull request #1384 from naturalcrit/StylePanel
Style panel
2021-06-10 10:41:19 -04:00
Trevor Buckner
c98cedc20f Merge branch 'master' into StylePanel 2021-06-10 10:39:39 -04:00
Trevor Buckner
2b1063c34d v2.12.0 bump version number 2021-06-10 10:38:03 -04:00
Trevor Buckner
fc8be9c8fb Babel to support ES6 Javascript in older browsers 2021-06-10 00:09:29 -04:00
G.Ambatte
70bdb07c1e Update client/homebrew/editor/metadataEditor/metadataEditor.jsx
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>
2021-06-09 21:42:12 +12:00
Trevor Buckner
51aba937f5 Fix CSS highlighting 2021-06-07 11:51:02 -04:00
Trevor Buckner
9363a15daa Merge pull request #1376 from naturalcrit/StylePanel
Separate "style" and "metadata" panels
2021-06-05 16:09:37 -04:00
Trevor Buckner
1ef5bfed94 Remove default text 2021-06-05 16:09:23 -04:00
Trevor Buckner
e67fadef02 Separate "style" and "metadata" panels 2021-06-05 15:58:31 -04:00
Trevor Buckner
99825d10c4 Merge pull request #1373 from earlng/contribution
Update Dockerfile
2021-06-03 13:31:49 -04:00
Trevor Buckner
a7b52f9a96 Merge pull request #1375 from naturalcrit/dependabot/npm_and_yarn/googleapis-75.0.0
Bump googleapis from 74.2.0 to 75.0.0
2021-06-03 13:30:32 -04:00
dependabot[bot]
ef9d4d8525 Bump googleapis from 74.2.0 to 75.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 74.2.0 to 75.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/googleapis-v74.2.0...googleapis-v75.0.0)

---
updated-dependencies:
- dependency-name: googleapis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-03 05:03:03 +00:00
dependabot[bot]
2f751285ed Bump eslint-plugin-react from 7.23.2 to 7.24.0
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.23.2 to 7.24.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.23.2...v7.24.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-02 03:49:23 +00:00
Trevor Buckner
4504a25272 Merge pull request #1368 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.12.12
Bump mongoose from 5.12.11 to 5.12.12
2021-06-01 23:47:44 -04:00
Trevor Buckner
aefc4698ab Merge pull request #1369 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.14.4
Bump @babel/preset-env from 7.14.2 to 7.14.4
2021-06-01 23:47:36 -04:00
Trevor Buckner
28af7353ea Merge pull request #1372 from naturalcrit/dependabot/npm_and_yarn/pico-check-2.1.3
Bump pico-check from 2.0.3 to 2.1.3
2021-06-01 23:47:17 -04:00
dependabot[bot]
22a078b628 Bump pico-check from 2.0.3 to 2.1.3
Bumps [pico-check](https://github.com/stolksdorf/pico-check) from 2.0.3 to 2.1.3.
- [Release notes](https://github.com/stolksdorf/pico-check/releases)
- [Commits](https://github.com/stolksdorf/pico-check/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-01 05:06:37 +00:00
Earl Ng
d8a8275723 Update Dockerfile
Updated the version of node.js that needs to be pulled to match the package.json file.
2021-06-01 12:02:01 +08:00
dependabot[bot]
d13b478c56 Bump @babel/preset-env from 7.14.2 to 7.14.4
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.2 to 7.14.4.
- [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.14.4/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-31 05:05:16 +00:00
dependabot[bot]
5ee146b6be Bump mongoose from 5.12.11 to 5.12.12
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.12.11 to 5.12.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.12.11...5.12.12)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-31 05:04:25 +00:00
Trevor Buckner
d666bacf1f Merge pull request #1361 from naturalcrit/dependabot/npm_and_yarn/browserslist-4.16.6
Bump browserslist from 4.16.3 to 4.16.6
2021-05-28 13:14:50 -04:00
Trevor Buckner
81662bf86b Merge pull request #1349 from naturalcrit/dependabot/npm_and_yarn/nanoid-3.1.23
Bump nanoid from 3.1.22 to 3.1.23
2021-05-28 13:14:41 -04:00
Trevor Buckner
99901ed0ea Merge pull request #1351 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.14.2
Bump @babel/preset-env from 7.14.0 to 7.14.2
2021-05-28 13:14:33 -04:00
dependabot[bot]
18a96890ee Bump nanoid from 3.1.22 to 3.1.23
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.22 to 3.1.23.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.22...3.1.23)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-28 16:58:27 +00:00
dependabot[bot]
3a4c72f1b8 Bump @babel/preset-env from 7.14.0 to 7.14.2
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.0 to 7.14.2.
- [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.14.2/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-28 16:57:52 +00:00
dependabot[bot]
19866010df Bump browserslist from 4.16.3 to 4.16.6
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.16.3 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.16.3...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-28 16:57:25 +00:00
Trevor Buckner
e3e00bbd7c Merge pull request #1367 from naturalcrit/dependabot/npm_and_yarn/marked-2.0.6
Bump marked from 2.0.3 to 2.0.6
2021-05-28 12:56:34 -04:00
Trevor Buckner
c4e3bfee6c Merge pull request #1362 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.12.11
Bump mongoose from 5.12.7 to 5.12.11
2021-05-28 12:56:20 -04:00
Trevor Buckner
d1c9f6f5dd Merge pull request #1365 from naturalcrit/dependabot/npm_and_yarn/googleapis-74.2.0
Bump googleapis from 73.0.0 to 74.2.0
2021-05-28 12:56:04 -04:00
Trevor Buckner
58ccec1b46 Merge pull request #1360 from naturalcrit/dependabot/npm_and_yarn/eslint-7.27.0
Bump eslint from 7.26.0 to 7.27.0
2021-05-28 12:55:42 -04:00
Trevor Buckner
8faa45b19f Merge pull request #1358 from naturalcrit/dependabot/npm_and_yarn/codemirror-5.61.1
Bump codemirror from 5.61.0 to 5.61.1
2021-05-28 12:55:29 -04:00
Trevor Buckner
b2595e55cc Merge pull request #1355 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.14.3
Bump @babel/core from 7.14.0 to 7.14.3
2021-05-28 12:55:16 -04:00
dependabot[bot]
f309df5971 Bump marked from 2.0.3 to 2.0.6
Bumps [marked](https://github.com/markedjs/marked) from 2.0.3 to 2.0.6.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/release.config.js)
- [Commits](https://github.com/markedjs/marked/compare/v2.0.3...v2.0.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-28 05:03:18 +00:00
dependabot[bot]
7cdd90973b Bump googleapis from 73.0.0 to 74.2.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 73.0.0 to 74.2.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/googleapis-v73.0.0...googleapis-v74.2.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-27 05:03:22 +00:00
dependabot[bot]
ccdbffb376 Bump mongoose from 5.12.7 to 5.12.11
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.12.7 to 5.12.11.
- [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.12.7...5.12.11)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-25 05:02:47 +00:00
dependabot[bot]
2eeb2a4454 Bump eslint from 7.26.0 to 7.27.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.26.0 to 7.27.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.26.0...v7.27.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-24 05:09:01 +00:00
dependabot[bot]
1f894094c7 Bump codemirror from 5.61.0 to 5.61.1
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.61.0 to 5.61.1.
- [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.61.0...5.61.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-21 05:05:42 +00:00
dependabot[bot]
5f06de03a9 Bump @babel/core from 7.14.0 to 7.14.3
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.0 to 7.14.3.
- [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.14.3/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-18 05:02:48 +00:00
G.Ambatte
23e773ce64 Update client/homebrew/editor/metadataEditor/metadataEditor.jsx
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>
2021-05-18 16:37:13 +12:00
G.Ambatte
3b34fe72b9 Lint fix. 2021-05-17 19:23:35 +12:00
Sean Robertson
34f620c59b Fix 'Share to Reddit' link for Google brews
Fix 'Share to Reddit' generating incorrect links for brews stored on Google Drive
2021-05-17 12:16:22 +12:00
Trevor Buckner
a5a5127088 Merge pull request #1345 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.14.1
Bump @babel/preset-env from 7.14.0 to 7.14.1
2021-05-13 01:07:20 -04:00
Trevor Buckner
b939d936e9 Merge pull request #1347 from naturalcrit/dependabot/npm_and_yarn/eslint-7.26.0
Bump eslint from 7.25.0 to 7.26.0
2021-05-13 01:07:07 -04:00
dependabot[bot]
1b5e27a9b4 Bump eslint from 7.25.0 to 7.26.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.25.0 to 7.26.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.25.0...v7.26.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-10 05:10:13 +00:00
dependabot[bot]
789c18307a Bump @babel/preset-env from 7.14.0 to 7.14.1
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.0 to 7.14.1.
- [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.14.1/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-04 05:05:44 +00:00
Trevor Buckner
1bc0964aff Merge pull request #1341 from naturalcrit/v2.11.2
Up version to 2.11.2
2021-05-02 22:11:04 -04:00
Trevor Buckner
ce663155c4 Merge pull request #1340 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.14.0
Bump @babel/preset-env from 7.13.15 to 7.14.0
2021-05-02 22:08:48 -04:00
Trevor Buckner
1ad46c1ba9 Update changelog, update v3 snippets for tables 2021-05-02 22:08:25 -04:00
dependabot[bot]
9901c8c3f5 Bump @babel/preset-env from 7.13.15 to 7.14.0
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.15 to 7.14.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.14.0/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-03 00:50:00 +00:00
Trevor Buckner
b20b981a01 Merge pull request #1338 from G-Ambatte/includeTitleInNew
Update to include brew title, description, systems, etc. on the new page
2021-05-02 20:12:33 -04:00
Trevor Buckner
ff860df5c3 Merge pull request #1332 from naturalcrit/dependabot/npm_and_yarn/googleapis-73.0.0
Bump googleapis from 72.0.0 to 73.0.0
2021-05-02 20:10:55 -04:00
Trevor Buckner
69072f8e50 Merge pull request #1336 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.12.7
Bump mongoose from 5.12.5 to 5.12.7
2021-05-02 20:09:40 -04:00
Trevor Buckner
53bf47f7cb Merge pull request #1337 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.14.0
Bump @babel/core from 7.13.16 to 7.14.0
2021-05-02 20:09:07 -04:00
Trevor Buckner
61032710e8 Merge pull request #1333 from naturalcrit/dependabot/add-v2-config-file
Upgrade to GitHub-native Dependabot
2021-05-02 20:08:39 -04:00
Trevor Buckner
00527e7cf3 Merge pull request #1329 from naturalcrit/v3Tables
V3 Tables & Headers
2021-05-02 19:29:54 -04:00
Trevor Buckner
0423a43650 Tweak h1 2021-05-02 19:24:37 -04:00
G.Ambatte
2ba10655a8 Update to include brew title, description, systems, etc. on the new page 2021-05-01 14:38:54 +12:00
dependabot-preview[bot]
c5989ea95d Bump @babel/core from 7.13.16 to 7.14.0
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.16 to 7.14.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.14.0/packages/babel-core)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-30 06:56:09 +00:00
dependabot-preview[bot]
3f6c7a9c25 Bump mongoose from 5.12.5 to 5.12.7
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.12.5 to 5.12.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.12.5...5.12.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-30 06:55:27 +00:00
Trevor Buckner
a95e3552ff All headers updated 2021-04-29 22:18:04 -04:00
dependabot-preview[bot]
ef707a9b30 Upgrade to GitHub-native Dependabot 2021-04-29 15:31:20 +00:00
dependabot-preview[bot]
be51ab52fb Bump googleapis from 72.0.0 to 73.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 72.0.0 to 73.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/googleapis-v72.0.0...googleapis-v73.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-29 06:52:43 +00:00
Trevor Buckner
e0a25ea918 Merge pull request #1317 from naturalcrit/dependabot/npm_and_yarn/eslint-plugin-react-7.23.2
Bump eslint-plugin-react from 7.23.1 to 7.23.2
2021-04-27 15:36:51 -04:00
dependabot-preview[bot]
72ae258fa5 Bump eslint-plugin-react from 7.23.1 to 7.23.2
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.23.1 to 7.23.2.
- [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.23.1...v7.23.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-27 19:33:43 +00:00
Trevor Buckner
33d124e3f3 Merge pull request #1323 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.13.16
Bump @babel/core from 7.13.14 to 7.13.16
2021-04-27 15:24:47 -04:00
dependabot-preview[bot]
bc87f61bdc Bump @babel/core from 7.13.14 to 7.13.16
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.14 to 7.13.16.
- [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.13.16/packages/babel-core)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-27 19:22:13 +00:00
Trevor Buckner
fe03cca72b Update Booksanity Bold Italic font
The "Remake" version of BoldItalic font by /u/Barkalot was only Semibold, so updated the font to be thicker by simply using a slanted version (12 deg) of the Bold font.
2021-04-26 12:11:48 -04:00
Trevor Buckner
2007113ed8 Change spacing and padding for table cells 2021-04-26 12:10:04 -04:00
Trevor Buckner
f89b08a577 Merge pull request #1328 from naturalcrit/dependabot/npm_and_yarn/eslint-7.25.0
Bump eslint from 7.23.0 to 7.25.0
2021-04-26 09:31:54 -04:00
Trevor Buckner
288705950c Merge pull request #1325 from naturalcrit/dependabot/npm_and_yarn/googleapis-72.0.0
Bump googleapis from 70.0.0 to 72.0.0
2021-04-26 09:31:40 -04:00
Trevor Buckner
3240e0c348 Merge pull request #1326 from naturalcrit/dependabot/npm_and_yarn/codemirror-5.61.0
Bump codemirror from 5.60.0 to 5.61.0
2021-04-26 09:30:46 -04:00
Trevor Buckner
185c02f4ac Merge pull request #1319 from naturalcrit/dependabot/npm_and_yarn/marked-2.0.3
Bump marked from 2.0.1 to 2.0.3
2021-04-26 09:30:08 -04:00
Trevor Buckner
f382aaf73c Merge pull request #1316 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.13.15
Bump @babel/preset-env from 7.13.12 to 7.13.15
2021-04-26 09:27:50 -04:00
dependabot-preview[bot]
be88c992fa Bump eslint from 7.23.0 to 7.25.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.23.0 to 7.25.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.23.0...v7.25.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-26 03:45:37 +00:00
dependabot-preview[bot]
85ff25a63b Bump codemirror from 5.60.0 to 5.61.0
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.60.0 to 5.61.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.60.0...5.61.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-26 03:45:37 +00:00
dependabot-preview[bot]
4e65c62881 Bump marked from 2.0.1 to 2.0.3
Bumps [marked](https://github.com/markedjs/marked) from 2.0.1 to 2.0.3.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/release.config.js)
- [Commits](https://github.com/markedjs/marked/compare/v2.0.1...v2.0.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-26 03:45:33 +00:00
dependabot-preview[bot]
6d035f2a2d Bump @babel/preset-env from 7.13.12 to 7.13.15
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.12 to 7.13.15.
- [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.13.15/packages/babel-preset-env)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-26 03:45:31 +00:00
dependabot-preview[bot]
7a35f6bb24 Bump googleapis from 70.0.0 to 72.0.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 70.0.0 to 72.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/googleapis-v70.0.0...googleapis-v72.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-26 03:45:26 +00:00
Trevor Buckner
c00e956909 Merge pull request #1322 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.12.5
Bump mongoose from 5.12.3 to 5.12.5
2021-04-25 23:43:41 -04:00
Trevor Buckner
cf3bf459f4 Merge pull request #1318 from Jc-L/typo-fixes
Fix minor typo
2021-04-25 23:41:45 -04:00
Trevor Buckner
e82d109840 Merge pull request #1327 from naturalcrit/FixAccidentalGoogleToHBTransfers
Fix accidental google to hb transfers
2021-04-25 23:40:50 -04:00
Trevor Buckner
c9a84a1813 Prevent accidental transfers from google to HB when not signed in 2021-04-25 23:38:44 -04:00
Trevor Buckner
7186a94c27 Do not force brew back to Homebrew if a Google save fails 2021-04-23 22:10:56 -04:00
dependabot-preview[bot]
45e4e98cb5 Bump mongoose from 5.12.3 to 5.12.5
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.12.3 to 5.12.5.
- [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.12.3...5.12.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-22 12:21:43 +00:00
Jean-Charles Longuet
9fc31e7f39 Fix minor typo 2021-04-09 13:42:09 +02:00
Trevor Buckner
983a37c77f Make cursor move to end of snippet after insertion 2021-04-06 11:43:34 -04:00
Trevor Buckner
a3b6a90fde Delete MonsterBorderFancy.png 2021-04-05 01:12:23 -04:00
Trevor Buckner
b771d82100 fix image file name 2021-04-05 01:11:55 -04:00
Trevor Buckner
9fa179ed9c Merge pull request #1310 from naturalcrit/Use-DataUri
Move base64 images out to files
2021-04-05 01:05:09 -04:00
Trevor Buckner
14d83d4263 Move base64 images out to files 2021-04-05 01:01:18 -04:00
Trevor Buckner
73ccad8a76 Remove unused images. 2021-04-04 23:28:44 -04:00
Trevor Buckner
488dbbb336 Make heroku happy with vitreum installation? 2021-04-04 23:04:22 -04:00
Trevor Buckner
08c8b69f4d Merge pull request #1309 from naturalcrit/dependabot/npm_and_yarn/classnames-2.3.1
Bump classnames from 2.2.6 to 2.3.1
2021-04-04 17:23:44 -04:00
dependabot-preview[bot]
cabb9b6c3b Bump classnames from 2.2.6 to 2.3.1
Bumps [classnames](https://github.com/JedWatson/classnames) from 2.2.6 to 2.3.1.
- [Release notes](https://github.com/JedWatson/classnames/releases)
- [Changelog](https://github.com/JedWatson/classnames/blob/master/HISTORY.md)
- [Commits](https://github.com/JedWatson/classnames/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-04 21:09:56 +00:00
Trevor Buckner
6697aa096a Merge pull request #1308 from naturalcrit/updateDependencies
update dependencies as of 4-4-21
2021-04-04 17:08:29 -04:00
Trevor Buckner
582725e7d7 Merge pull request #1307 from naturalcrit/V3MonsterBlockSnippets
V3 monster block snippets
2021-04-04 17:07:40 -04:00
Trevor Buckner
476d618286 update dependencies as of 4-4-21 2021-04-04 17:06:52 -04:00
Trevor Buckner
c186b6677b Clean up duplicate code in snippet generator 2021-04-04 16:22:50 -04:00
Trevor Buckner
ea9ba84dc2 Snippet for unframed monster block. 2021-04-04 12:50:12 -04:00
Trevor Buckner
bf616494f1 Fix padding so margin-top is not needed 2021-04-04 12:28:36 -04:00
Trevor Buckner
0b54bc046d Update wide monster snippet to use {{ 2021-04-04 00:16:29 -04:00
Trevor Buckner
c8c1966b8a Fix top margin 2021-04-04 00:11:14 -04:00
Trevor Buckner
9ad1c91472 Add wide monster blocks 2021-04-03 01:54:24 -04:00
Trevor Buckner
d8525f0eba .frame class to enable/remove the border 2021-04-02 14:01:27 -04:00
Trevor Buckner
7ae419716a Monster snippet added 2021-04-02 11:02:19 -04:00
Trevor Buckner
b0185a9ae4 Base Monster block style complete. 2021-04-01 23:57:13 -04:00
Trevor Buckner
d2cdb18a57 Cleaning up phb.style.less 2021-03-31 12:21:01 -04:00
Trevor Buckner
f04df5e297 Delete old fonts 2021-03-31 11:40:38 -04:00
Trevor Buckner
b90caaba85 Fix numbers on ScalySans fonts
In the latest version of fonts found online, numbers got restyled and misaligned. Copied numbers from old font version.
2021-03-31 11:39:21 -04:00
Trevor Buckner
d15bec08a3 Merge pull request #1282 from naturalcrit/ReduceRedundancyInServerJS
[WIP] Move common Server.js logic into functions
2021-03-27 23:43:11 -04:00
Trevor Buckner
ab473b12da Apply asyncHandler to getBrewFromId 2021-03-26 22:55:46 -04:00
Trevor Buckner
83c444ce11 Central Error Handling 2021-03-26 22:50:03 -04:00
Trevor Buckner
3ade40f2d9 Merge pull request #1291 from naturalcrit/BumpToV2.11.1
Update changelog
2021-03-20 23:58:27 -04:00
Trevor Buckner
0debd2bbf0 Update changelog 2021-03-20 23:57:27 -04:00
Trevor Buckner
1a3afc9661 Merge pull request #1286 from G-Ambatte/simplifyFancyFirstLetter
Add snippet for removing drop cap (aka fancy first letter).
2021-03-20 23:50:12 -04:00
Trevor Buckner
ac4ebbe548 Merge pull request #1290 from naturalcrit/NotifyTrashedGoogleBrew
Popup warning when opening a trashed google brew
2021-03-20 23:49:45 -04:00
Trevor Buckner
089414c9ff Fix v3 snippet 2021-03-20 23:48:55 -04:00
Trevor Buckner
a1dbf0f2e5 Popup warning when opening a trashed google brew 2021-03-20 23:33:22 -04:00
G.Ambatte
712824d8a6 Add 'Tweak Drop Cap' with default settings to allow users to tweak the drop cap as desired 2021-03-19 17:44:07 +13:00
G.Ambatte
7491f463b4 Lint fixes. 2021-03-19 17:32:27 +13:00
G.Ambatte
8f08591ab9 Add snippet for removing drop cap (aka fancy first letter). 2021-03-19 17:28:49 +13:00
Trevor Buckner
b98586150f Condense Download and Source paths also 2021-03-17 12:35:50 -04:00
Trevor Buckner
2f094801ca Move "get brew" logic to common function
Also add centralized error handling middleware
2021-03-16 00:06:01 -04:00
Trevor Buckner
dd35f101fe Add note about the "Download" button 2021-03-12 22:39:00 -05:00
Trevor Buckner
8a7513afd0 Merge pull request #1273 from naturalcrit/dependabot/npm_and_yarn/elliptic-6.5.4
[Security] Bump elliptic from 6.5.3 to 6.5.4
2021-03-12 22:34:23 -05:00
Trevor Buckner
2628ec00dc Merge pull request #1275 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.13.10
Bump @babel/preset-env from 7.13.9 to 7.13.10
2021-03-12 22:33:59 -05:00
dependabot-preview[bot]
778e27a374 Bump @babel/preset-env from 7.13.9 to 7.13.10
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.9 to 7.13.10.
- [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.13.10/packages/babel-preset-env)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-13 03:33:21 +00:00
Trevor Buckner
dd41eddd72 Merge pull request #1278 from naturalcrit/dependabot/npm_and_yarn/nanoid-3.1.21
Bump nanoid from 3.1.20 to 3.1.21
2021-03-12 22:33:01 -05:00
Trevor Buckner
5872452a6a Merge branch 'master' into dependabot/npm_and_yarn/nanoid-3.1.21 2021-03-12 22:32:52 -05:00
Trevor Buckner
af05403846 Merge pull request #1276 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.13.10
Bump @babel/core from 7.13.8 to 7.13.10
2021-03-12 22:31:28 -05:00
Trevor Buckner
3a55755721 Merge pull request #1279 from naturalcrit/dependabot/npm_and_yarn/mongoose-5.12.0
Bump mongoose from 5.11.18 to 5.12.0
2021-03-12 22:31:01 -05:00
Trevor Buckner
24957c653d Merge pull request #1198 from G-Ambatte/sourceDL
Download Brew Source as Text File
2021-03-12 22:29:55 -05:00
Trevor Buckner
6a12518ac1 Hardcode prefix 2021-03-12 22:24:36 -05:00
dependabot-preview[bot]
318e2924ca Bump mongoose from 5.11.18 to 5.12.0
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.11.18 to 5.12.0.
- [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.18...5.12.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-12 06:55:20 +00:00
dependabot-preview[bot]
0da5d00f9c Bump nanoid from 3.1.20 to 3.1.21
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.20 to 3.1.21.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.20...3.1.21)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-12 06:54:13 +00:00
dependabot-preview[bot]
7612702d73 Bump @babel/core from 7.13.8 to 7.13.10
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.8 to 7.13.10.
- [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.13.10/packages/babel-core)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-09 07:08:03 +00:00
dependabot-preview[bot]
9ba91b2dcc [Security] Bump elliptic from 6.5.3 to 6.5.4
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4. **This update includes a security fix.**
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-08 16:31:00 +00:00
Trevor Buckner
c4db94e86f Tweaks to work with current Master 2021-03-07 00:34:47 -05:00
Trevor Buckner
08492b943b Merge branch 'master' into pr/1198 2021-03-06 22:42:15 -05:00
Trevor Buckner
a1bf8ca945 Small bugs 2021-03-06 01:49:07 -05:00
Trevor Buckner
6d97eb308e Merge pull request #1271 from naturalcrit/Bump-version-to-2.11.0
Bump version number to 2.11.0
2021-03-06 01:24:33 -05:00
Trevor Buckner
64fe595b5f Bump version number. 2021-03-06 00:19:23 -05:00
Trevor Buckner
d82b385904 Merge pull request #1268 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.13.9
Bump @babel/preset-env from 7.13.5 to 7.13.9
2021-03-05 23:18:40 -05:00
Trevor Buckner
95201eb757 Merge branch 'master' into dependabot/npm_and_yarn/babel/preset-env-7.13.9 2021-03-05 23:17:47 -05:00
dependabot-preview[bot]
a387907604 Bump @babel/preset-env from 7.13.5 to 7.13.9
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.5 to 7.13.9.
- [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.13.9/packages/babel-preset-env)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-06 04:10:49 +00:00
Trevor Buckner
f16eba4855 Merge pull request #1269 from naturalcrit/dependabot/npm_and_yarn/googleapis-67.1.1
Bump googleapis from 67.1.0 to 67.1.1
2021-03-05 23:10:15 -05:00
Trevor Buckner
efdd68c2b8 Merge pull request #1264 from naturalcrit/dependabot/npm_and_yarn/query-string-6.14.1
Bump query-string from 6.14.0 to 6.14.1
2021-03-05 23:09:53 -05:00
Trevor Buckner
e927b675a4 Merge pull request #1263 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.13.8
Bump @babel/core from 7.13.1 to 7.13.8
2021-03-05 23:09:42 -05:00
Trevor Buckner
5d9373026b Merge pull request #1262 from naturalcrit/dependabot/npm_and_yarn/marked-2.0.1
Bump marked from 2.0.0 to 2.0.1
2021-03-05 23:09:09 -05:00
Trevor Buckner
48922e5293 Merge pull request #1261 from naturalcrit/dependabot/npm_and_yarn/eslint-7.21.0
Bump eslint from 7.20.0 to 7.21.0
2021-03-05 23:08:55 -05:00
Trevor Buckner
a55548d471 Merge pull request #1208 from G-Ambatte/addNewToNavBar
Adds a `New` button to the Nav bar
2021-03-05 22:33:07 -05:00
Trevor Buckner
f5d5f8cf67 Simplify Nav Item 2021-03-05 22:31:59 -05:00
Trevor Buckner
0060691b50 Merge pull request #1242 from G-Ambatte/importPage
Import from Share ID
2021-03-04 22:59:52 -05:00
Trevor Buckner
5b242989da Handle cached text
The NEW page saves to the browser LocalStorage the current text in case the user goes to a different page before saving. The new "import" function wasn't working since it was being overwritten by any cached values if they existed from an earlier "new" page.
2021-03-04 22:58:40 -05:00
G.Ambatte
3358094319 Fix issues arising post-merge 2021-03-04 22:01:37 -05:00
G.Ambatte
2f9bd00d70 Merge importPage functions into newPage. 2021-03-04 22:01:11 -05:00
G.Ambatte
ed23578dcf Lint fixes. 2021-03-04 21:57:33 -05:00
G.Ambatte
41ecbb62a2 Redirect new to import. 2021-03-04 21:57:32 -05:00
G.Ambatte
32ef36d7f7 Initial commit: Import from Share ID appears to be functioning correctly. 2021-03-04 21:57:32 -05:00
Trevor Buckner
50936253de Merge pull request #1267 from naturalcrit/UpdateSolberaImitationFont
Update Fonts
2021-03-04 21:46:45 -05:00
Trevor Buckner
3c7b6eb5c3 Merge branch 'master' into UpdateSolberaImitationFont 2021-03-04 21:44:46 -05:00
Trevor Buckner
c28fed0893 Use .woff2 files instead of base64 encoding 2021-03-04 21:39:37 -05:00
dependabot-preview[bot]
36910a0a8e Bump googleapis from 67.1.0 to 67.1.1
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 67.1.0 to 67.1.1.
- [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/v67.1.0...v67.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-02 06:53:03 +00:00
Trevor Buckner
5824ab6eb5 Merge branch 'PRODUCTION' into master 2021-03-01 15:17:40 -05:00
dependabot-preview[bot]
e6ae1ddec6 Bump query-string from 6.14.0 to 6.14.1
Bumps [query-string](https://github.com/sindresorhus/query-string) from 6.14.0 to 6.14.1.
- [Release notes](https://github.com/sindresorhus/query-string/releases)
- [Commits](https://github.com/sindresorhus/query-string/compare/v6.14.0...v6.14.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-01 08:59:22 +00:00
dependabot-preview[bot]
2213d23115 Bump @babel/core from 7.13.1 to 7.13.8
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.1 to 7.13.8.
- [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.13.8/packages/babel-core)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-01 08:58:22 +00:00
dependabot-preview[bot]
6393cdec9b Bump marked from 2.0.0 to 2.0.1
Bumps [marked](https://github.com/markedjs/marked) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/release.config.js)
- [Commits](https://github.com/markedjs/marked/compare/v2.0.0...v2.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-01 08:57:28 +00:00
dependabot-preview[bot]
a10f573a30 Bump eslint from 7.20.0 to 7.21.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.20.0 to 7.21.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.20.0...v7.21.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-01 08:56:43 +00:00
Rodrigo Kuerten
9dcce15790 Updated extraKeys (bold and italic) and added new shortcut (for span tags) (#1191)
* Updated extraKeys (bold and italic) and added new shortcut (for span)

* Updated makeSpan shortcut to Ctrl/Cmd-M

* ESLint

* Space after {{ so text appears

Co-authored-by: Trevor Buckner <calculuschild@gmail.com>
2021-02-28 23:03:50 -05:00
G.Ambatte
481c9f067c Fix title issue (#1251)
* Maximum title length set to 100 characters.

* Reverse unnecessary change that was incorrectly included in previous commit.

* Reduced code change to one addition on a single line.

* Revert "Reduced code change to one addition on a single line."

This reverts commit 2a355cf115.

* Use newer syntax to shorten

Co-authored-by: Trevor Buckner <calculuschild@gmail.com>
2021-02-26 17:47:29 -05:00
dependabot-preview[bot]
1e64e49dc3 Bump nconf from 0.11.1 to 0.11.2 (#1216)
Bumps [nconf](https://github.com/flatiron/nconf) from 0.11.1 to 0.11.2.
- [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.1...v0.11.2)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 19:09:34 -05:00
dependabot-preview[bot]
19a2ecd281 Bump @babel/core from 7.12.10 to 7.13.1 (#1254)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.12.10 to 7.13.1.
- [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.13.1/packages/babel-core)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 19:08:46 -05:00
dependabot-preview[bot]
03b02669a4 Bump @babel/preset-react from 7.12.10 to 7.12.13 (#1225)
Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.12.10 to 7.12.13.
- [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.12.13/packages/babel-preset-react)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 19:05:13 -05:00
Trevor Buckner
c979f02ce4 Update Marked.js version 2021-02-25 19:03:00 -05:00
dependabot-preview[bot]
bc86c1b8fc Bump eslint from 7.18.0 to 7.20.0 (#1244)
Bumps [eslint](https://github.com/eslint/eslint) from 7.18.0 to 7.20.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.18.0...v7.20.0)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 15:45:47 -05:00
dependabot-preview[bot]
37d0a4aad2 Bump googleapis from 67.0.0 to 67.1.0 (#1245)
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 67.0.0 to 67.1.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/v67.0.0...v67.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>
2021-02-25 15:45:36 -05:00
dependabot-preview[bot]
ff70b5c546 Bump mongoose from 5.11.13 to 5.11.18 (#1256)
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.11.13 to 5.11.18.
- [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.13...5.11.18)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 15:44:27 -05:00
dependabot-preview[bot]
7daec673ba Bump lodash from 4.17.20 to 4.17.21 (#1252)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 15:44:12 -05:00
dependabot-preview[bot]
f2d07a699a Bump @babel/preset-env from 7.12.11 to 7.13.5 (#1257)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.12.11 to 7.13.5.
- [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.13.5/packages/babel-preset-env)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 15:44:00 -05:00
dependabot-preview[bot]
721511e484 Bump query-string from 6.13.8 to 6.14.0 (#1236)
Bumps [query-string](https://github.com/sindresorhus/query-string) from 6.13.8 to 6.14.0.
- [Release notes](https://github.com/sindresorhus/query-string/releases)
- [Commits](https://github.com/sindresorhus/query-string/compare/v6.13.8...v6.14.0)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 15:19:33 -05:00
dependabot-preview[bot]
2942660201 Bump codemirror from 5.59.2 to 5.59.4 (#1258)
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.59.2 to 5.59.4.
- [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.2...5.59.4)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-25 15:17:27 -05:00
Christian Brickhouse
68811eb3fc Implementing magic item snippet from Issue 671. (#842)
* Implementing magic item snippet from Issue 671.

* Fixes syntax errors. Function moved into existing magic module.

* Implementing magic item snippet from Issue 671.

* Fixes syntax errors. Function moved into existing magic module.

* Magic Item Snippet, <dl>, `:` for blank line

Co-authored-by: Trevor Buckner <calculuschild@gmail.com>
2021-02-24 21:58:11 -05:00
Trevor Buckner
468b7319d1 Unify brew structure in all pages 2021-02-20 21:59:18 -05:00
Rasmus Bækgaard
009a11a9f5 Add QR-Code as snippet under Editor (#539)
* Add snippet for QR-code

* Add snippet for QR-code

* Refactor to expose metadata to snippets

* Lint

Co-authored-by: Rasmus Bækgaard <git@bakgaard.net>
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>
2021-02-19 23:39:29 -05:00
Alexey Sachkov
7057422077 Enable caching of static assets (#1217)
* Enable caching of static assets

* Remove dependency on mime package

Since we only care about two file extensions at the moment,
there is no need to grab the whole package just to avoid
calling 'endsWith' twice.
2021-02-19 17:58:02 -05:00
G.Ambatte
ecae16b5d4 Update robots.txt (#1239) 2021-02-19 17:54:00 -05:00
Trevor Buckner
d57df84a59 Update Production (#1249)
* Legacy renderer (#1184)

* Include two versions of Marked.js

* Include two versions of Marked.js

* Working two different render pipelines

Adds stylesheet "styleLegacy.less"
Adds markdownHandler "markdownLegacy.js"
The BrewRenderer will switch between these and the new pipeline dependent on the "version" prop passed in.

* Mustache-style div blocks

* Legacy snippets & columnbreak

* Codemirror styling for Div Blocks

* Lint

* Codemirror highlights for inline Divs as well

These will turn red `{{class Content}}`

Multi-line divs will turn purple

```
{{class,class2
content
}}
```

No real need for these to be different colors. Just for testing.

* More lint

* Update dependencies.

* Adding Button to switch render pipelines

* Update Marked.js

* Popup alert to refresh page when renderer changed

* Don't compress files in Development (very slow)

* Block DIV or inline Span depending on {{ placement

* \column emits a Div instead of Span

* Allow share page to use new renderer

* {{ divs no longer need empty lines. Spans work in lists.

* Typo

* Typo

* Enforce \page must be at start of line. Code cleanup.

* Inject newlines after/before {{/}} to avoid needing blank lines

* Fixes issues with tables.

* Remove console.log

* Fix spacing issue for Spans

* Move things from Brewrenderer to Markdown

Try to keep all custom text fiddling in one spot.

* Rename variables

* Update Font-Awesome to v5.15. Fix style issues on popups.

* Update {{ Divs/Spans, Fix nested hilighting

* Fixed Spans/divs with no tags or just commas

* Use blacklist for {{ to allow more characters

* Update package-lock.json

* Update all icons to Font-awesome 5

* V3 hidden behind config variable

Add "globalThis.enable_v3 = true" in the console to enable.

* lint

* Legacy renderer (#1229)

* Include two versions of Marked.js

* Include two versions of Marked.js

* Working two different render pipelines

Adds stylesheet "styleLegacy.less"
Adds markdownHandler "markdownLegacy.js"
The BrewRenderer will switch between these and the new pipeline dependent on the "version" prop passed in.

* Mustache-style div blocks

* Legacy snippets & columnbreak

* Codemirror styling for Div Blocks

* Lint

* Codemirror highlights for inline Divs as well

These will turn red `{{class Content}}`

Multi-line divs will turn purple

```
{{class,class2
content
}}
```

No real need for these to be different colors. Just for testing.

* More lint

* Update dependencies.

* Adding Button to switch render pipelines

* Update Marked.js

* Popup alert to refresh page when renderer changed

* Don't compress files in Development (very slow)

* Block DIV or inline Span depending on {{ placement

* \column emits a Div instead of Span

* Allow share page to use new renderer

* {{ divs no longer need empty lines. Spans work in lists.

* Typo

* Typo

* Enforce \page must be at start of line. Code cleanup.

* Inject newlines after/before {{/}} to avoid needing blank lines

* Fixes issues with tables.

* Remove console.log

* Fix spacing issue for Spans

* Move things from Brewrenderer to Markdown

Try to keep all custom text fiddling in one spot.

* Rename variables

* Update Font-Awesome to v5.15. Fix style issues on popups.

* Update {{ Divs/Spans, Fix nested hilighting

* Fixed Spans/divs with no tags or just commas

* Use blacklist for {{ to allow more characters

* Update package-lock.json

* Update all icons to Font-awesome 5

* V3 hidden behind config variable

Add "globalThis.enable_v3 = true" in the console to enable.

* lint

* Give user styles higher priority to still allow overrides

* Apply style priority to *all* user styles

* Change .legacy .v3 to .phb, .phb3

* Revert accidental color change

* Fix brew styles overwriting each other. (#1230)

* Fix /page not working in legacy mode. (#1233)

* Fix brew styles overwriting each other.

* Word wrapping, start fixing spacing on Title letter

* Fix \page in legacy brews when not at line start

* Default 'legacy' if not set. Auto-change styles.

* Fix brew styles overwriting each other.

* Word wrapping, start fixing spacing on Title letter

* Fix \page in legacy brews when not at line start

* Fix Page Padding

* Set 'legacy' as default value if not set in brew saved file.

* Apply Legacy\v3 renderer to print page (#1235)
2021-02-19 17:47:56 -05:00
Trevor Buckner
146da57ba3 Apply Legacy\v3 renderer to print page (#1235) 2021-02-08 22:06:53 -05:00
Trevor Buckner
fd94d162ea Padding & margins 2021-02-08 16:06:54 -05:00
Trevor Buckner
b5abd472b0 Merge branch 'master' into UpdateSolberaImitationFont 2021-02-08 15:56:45 -05:00
Trevor Buckner
ee4ecc0b41 Default 'legacy' if not set. Auto-change styles.
* Fix brew styles overwriting each other.

* Word wrapping, start fixing spacing on Title letter

* Fix \page in legacy brews when not at line start

* Fix Page Padding

* Set 'legacy' as default value if not set in brew saved file.
2021-02-08 15:09:09 -05:00
Trevor Buckner
04fb1f243d Update Font 2021-02-08 14:03:33 -05:00
Trevor Buckner
e5ccfa3a50 Fix /page not working in legacy mode. (#1233)
* Fix brew styles overwriting each other.

* Word wrapping, start fixing spacing on Title letter

* Fix \page in legacy brews when not at line start
2021-02-08 13:58:24 -05:00
Trevor Buckner
c642a35fb3 Fix brew styles overwriting each other. (#1230) 2021-02-05 14:58:41 -05:00
Trevor Buckner
2fe353377b Revert accidental color change 2021-02-05 09:38:20 -05:00
Trevor Buckner
de1017a20a Legacy renderer (#1229)
* Include two versions of Marked.js

* Include two versions of Marked.js

* Working two different render pipelines

Adds stylesheet "styleLegacy.less"
Adds markdownHandler "markdownLegacy.js"
The BrewRenderer will switch between these and the new pipeline dependent on the "version" prop passed in.

* Mustache-style div blocks

* Legacy snippets & columnbreak

* Codemirror styling for Div Blocks

* Lint

* Codemirror highlights for inline Divs as well

These will turn red `{{class Content}}`

Multi-line divs will turn purple

```
{{class,class2
content
}}
```

No real need for these to be different colors. Just for testing.

* More lint

* Update dependencies.

* Adding Button to switch render pipelines

* Update Marked.js

* Popup alert to refresh page when renderer changed

* Don't compress files in Development (very slow)

* Block DIV or inline Span depending on {{ placement

* \column emits a Div instead of Span

* Allow share page to use new renderer

* {{ divs no longer need empty lines. Spans work in lists.

* Typo

* Typo

* Enforce \page must be at start of line. Code cleanup.

* Inject newlines after/before {{/}} to avoid needing blank lines

* Fixes issues with tables.

* Remove console.log

* Fix spacing issue for Spans

* Move things from Brewrenderer to Markdown

Try to keep all custom text fiddling in one spot.

* Rename variables

* Update Font-Awesome to v5.15. Fix style issues on popups.

* Update {{ Divs/Spans, Fix nested hilighting

* Fixed Spans/divs with no tags or just commas

* Use blacklist for {{ to allow more characters

* Update package-lock.json

* Update all icons to Font-awesome 5

* V3 hidden behind config variable

Add "globalThis.enable_v3 = true" in the console to enable.

* lint

* Give user styles higher priority to still allow overrides

* Apply style priority to *all* user styles

* Change .legacy .v3 to .phb, .phb3
2021-02-04 23:31:37 -05:00
Trevor Buckner
e2cd7d9f07 Legacy renderer (#1184)
* Include two versions of Marked.js

* Include two versions of Marked.js

* Working two different render pipelines

Adds stylesheet "styleLegacy.less"
Adds markdownHandler "markdownLegacy.js"
The BrewRenderer will switch between these and the new pipeline dependent on the "version" prop passed in.

* Mustache-style div blocks

* Legacy snippets & columnbreak

* Codemirror styling for Div Blocks

* Lint

* Codemirror highlights for inline Divs as well

These will turn red `{{class Content}}`

Multi-line divs will turn purple

```
{{class,class2
content
}}
```

No real need for these to be different colors. Just for testing.

* More lint

* Update dependencies.

* Adding Button to switch render pipelines

* Update Marked.js

* Popup alert to refresh page when renderer changed

* Don't compress files in Development (very slow)

* Block DIV or inline Span depending on {{ placement

* \column emits a Div instead of Span

* Allow share page to use new renderer

* {{ divs no longer need empty lines. Spans work in lists.

* Typo

* Typo

* Enforce \page must be at start of line. Code cleanup.

* Inject newlines after/before {{/}} to avoid needing blank lines

* Fixes issues with tables.

* Remove console.log

* Fix spacing issue for Spans

* Move things from Brewrenderer to Markdown

Try to keep all custom text fiddling in one spot.

* Rename variables

* Update Font-Awesome to v5.15. Fix style issues on popups.

* Update {{ Divs/Spans, Fix nested hilighting

* Fixed Spans/divs with no tags or just commas

* Use blacklist for {{ to allow more characters

* Update package-lock.json

* Update all icons to Font-awesome 5

* V3 hidden behind config variable

Add "globalThis.enable_v3 = true" in the console to enable.

* lint
2021-02-02 20:38:25 -05:00
G.Ambatte
c3bfd1e8bf Prefix loading successfully from config/local.json for both Homebrewery and Google brews.
Concats switched to string literals to make Linter happy.
2021-01-29 22:53:58 +13:00
G.Ambatte
051773a084 Move filename prefix to configurable item in config/default.json 2021-01-28 23:13:01 +13:00
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
e367cb2152 Initial commit - adds a New button to the Nav bar on the Home, User, and Edit pages. 2021-01-23 00:36:10 +13:00
G.Ambatte
bcbf596aa8 Increase minimum height of brewItem in CSS so all four items are always visible.
Adjust eslint line in `server.js` to re-enable the test with a slightly higher maximum value.
2021-01-22 21:24:22 +13:00
G.Ambatte
d88b04783d Lint corrections 2021-01-22 17:31:01 +13:00
G.Ambatte
6d219aa701 Unwinding refactored code + corrections. 2021-01-22 17:29:26 +13:00
G.Ambatte
da32845dd1 (WIP) Unwinding refactoring to improve clarity and readability of new functions 2021-01-22 16:44:34 +13:00
Sean Robertson
4073536d96 (WIP) Remove target=_blank and rel=noopener noreferrer from brewItem.jsx to eliminate white flash on User page when clicking link to download source of a brew. 2021-01-22 13:59:15 +13:00
Sean Robertson
21f08c97a1 Expand source text escaping/processing for improved readability 2021-01-22 13:56:29 +13:00
G.Ambatte
06223d576d Fixes from Linter 2021-01-21 20:32:23 +13:00
G.Ambatte
5c7a9c92d1 Integrated download function into server.js
Remove new function from `Homebrew.Model.js`
Remove `sourceFunctions.jsx` module
2021-01-21 20:27:46 +13:00
G.Ambatte
0e8348f360 Catch zero length filenames 2021-01-21 07:39:08 +13:00
G.Ambatte
8060ed5f8e Rename sanitizeHtml function to escapeTextForHtmlDisplay which better describes it's actual function 2021-01-21 07:37:50 +13:00
G.Ambatte
e8135fcbb4 Added sanitizeHtml function to Homebrew model to generate HTML from the brew for the source page.
Moved `source` page generation function to a new function module. Added option to function to create plain text download with a sanitized filename and made it accessible from a new page: `download`.
Added the `download` item to the BrewItem so it appears on each brew on the User page.
Added `sanitize-filename` dependency to `package.json`.
2021-01-21 00:02:15 +13:00
G.Ambatte
7fccb7e03e Shift functionality to new file in attempt to reduce code duplication in server.js 2021-01-17 22:07:26 +13:00
G.Ambatte
715ddf2b8c Initial commit 2021-01-17 18:52:56 +13:00
G.Ambatte
717a5886cf Merge branch 'master' of https://github.com/G-Ambatte/homebrewery 2021-01-17 17:23:18 +13:00
G.Ambatte
407232c708 Fix for MongoDB package update breaking install script
Change to enable use of rcvars for NODE_ENV and PORT
2021-01-17 17:19:24 +13:00
G.Ambatte
edd902397e Remove hanging comma in config.json
Change default port assignment from 8000 to 8001 in config.json and FreeBSD service config
2021-01-17 17:19:24 +13:00
G.Ambatte
24f5fcb5a0 Move NODE_ENV to service file; remove from server.js and config/default.json 2021-01-17 17:19:24 +13:00
G.Ambatte
8f6270723e Update server.js
Lint is happy, now attempting to pacify CircleCI
2021-01-17 17:19:24 +13:00
G.Ambatte
9cccd2d74e Update server.js
Eliminating unnecessary debugging code, reducing line count and making lint happy :)
2021-01-17 17:19:24 +13:00
G.Ambatte
ed34b65dbd Update homebrewery
Add `dev_mode` to the HomeBrewery service, which starts the HomeBrewery project in live rebuild mode.
2021-01-17 17:19:23 +13:00
G.Ambatte
4484cc7d16 Change install directory
Change of install directory to `/usr/local/homebrewery`
2021-01-17 17:19:23 +13:00
G.Ambatte
8677994fb7 Update install.sh
Change to main project repo, on assumption that the PR will be merged at some point
2021-01-17 17:19:23 +13:00
G.Ambatte
ea555eb410 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.
2021-01-17 17:19:23 +13:00
G.Ambatte
e140b656a6 Update server.js
Change to setting NODE_ENV; default to 'local' if not set via environment variable or in the config file.
2021-01-17 17:19:23 +13:00
G.Ambatte
6423d909d7 Remove environment variables from rc.d
Remove environment variables from rc.d as they are now in config/default.json.
2021-01-17 17:19:23 +13:00
G.Ambatte
8887961d09 Add config items
Add config items and default values:
- web_port (8001)
- environment (local)
2021-01-17 17:19:17 +13:00
G.Ambatte
4ee891a3ba Update install.sh
Switch to latest version of MongoDB
2021-01-17 17:19:11 +13:00
G.Ambatte
96b976fd4a Update install.sh 2021-01-17 17:19:11 +13:00
G.Ambatte
1b9d46f834 Create install.sh 2021-01-17 17:19:10 +13: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
03e74afe80 Fix for MongoDB package update breaking install script
Change to enable use of rcvars for NODE_ENV and PORT
2021-01-10 17:23:14 +13:00
G.Ambatte
b0c1a5a6b1 Add link to FreeBSD install documenation in the main README.md file 2021-01-10 15:27:04 +13:00
G.Ambatte
3af43164f4 Remove hanging comma in config.json
Change default port assignment from 8000 to 8001 in config.json and FreeBSD service config
2021-01-09 23:03:39 +13: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
G.Ambatte
e394539742 Move NODE_ENV to service file; remove from server.js and config/default.json 2021-01-03 23:57:43 +13: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
G.Ambatte
bd416233eb Update server.js
Lint is happy, now attempting to pacify CircleCI
2020-12-29 17:57:33 +13:00
G.Ambatte
7ca9d601a0 Update server.js
Eliminating unnecessary debugging code, reducing line count and making lint happy :)
2020-12-29 17:55:15 +13:00
G.Ambatte
ac8988ad41 Update homebrewery
Add `dev_mode` to the HomeBrewery service, which starts the HomeBrewery project in live rebuild mode.
2020-12-28 21:31:00 +13:00
G.Ambatte
90fdc71279 Change install directory
Change of install directory to `/usr/local/homebrewery`
2020-12-28 15:24:29 +13:00
G.Ambatte
5d126ff14d Update install.sh
Change to main project repo, on assumption that the PR will be merged at some point
2020-12-28 15:12:51 +13:00
G.Ambatte
38e098f6c4 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.
2020-12-28 14:28:49 +13:00
G.Ambatte
d3fa8a54ae 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.
2020-12-28 12:56:17 +13: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
G.Ambatte
443094d282 Update server.js
Change to setting NODE_ENV; default to 'local' if not set via environment variable or in the config file.
2020-12-06 21:54:20 +13:00
G.Ambatte
e727f1749f Remove environment variables from rc.d
Remove environment variables from rc.d as they are now in config/default.json.
2020-12-06 20:41:48 +13:00
G.Ambatte
1224a54884 Add config items
Add config items and default values:
- web_port (8001)
- environment (local)
2020-12-06 20:39:43 +13:00
G.Ambatte
897e7dccc6 Update install.sh
Switch to latest version of MongoDB
2020-12-06 18:43:01 +13:00
G.Ambatte
88631ed7a8 Update install.sh 2020-12-06 18:35:24 +13:00
G.Ambatte
65f4094b5a Create install.sh 2020-12-06 18:06:23 +13:00
G.Ambatte
99656357b1 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.
2020-12-06 18:01:39 +13:00
G.Ambatte
d33ae2a50a Update server.js
Eliminate requirement to CD into project directory prior to running `npm start` or `node server.js`.
2020-12-06 17:51:18 +13:00
G.Ambatte
f419430c6b Merge pull request #1 from naturalcrit/master
Add detailed MongoDB Install instructions
2020-11-30 19:30:23 +13: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
125 changed files with 17267 additions and 3826 deletions

View File

@@ -1,7 +1,7 @@
module.exports = {
root : true,
parserOptions : {
ecmaVersion : 9,
ecmaVersion : 2021,
sourceType : 'module',
ecmaFeatures : {
jsx : true
@@ -55,7 +55,7 @@ module.exports = {
'array-bracket-spacing' : ['warn', 'never'],
'arrow-spacing' : ['warn', { before: false, after: false }],
'comma-spacing' : ['warn', { before: false, after: true }],
'indent' : ['warn', 'tab'],
'indent' : ['warn', 'tab', { 'MemberExpression': 'off' }],
'keyword-spacing' : ['warn', {
before : true,
after : true,

69
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 99
ignore:
- dependency-name: eslint
versions:
- 7.19.0
- 7.22.0
- 7.23.0
- 7.24.0
- dependency-name: "@babel/core"
versions:
- 7.12.13
- 7.12.16
- 7.12.17
- 7.13.13
- 7.13.14
- 7.13.15
- dependency-name: googleapis
versions:
- 68.0.0
- 70.0.0
- 71.0.0
- dependency-name: "@babel/preset-env"
versions:
- 7.12.13
- 7.12.16
- 7.12.17
- 7.13.0
- 7.13.12
- 7.13.8
- dependency-name: mongoose
versions:
- 5.11.14
- 5.11.15
- 5.11.16
- 5.11.17
- 5.11.18
- 5.11.19
- 5.12.1
- 5.12.2
- 5.12.3
- dependency-name: eslint-plugin-react
versions:
- 7.23.0
- 7.23.1
- dependency-name: query-string
versions:
- 7.0.0
- dependency-name: nanoid
versions:
- 3.1.22
- dependency-name: "@babel/preset-react"
versions:
- 7.13.13
- dependency-name: codemirror
versions:
- 5.59.3
- 5.60.0
- dependency-name: classnames
versions:
- 2.3.0
- dependency-name: marked
versions:
- 1.2.8

View File

@@ -1,4 +1,4 @@
FROM node:8
FROM node:14.15
ENV NODE_ENV=docker

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

103
README.md
View File

@@ -1,45 +1,93 @@
# The Homebrewery
The Homebrewery is a tool for making authentic looking [D&D content](https://dnd.wizards.com/products/tabletop-games/rpg-products/rpg_playershandbook) using [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). It is distributed under the terms of the [MIT License](./license).
[![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](https://homebrewery.naturalcrit.com). 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.
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 two programs that the Homebrewery requires to run.
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/)
1. install [mongodb](https://www.mongodb.com/try/download/community) (Community version)
Second, download a copy of the repository. If you have git you can do so with
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
```
Third, you will need to add the environment variable `NODE_ENV = local` to allow the project to run locally.
[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`
Fourth, you will need to install the program and run it using the two commands:
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.
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)
### 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.
### Running the application on FreeBSD or FreeNAS
If you are developing locally and would like to generate your own, follow the above steps and then run `npm run phb`.
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](/issues). You can also get help for issues on the subreddit [r/homebrewery](https://www.reddit.com/r/homebrewery)
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
@@ -47,6 +95,33 @@ 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.
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.
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,5 +1,123 @@
<style>
h5 {
font-size: .35cm !important;
}
</style>
# changelog
### Friday, 30/07/2021 - v2.13.2
- Background work to allow new themes in the future
- Fixed cursor getting stuck when resizing divider bar
##### G-Ambatte :
- Fix Style tab not copying when Cloned To New
- Basic brew sorting on User page
- Reduced data sent on each request from server
##### Gazook89 :
- Cleaned up styling on menus
### Saturday, 28/6/2021 - v2.13.1
- Fixed the issue with new brews not saving!
### Saturday, 26/6/2021 - v2.13.0
- "Share to Reddit" button now works with Google brews
- Downloading or viewing the source of your brew will now show the contents of the Style tab at the top of the document in a backtick code fence like this:
\`\`\`css
myStyle {color: black}
\`\`\`
##### G-Ambatte :
- New **Download**, **View**, and **Clone to New** buttons in the "Source" dropdown on the Share page.
- Pasting your brew into a "New" page and saving will transfer any CSS in the code fence to the Style tab.
- Unsaved work in the New page Style tab is now cached to your browser storage if you navigate away.
### Thursday, 10/6/2021 - v2.12.0
- New "style" tab to better organize custom CSS in preparation for new themes and sharable styles.
- Your own Google brews will no longer show up in the list when viewing someone else's profile.
### Saturday, 02/5/2021 - v2.11.2
- Fix for edge case where brews could accidentally transfer from Google Drive back to Homebrewery.
- Move cursor to end of snippet after insertion
### Saturday, 20/3/2021 - v2.11.1
- Warning when opening brew in your Google Drive trash
##### G-Ambatte :
- Snippet to remove drop caps (fancy first letter after title)
```
```
### Saturday, 13/3/2021 - v2.11.0
- Many background things for upcoming v3. Get pumped.
##### G-Ambatte :
- Fixed new brews failing to save when auto-generated file name is too long.
- "New" button added to the Nav bar.
- "Download" button to download your brew as a text file.
- Reduced download size and improved caching.
##### RKuerten :
- Bold and Italics hotkeys for Mac users (Cmd+B, Cmd+I)
### 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
\page
### 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
@@ -33,21 +151,14 @@
### 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)
## BIG NEWS
With the next major release of Homebrewery, v3.0.0, this tool *will no longer support raw HTML input for brew code*. Most issues and errors users are having are because of this feature and it's become too taxing to help and fix these issues.
All brews made previous to the release of v3.0.0 will still render normally.
### Thursday, 19/01/2017 - v2.7.0
### 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
@@ -60,8 +171,6 @@ All brews made previous to the release of v3.0.0 will still render normally.
- 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
@@ -73,18 +182,17 @@ All brews made previous to the release of v3.0.0 will still render normally.
- 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.
\page
### Wednesday, 23/11/2016 - v2.5.0
- Metadata can now be added to brews
- Added a metadata editor onto the edit and new pages
@@ -95,7 +203,6 @@ All brews made previous to the release of v3.0.0 will still render normally.
- 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
@@ -120,7 +227,6 @@ All brews made previous to the release of v3.0.0 will still render normally.
- 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!)
@@ -141,12 +247,9 @@ All brews made previous to the release of v3.0.0 will still render normally.
- 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.
\page
### 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!
@@ -161,6 +264,8 @@ All brews made previous to the release of v3.0.0 will still render normally.
### Wednesday, 25/05/2016 -v2.0.5
- The class table generators have the proper ability score improvement progression.
\page
### 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)
@@ -171,8 +276,6 @@ All brews made previous to the release of v3.0.0 will still render normally.
- 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.
@@ -212,8 +315,6 @@ Massive changelog incoming:
- 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
@@ -273,7 +374,6 @@ Massive changelog incoming:
* Increased padding on table cells
* Raw html now shows in view source
## v1.0.0 - Release
### Wednesday, 3/01/2016
@@ -281,4 +381,3 @@ Massive changelog incoming:
* 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

@@ -18,7 +18,7 @@ const Admin = createClass({
<header>
<div className='container'>
<i className='fa fa-rocket' />
<i className='fas fa-rocket' />
homebrewery admin
</div>
</header>

View File

@@ -45,8 +45,8 @@ const BrewCleanup = createClass({
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>
? <i className='fas fa-spin fa-spinner' />
: <span><i className='fas fa-times' /> Remove</span>
}
</button>
<span>Found {this.state.count} Brews that could be removed. </span>
@@ -59,7 +59,7 @@ const BrewCleanup = createClass({
<button onClick={this.prime} className='query'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
? <i className='fas fa-spin fa-spinner' />
: 'Query Brews'
}
</button>

View File

@@ -59,8 +59,8 @@ const BrewCompress = createClass({
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>
? <i className='fas fa-spin fa-spinner' />
: <span><i className='fas fa-compress' /> compress </span>
}
</button>
{this.state.pending
@@ -76,7 +76,7 @@ const BrewCompress = createClass({
<button onClick={this.prime} className='query'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
? <i className='fas fa-spin fa-spinner' />
: 'Query Brews'
}
</button>

View File

@@ -61,7 +61,7 @@ const BrewLookup = createClass({
<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', {
<i className={cx('fas', {
'fa-search' : !this.state.searching,
'fa-spin fa-spinner' : this.state.searching,
})} />

View File

@@ -37,7 +37,7 @@ const Stats = createClass({
</dl>
{this.state.fetching
&& <div className='pending'><i className='fa fa-spin fa-spinner' /></div>
&& <div className='pending'><i className='fas fa-spin fa-spinner' /></div>
}
</div>;
}

View File

@@ -4,12 +4,14 @@ const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
const Markdown = require('naturalcrit/markdown.js');
const ErrorBar = require('./errorBar/errorBar.jsx');
//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;
const PAGE_HEIGHT = 1056;
const PPR_THRESHOLD = 50;
@@ -17,45 +19,61 @@ const PPR_THRESHOLD = 50;
const BrewRenderer = createClass({
getDefaultProps : function() {
return {
text : '',
errors : []
text : '',
style : '',
renderer : 'legacy',
errors : []
};
},
getInitialState : function() {
const pages = this.props.text.split('\\page');
let pages;
if(this.props.renderer == 'legacy') {
pages = this.props.text.split('\\page');
} else {
pages = this.props.text.split(/^\\page/gm);
}
return {
viewablePageNumber : 0,
height : 0,
isMounted : false,
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD,
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD,
visibility : 'hidden',
initialContent : `<!DOCTYPE html><html><head>
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.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>`
};
},
height : 0,
lastRender : <div></div>,
componentDidMount : function() {
this.updateSize();
window.addEventListener('resize', this.updateSize);
},
componentWillUnmount : function() {
window.removeEventListener('resize', this.updateSize);
},
componentWillReceiveProps : function(nextProps) {
const pages = nextProps.text.split('\\page');
this.setState({
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD
});
componentDidUpdate : function(prevProps) {
if(prevProps.text !== this.props.text) {
let pages;
if(this.props.renderer == 'legacy') {
pages = this.props.text.split('\\page');
} else {
pages = this.props.text.split(/^\\page/gm);
}
this.setState({
pages : pages,
usePPR : pages.length >= PPR_THRESHOLD
});
}
},
updateSize : function() {
this.setState({
height : this.refs.main.parentNode.clientHeight,
isMounted : true
height : this.refs.main.parentNode.clientHeight,
});
},
@@ -85,7 +103,7 @@ const BrewRenderer = createClass({
},
renderPageInfo : function(){
return <div className='pageInfo'>
return <div className='pageInfo' ref='main'>
{this.state.viewablePageNumber + 1} / {this.state.pages.length}
</div>;
},
@@ -100,18 +118,26 @@ const BrewRenderer = createClass({
renderDummyPage : function(index){
return <div className='phb' id={`p${index + 1}`} key={index}>
<i className='fa fa-spinner fa-spin' />
<i className='fas fa-spinner fa-spin' />
</div>;
},
renderStyle : function() {
if(!this.props.style) return;
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${this.props.style} </style>` }} />;
},
renderPage : function(pageText, index){
return <div className='phb' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: Markdown.render(pageText) }} key={index} />;
if(this.props.renderer == 'legacy')
return <div className='phb page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(pageText) }} key={index} />;
else
return <div className='phb3 page' id={`p${index + 1}`} dangerouslySetInnerHTML={{ __html: Markdown.render(pageText) }} key={index} />;
},
renderPages : function(){
if(this.state.usePPR){
return _.map(this.state.pages, (page, index)=>{
if(this.shouldRender(page, index)){
if(this.shouldRender(page, index) && typeof window !== 'undefined'){
return this.renderPage(page, index);
} else {
return this.renderDummyPage(index);
@@ -120,29 +146,67 @@ const BrewRenderer = createClass({
}
if(this.props.errors && this.props.errors.length) return this.lastRender;
this.lastRender = _.map(this.state.pages, (page, index)=>{
return this.renderPage(page, index);
if(typeof window !== 'undefined') {
return this.renderPage(page, index);
} 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(){
//render in iFrame so broken code doesn't crash the site.
//Also render dummy page while iframe is mounting.
return (
<React.Fragment>
<div className='brewRenderer'
onScroll={this.handleScroll}
ref='main'
style={{ height: this.state.height }}>
<ErrorBar errors={this.props.errors} />
<div className='popups'>
<RenderWarnings />
<NotificationPopup />
{!this.state.isMounted
? <div className='brewRenderer' onScroll={this.handleScroll}>
<div className='pages' ref='pages'>
{this.renderDummyPage(1)}
</div>
</div>
: null}
<div className='pages' ref='pages'>
{this.renderPages()}
<Frame initialContent={this.state.initialContent}
head = <link href={`${this.props.renderer == 'legacy' ? '/themes/5ePhbLegacy.style.css' : '/themes/5ePhb.style.css'}`} rel='stylesheet'/>
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'>
{/* Apply CSS from Style tab and render pages from Markdown tab */}
{this.state.isMounted
&&
<>
{this.renderStyle()}
{this.renderPages()}
</>
}
</div>
</div>
</div>;
</Frame>
{this.renderPageInfo()}
{this.renderPPRmsg()}
</React.Fragment>

View File

@@ -1,14 +1,11 @@
@import (multiple, less) 'shared/naturalcrit/styles/reset.less';
@import (less) './client/homebrew/phbStyle/phb.style.less';
.pane{
position : relative;
}
.brewRenderer{
will-change : transform;
overflow-y : scroll;
.pages{
margin : 30px 0px;
&>.phb{
&>.page{
margin-right : auto;
margin-bottom : 30px;
margin-left : auto;
@@ -16,6 +13,9 @@
}
}
}
.pane{
position : relative;
}
.pageInfo{
position : absolute;
right : 17px;
@@ -37,4 +37,4 @@
font-size : 10px;
font-weight : 800;
color : white;
}
}

View File

@@ -62,7 +62,7 @@ const ErrorBar = createClass({
if(!this.props.errors.length) return null;
return <div className='errorBar'>
<i className='fa fa-exclamation-triangle' />
<i className='fas 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()}

View File

@@ -4,7 +4,7 @@ const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames'); //Unused variable
const DISMISS_KEY = 'dismiss_notification7-24-19';
const DISMISS_KEY = 'dismiss_notification7-10-20';
const NotificationPopup = createClass({
getInitialState : function() {
@@ -22,17 +22,22 @@ const NotificationPopup = createClass({
notifications : {
psa : function(){
return <li key='psa'>
<em>Known bug: Grey Shadow Boxes </em> <br />
The shadows around certain brew elements such as notes and statblocks might appear as a solid grey box when generating a PDF. &nbsp;
<a target='_blank' href='https://old.reddit.com/r/homebrewery/comments/ch3v0d/psa_grey_boxesshadows_around_notes_stat_blocks_etc/'>
See this Reddit post
</a> for updates and possible workarounds.
<em>Google Drive Integration!</em> <br />
We have added Google Drive integration to the Homebrewery! <a target='_blank' href='https://www.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 />
At the moment we do not save a history of your projects, so please make frequent backups of your brews! &nbsp;
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!
@@ -55,8 +60,8 @@ const NotificationPopup = createClass({
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' />
<i className='fas fa-times dismiss' onClick={this.dismiss}/>
<i className='fas 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>

View File

@@ -9,11 +9,11 @@
.notificationPopup{
position : relative;
float : right;
display : inline-block;
display : inline-block;
width : 350px;
padding : 20px;
padding : 15px;
padding-bottom : 10px;
padding-left : 85px;
padding-left : 55px;
background-color : @blue;
color : white;
a{
@@ -22,8 +22,8 @@
}
i.info{
position : absolute;
top : 24px;
left : 24px;
top : 12px;
left : 12px;
opacity : 0.8;
font-size : 2.5em;
}

View File

@@ -3,95 +3,166 @@ const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const dedent = require('dedent-tabs').default;
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
const SnippetBar = require('./snippetbar/snippetbar.jsx');
const MetadataEditor = require('./metadataEditor/metadataEditor.jsx');
const SNIPPETBAR_HEIGHT = 25;
const DEFAULT_STYLE_TEXT = dedent`
/*=======--- Example CSS styling ---=======*/
/* Any CSS here will apply to your document! */
.myExampleClass {
color: black;
}`;
const splice = function(str, index, inject){
return str.slice(0, index) + inject + str.slice(index);
};
const SNIPPETBAR_HEIGHT = 25;
const Editor = createClass({
getDefaultProps : function() {
return {
value : '',
onChange : ()=>{},
brew : {
text : '',
style : ''
},
metadata : {},
onMetadataChange : ()=>{},
onTextChange : ()=>{},
onStyleChange : ()=>{},
onMetaChange : ()=>{},
renderer : 'legacy'
};
},
getInitialState : function() {
return {
showMetadataEditor : false
view : 'text' //'text', 'style', 'meta'
};
},
cursorPosition : {
line : 0,
ch : 0
},
isText : function() {return this.state.view == 'text';},
isStyle : function() {return this.state.view == 'style';},
isMeta : function() {return this.state.view == 'meta';},
componentDidMount : function() {
this.updateEditorSize();
this.highlightPageLines();
this.highlightCustomMarkdown();
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);
if(this.refs.codeEditor) {
let paneHeight = this.refs.main.parentNode.clientHeight;
paneHeight -= SNIPPETBAR_HEIGHT + 1;
this.refs.codeEditor.codeMirror.setSize(null, paneHeight);
}
},
handleTextChange : function(text){
this.props.onChange(text);
},
handleCursorActivty : function(curpos){
this.cursorPosition = curpos;
},
handleInject : function(injectText){
const lines = this.props.value.split('\n');
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
const text = (this.isText() ? this.props.brew.text : this.props.brew.style);
this.handleTextChange(lines.join('\n'));
this.refs.codeEditor.setCursorPosition(this.cursorPosition.line, this.cursorPosition.ch + injectText.length);
const lines = text.split('\n');
const cursorPos = this.refs.codeEditor.getCursorPosition();
lines[cursorPos.line] = splice(lines[cursorPos.line], cursorPos.ch, injectText);
this.refs.codeEditor.setCursorPosition(cursorPos.line + injectText.split('\n').length, cursorPos.ch + injectText.length);
if(this.isText()) this.props.onTextChange(lines.join('\n'));
if(this.isStyle()) this.props.onStyleChange(lines.join('\n'));
},
handgleToggle : function(){
handleViewChange : function(newView){
this.setState({
showMetadataEditor : !this.state.showMetadataEditor
});
view : newView
}, this.updateEditorSize); //TODO: not sure if updateeditorsize needed
},
getCurrentPage : function(){
const lines = this.props.value.split('\n').slice(0, this.cursorPosition.line + 1);
const lines = this.props.brew.text.split('\n').slice(0, this.cursorPosition.line + 1);
return _.reduce(lines, (r, line)=>{
if(line.indexOf('\\page') !== -1) r++;
return r;
}, 1);
},
highlightPageLines : function(){
highlightCustomMarkdown : function(){
if(!this.refs.codeEditor) return;
const codeMirror = this.refs.codeEditor.codeMirror;
if(this.state.view === 'text') {
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;
//reset custom text styles
const customHighlights = codeMirror.getAllMarks();
for (let i=0;i<customHighlights.length;i++) customHighlights[i].clear();
const lineNumbers = _.reduce(this.props.brew.text.split('\n'), (r, line, lineNumber)=>{
//reset custom line styles
codeMirror.removeLineClass(lineNumber, 'background');
codeMirror.removeLineClass(lineNumber, 'text');
// Legacy Codemirror styling
if(this.props.renderer == 'legacy') {
if(line.includes('\\page')){
codeMirror.addLineClass(lineNumber, 'background', 'pageLine');
r.push(lineNumber);
}
}
// New Codemirror styling for V3 renderer
if(this.props.renderer == 'V3') {
if(line.startsWith('\\page')){
codeMirror.addLineClass(lineNumber, 'background', 'pageLine');
r.push(lineNumber);
}
if(line.match(/^\\column$/)){
codeMirror.addLineClass(lineNumber, 'text', 'columnSplit');
r.push(lineNumber);
}
// Highlight inline spans {{content}}
if(line.includes('{{') && line.includes('}}')){
const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g;
let match;
let blockCount = 0;
while ((match = regex.exec(line)) != null) {
if(match[0].startsWith('{')) {
blockCount += 1;
} else {
blockCount -= 1;
}
if(blockCount < 0) {
blockCount = 0;
continue;
}
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' });
}
} else if(line.trimLeft().startsWith('{{') || line.trimLeft().startsWith('}}')){
// Highlight block divs {{\n Content \n}}
let endCh = line.length+1;
const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/);
if(match)
endCh = match.index+match[0].length;
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
}
}
return r;
}, []);
return lineNumbers;
}
},
brewJump : function(){
const currentPage = this.getCurrentPage();
window.location.hash = `p${currentPage}`;
@@ -99,40 +170,44 @@ const Editor = createClass({
//Called when there are changes to the editor's dimensions
update : function(){
this.refs.codeEditor.updateSize();
this.refs.codeEditor?.updateSize();
},
renderMetadataEditor : function(){
if(!this.state.showMetadataEditor) return;
return <MetadataEditor
metadata={this.props.metadata}
onChange={this.props.onMetadataChange}
/>;
renderEditor : function(){
if(this.isText()){
return <CodeEditor key='text'
ref='codeEditor'
language='gfm'
value={this.props.brew.text}
onChange={this.props.onTextChange} />;
}
if(this.isStyle()){
return <CodeEditor key='style'
ref='codeEditor'
language='css'
value={this.props.brew.style ?? DEFAULT_STYLE_TEXT}
onChange={this.props.onStyleChange} />;
}
if(this.isMeta()){
return <MetadataEditor
metadata={this.props.brew}
onChange={this.props.onMetaChange} />;
}
},
render : function(){
this.highlightPageLines();
this.highlightCustomMarkdown();
return (
<div className='editor' ref='main'>
<SnippetBar
brew={this.props.value}
brew={this.props.brew}
view={this.state.view}
onViewChange={this.handleViewChange}
onInject={this.handleInject}
onToggle={this.handgleToggle}
showmeta={this.state.showMetadataEditor} />
{this.renderMetadataEditor()}
<CodeEditor
ref='codeEditor'
wrap={true}
language='gfm'
value={this.props.value}
onChange={this.handleTextChange}
onCursorActivity={this.handleCursorActivty} />
showEditButtons={this.props.showEditButtons}
renderer={this.props.renderer} />
{/*
<div className='brewJump' onClick={this.brewJump}>
<i className='fa fa-arrow-right' />
</div>
*/}
{this.renderEditor()}
</div>
);
}

View File

@@ -9,6 +9,22 @@
background-color : fade(#333, 15%);
border-bottom : #333 solid 1px;
}
.columnSplit{
font-style : italic;
color : grey;
background-color : fade(#299, 15%);
border-bottom : #299 solid 1px;
}
.block{
color : purple;
font-weight : bold;
//font-style: italic;
}
.inline-block{
color : red;
font-weight : bold;
//font-style: italic;
}
}
.brewJump{
@@ -26,4 +42,4 @@
.tooltipLeft("Jump to brew page");
}
}
}

View File

@@ -17,7 +17,8 @@ const MetadataEditor = createClass({
tags : '',
published : false,
authors : [],
systems : []
systems : [],
renderer : 'legacy'
},
onChange : ()=>{}
};
@@ -36,6 +37,12 @@ const MetadataEditor = createClass({
}
this.props.onChange(this.props.metadata);
},
handleRenderer : function(renderer, e){
if(e.target.checked){
this.props.metadata.renderer = renderer;
}
this.props.onChange(this.props.metadata);
},
handlePublish : function(val){
this.props.onChange(_.merge({}, this.props.metadata, {
published : val
@@ -43,7 +50,7 @@ const MetadataEditor = createClass({
},
handleDelete : function(){
if(this.props.metadata.authors.length <= 1){
if(this.props.metadata.authors && 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 {
@@ -60,10 +67,12 @@ const MetadataEditor = createClass({
getRedditLink : function(){
const meta = this.props.metadata;
const shareLink = (meta.googleId || '') + meta.shareId;
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})**`;
**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`;
return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`;
},
@@ -83,11 +92,11 @@ const MetadataEditor = createClass({
renderPublish : function(){
if(this.props.metadata.published){
return <button className='unpublish' onClick={()=>this.handlePublish(false)}>
<i className='fa fa-ban' /> unpublish
<i className='fas fa-ban' /> unpublish
</button>;
} else {
return <button className='publish' onClick={()=>this.handlePublish(true)}>
<i className='fa fa-globe' /> publish
<i className='fas fa-globe' /> publish
</button>;
}
},
@@ -99,7 +108,7 @@ const MetadataEditor = createClass({
<label>delete</label>
<div className='value'>
<button className='publish' onClick={this.handleDelete}>
<i className='fa fa-trash' /> delete brew
<i className='fas fa-trash-alt' /> delete brew
</button>
</div>
</div>;
@@ -107,7 +116,7 @@ const MetadataEditor = createClass({
renderAuthors : function(){
let text = 'None.';
if(this.props.metadata.authors.length){
if(this.props.metadata.authors && this.props.metadata.authors.length){
text = this.props.metadata.authors.join(', ');
}
return <div className='field authors'>
@@ -126,13 +135,42 @@ const MetadataEditor = createClass({
<div className='value'>
<a href={this.getRedditLink()} target='_blank' rel='noopener noreferrer'>
<button className='publish'>
<i className='fa fa-reddit-alien' /> share to reddit
<i className='fab fa-reddit-alien' /> share to reddit
</button>
</a>
</div>
</div>;
},
renderRenderOptions : function(){
if(!global.enable_v3) return;
return <div className='field systems'>
<label>Renderer</label>
<div className='value'>
<label key='legacy'>
<input
type='radio'
value = 'legacy'
name = 'renderer'
checked={this.props.metadata.renderer === 'legacy'}
onChange={(e)=>this.handleRenderer('legacy', e)} />
Legacy
</label>
<label key='V3'>
<input
type='radio'
value = 'V3'
name = 'renderer'
checked={this.props.metadata.renderer === 'V3'}
onChange={(e)=>this.handleRenderer('V3', e)} />
V3
</label>
</div>
</div>;
},
render : function(){
return <div className='metadataEditor'>
<div className='field title'>
@@ -154,6 +192,8 @@ const MetadataEditor = createClass({
</div>
*/}
{this.renderAuthors()}
<div className='field systems'>
<label>systems</label>
<div className='value'>
@@ -161,7 +201,7 @@ const MetadataEditor = createClass({
</div>
</div>
{this.renderAuthors()}
{this.renderRenderOptions()}
<div className='field publish'>
<label>publish</label>

View File

@@ -18,10 +18,11 @@
font-weight : 800;
line-height : 1.8em;
text-transform : uppercase;
flex-grow : 0;
flex : 0 0 auto;
}
&>.value{
flex-grow : 1;
flex : 1 1 auto;
min-width : 200px;
}
}
.description.field textarea.value{
@@ -38,15 +39,22 @@
font-size : 0.7em;
font-weight : 800;
user-select : none;
white-space : nowrap;
display : inline-flex;
align-items : center;
}
input{
vertical-align : middle;
cursor : pointer;
margin : 3px;
}
}
.publish.field .value{
position : relative;
margin-bottom: 15px;
button{
width:100%;
}
button.publish{
.button(@blueLight);
}
@@ -76,4 +84,4 @@
font-size: 0.8em;
line-height : 1.5em;
}
}
}

View File

@@ -5,22 +5,30 @@ const _ = require('lodash');
const cx = require('classnames');
const Snippets = require('./snippets/snippets.js');
const SnippetsLegacy = require('./snippetsLegacy/snippets.js');
const SnippetsV3 = 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
brew : {},
view : 'text',
onViewChange : ()=>{},
onInject : ()=>{},
onToggle : ()=>{},
showEditButtons : true,
renderer : 'legacy'
};
},
getInitialState : function() {
return {
renderer : this.props.renderer
};
},
@@ -29,7 +37,16 @@ const Snippetbar = createClass({
},
renderSnippetGroups : function(){
return _.map(Snippets, (snippetGroup)=>{
let snippets = [];
if(this.props.view === 'text') {
if(this.props.renderer === 'V3')
snippets = SnippetsV3;
else
snippets = SnippetsLegacy;
}
return _.map(snippets, (snippetGroup)=>{
return <SnippetGroup
brew={this.props.brew}
groupName={snippetGroup.groupName}
@@ -41,13 +58,29 @@ const Snippetbar = createClass({
});
},
renderEditorButtons : function(){
if(!this.props.showEditButtons) return;
return <div className='editors'>
<div className={cx('text', { selected: this.props.view === 'text' })}
onClick={()=>this.props.onViewChange('text')}>
<i className='fa fa-beer' />
</div>
<div className={cx('style', { selected: this.props.view === 'style' })}
onClick={()=>this.props.onViewChange('style')}>
<i className='fa fa-paint-brush' />
</div>
<div className={cx('meta', { selected: this.props.view === 'meta' })}
onClick={()=>this.props.onViewChange('meta')}>
<i className='fas fa-info-circle' />
</div>
</div>;
},
render : function(){
return <div className='snippetBar'>
{this.renderSnippetGroups()}
<div className={cx('toggleMeta', { selected: this.props.showmeta })}
onClick={this.props.onToggle}>
<i className='fa fa-bars' />
</div>
{this.renderEditorButtons()}
</div>;
}
});
@@ -62,9 +95,9 @@ module.exports = Snippetbar;
const SnippetGroup = createClass({
getDefaultProps : function() {
return {
brew : '',
brew : {},
groupName : '',
icon : 'fa-rocket',
icon : 'fas fa-rocket',
snippets : [],
onSnippetClick : function(){},
};
@@ -75,16 +108,16 @@ const SnippetGroup = createClass({
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}`} />
<i className={snippet.icon} />
{snippet.name}
</div>;
});
},
render : function(){
return <div className='snippetGroup'>
return <div className='snippetGroup snippetBarButton'>
<div className='text'>
<i className={`fa fa-fw ${this.props.icon}`} />
<i className={this.props.icon} />
<span className='groupName'>{this.props.groupName}</span>
</div>
<div className='dropdown'>

View File

@@ -1,47 +1,64 @@
.snippetBar{
@height : 25px;
@menuHeight : 25px;
position : relative;
height : @height;
height : @menuHeight;
background-color : #ddd;
.toggleMeta{
position : absolute;
top : 0px;
right : 0px;
height : @height;
width : @height;
cursor : pointer;
line-height : @height;
text-align : center;
.tooltipLeft("Edit Brew Metadata");
.editors{
position : absolute;
display : flex;
top : 0px;
right : 0px;
height : @menuHeight;
width : 90px;
justify-content : space-between;
&>div{
height : @menuHeight;
width : @menuHeight;
cursor : pointer;
line-height : @menuHeight;
text-align : center;
&:hover,&.selected{
background-color : #999;
}
&.text{
.tooltipLeft('Brew Editor');
}
&.style{
.tooltipLeft('Style Editor');
}
&.meta{
.tooltipLeft('Properties');
}
}
}
.snippetBarButton{
height : @menuHeight;
line-height : @menuHeight;
display : inline-block;
padding : 0px 5px;
font-weight : 800;
font-size : 0.625em;
text-transform : uppercase;
cursor : pointer;
&:hover, &.selected{
background-color : #999;
}
}
.snippetGroup{
display : inline-block;
height : @height;
padding : 0px 5px;
cursor : pointer;
font-size : 0.6em;
font-weight : 800;
line-height : @height;
text-transform : uppercase;
border-right : 1px solid black;
i{
vertical-align : middle;
margin-right : 3px;
font-size : 1.2em;
}
&:hover, &.selected{
background-color : #999;
}
.text{
line-height : @height;
.groupName{
font-size : 10px;
}
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;
@@ -62,7 +79,7 @@
font-size : 10px;
i{
margin-right : 8px;
font-size : 13px;
font-size : 1.2em;
}
&:hover{
background-color : #999;
@@ -70,4 +87,4 @@
}
}
}
}
}

View File

@@ -102,6 +102,13 @@ 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>

View File

@@ -47,11 +47,17 @@ const spellNames = [
'Ultimate Rite of the Confetti Angel',
'Ultimate Ritual of Mouthwash',
];
const itemNames = [
'Doorknob of Niceness',
'Paper Armor of Folding',
'Mixtape of Sadness',
'Staff of Endless Confetti',
];
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 levels = ['Cantrips (0 Level)', '1st 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)=>{
@@ -60,7 +66,7 @@ module.exports = {
return `##### ${level} \n${spells} \n`;
}).join('\n');
return `<div class='spellList'>\n${content}\n</div>`;
return `{{spellList\n${content}\n}}`;
},
spell : function(){
@@ -76,16 +82,28 @@ module.exports = {
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. ',
'**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 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');
},
item : function() {
return [
`#### ${_.sample(itemNames)}`,
`*${_.sample(['Wondrous item', 'Armor', 'Weapon'])}, ${_.sample(['Common', 'Uncommon', 'Rare', 'Very Rare', 'Legendary', 'Artifact'])} (requires attunement)*`,
`:`,
`This knob is pretty nice. When attached to a door, it allows a user to`,
`open that door with the strength of the nearest animal. For example, if`,
`there is a cow nearby, the user will have the "strength of a cow" while`,
`opening this door.`
].join('\n');
}
};
};

View File

@@ -1,4 +1,5 @@
const _ = require('lodash');
const dedent = require('dedent-tabs').default;
const genList = function(list, max){
return _.sampleSize(list, _.random(0, max)).join(', ') || 'None';
@@ -86,7 +87,7 @@ const getAlignment = function(){
};
const getStats = function(){
return `>|${_.times(6, function(){
return `|${_.times(6, function(){
const num = _.random(1, 20);
const mod = Math.ceil(num/2 - 5);
return `${num} (${mod >= 0 ? `+${mod}` : mod})`;
@@ -95,12 +96,12 @@ const getStats = function(){
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.',
'***Pack Tactics.*** These guys work together like peanut butter and jelly.',
'***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.*** Whenever this creature makes an attack, it starts telling you how much cooler it is than you.',
]);
};
@@ -133,68 +134,37 @@ const genAction = function(){
'Turnbuckle Roll'
]);
return `> ***${name}.*** *Melee Weapon Attack:* +4 to hit, reach 5ft., one target. *Hit* 5 (1d6 + 2) `;
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`;
monster : function(classes, genLines){
return dedent`
{{${classes}
## ${getMonsterName()}
*${getType()}, ${getAlignment()}*
___
**Armor Class** :: ${_.random(10, 20)} (chain mail, shield)
**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** :: darkvision 60 ft., passive Perception ${_.random(3, 20)}
**Languages** :: ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}
**Challenge** :: ${_.random(0, 15)} (${_.random(10, 10000)} XP)
___
:
${_.times(_.random(genLines, genLines + 2), function(){return genAbilities();}).join('\n\t\t\t\n\t\t\t')}
:
### Actions
${_.times(_.random(genLines, genLines + 2), function(){return genAction();}).join('\n\t\t\t\n\t\t\t')}
}}
\n`;
}
};

View File

@@ -6,79 +6,113 @@ const MonsterBlockGen = require('./monsterblock.gen.js');
const ClassFeatureGen = require('./classfeature.gen.js');
const CoverPageGen = require('./coverpage.gen.js');
const TableOfContentsGen = require('./tableOfContents.gen.js');
const dedent = require('dedent-tabs').default;
module.exports = [
{
groupName : 'Editor',
icon : 'fa-pencil',
icon : 'fas fa-pencil-alt',
snippets : [
{
name : 'Column Break',
icon : 'fa-columns',
gen : '```\n```\n\n'
icon : 'fas fa-columns',
gen : '\n\\column\n'
},
{
name : 'New Page',
icon : 'fa-file-text',
gen : '\\page\n\n'
icon : 'fas fa-file-alt',
gen : '\n\\page\n'
},
{
name : 'Vertical Spacing',
icon : 'fa-arrows-v',
gen : '<div style=\'margin-top:140px\'></div>\n\n'
icon : 'fas fa-arrows-alt-v',
gen : '\n::::\n'
},
{
name : 'Horizontal Spacing',
icon : 'fas fa-arrows-alt-h',
gen : ' {{width:100px}} '
},
{
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'
icon : 'fas fa-window-maximize',
gen : dedent`\n
{{wide
Everything in here will be extra wide. Tables, text, everything!
Beware though, CSS columns can behave a bit weird sometimes. You may
have to rely on the automatic column-break rather than \`\column\` if
you mix columns and wide blocks on the same page.
}}
\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')
icon : 'fas fa-image',
gen : dedent`
![cat warrior](https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg) {width:325px}
Credit: Kyounghwan Kim`
},
{
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')
icon : 'fas fa-tree',
gen : `![homebrew mug](http://i.imgur.com/hMna6G0.png) {position:absolute,top:50px,right:30px,width:280px}`
},
{
name : 'QR Code',
icon : 'fas fa-qrcode',
gen : (brew)=>{
return `![]` +
`(https://api.qrserver.com/v1/create-qr-code/?data=` +
`https://homebrewery.naturalcrit.com/share/${brew.shareId}` +
`&amp;size=100x100) {width:100px;mix-blend-mode:multiply}`;
}
},
{
name : 'Page Number',
icon : 'fa-bookmark',
gen : '<div class=\'pageNumber\'>1</div>\n<div class=\'footnote\'>PART 1 | FANCINESS</div>\n\n'
icon : 'fas fa-bookmark',
gen : '{{pageNumber 1}}\n{{footnote PART 1 | SECTION NAME}}\n\n'
},
{
name : 'Auto-incrementing Page Number',
icon : 'fa-sort-numeric-asc',
gen : '<div class=\'pageNumber auto\'></div>\n'
icon : 'fas fa-sort-numeric-down',
gen : '{{pageNumber,auto}}\n{{footnote PART 1 | SECTION NAME}}\n\n'
},
{
name : 'Link to page',
icon : 'fa-link',
icon : 'fas fa-link',
gen : '[Click here](#p3) to go to page 3\n'
},
{
name : 'Table of Contents',
icon : 'fa-book',
icon : 'fas fa-book',
gen : TableOfContentsGen
},
{
name : 'Remove Drop Cap',
icon : 'fas fa-remove-format',
gen : '<style>\n' +
' .phb3 h1+p:first-letter {\n' +
' all: unset;\n' +
' }\n' +
'</style>'
},
{
name : 'Tweak Drop Cap',
icon : 'fas fa-sliders-h',
gen : '<style>\n' +
' /* Drop Cap settings */\n' +
' .phb3 h1 + p::first-letter {\n' +
' float: left;\n' +
' font-family: SolberaImitationRemake;\n' +
' font-size: 3.5cm;\n' +
' color: #222;\n' +
' line-height: .8em;\n' +
' }\n' +
'</style>'
},
]
},
@@ -87,64 +121,76 @@ module.exports = [
{
groupName : 'PHB',
icon : 'fa-book',
icon : 'fas fa-book',
snippets : [
{
name : 'Spell',
icon : 'fa-magic',
icon : 'fas fa-magic',
gen : MagicGen.spell,
},
{
name : 'Spell List',
icon : 'fa-list',
icon : 'fas fa-scroll',
gen : MagicGen.spellList,
},
{
name : 'Class Feature',
icon : 'fa-trophy',
icon : 'fas fa-mask',
gen : ClassFeatureGen,
},
{
name : 'Note',
icon : 'fa-sticky-note',
icon : 'fas 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');
return dedent`
{{note
##### Time to Drop Knowledge
Use notes to point out some interesting information.
**Tables and lists** both work within a note.
}}
\n`;
},
},
{
name : 'Descriptive Text Box',
icon : 'fa-sticky-note-o',
icon : 'fas fa-comment-alt',
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');
return dedent`
{{descriptive
##### Time to Drop Knowledge
Use descriptive boxes to highlight text that should be read aloud.
**Tables and lists** both work within a descriptive box.
}}
\n`;
},
},
{
name : 'Monster Stat Block (unframed)',
icon : 'fas fa-paw',
gen : MonsterBlockGen.monster('monster', 2),
},
{
name : 'Monster Stat Block',
icon : 'fa-bug',
gen : MonsterBlockGen.half,
icon : 'fas fa-spider',
gen : MonsterBlockGen.monster('monster,frame', 2),
},
{
name : 'Wide Monster Stat Block',
icon : 'fa-paw',
gen : MonsterBlockGen.full,
icon : 'fas fa-dragon',
gen : MonsterBlockGen.monster('monster,frame,wide', 4),
},
{
name : 'Cover Page',
icon : 'fa-file-word-o',
icon : 'fas fa-file-word',
gen : CoverPageGen,
},
{
name : 'Magic Item',
icon : 'fas fa-hat-wizard',
gen : MagicGen.item,
},
]
},
@@ -154,79 +200,77 @@ module.exports = [
{
groupName : 'Tables',
icon : 'fa-table',
icon : 'fas fa-table',
snippets : [
{
name : 'Class Table',
icon : 'fa-table',
icon : 'fas fa-table',
gen : ClassTableGen.full,
},
{
name : 'Half Class Table',
icon : 'fa-list-alt',
icon : 'fas fa-list-alt',
gen : ClassTableGen.half,
},
{
name : 'Table',
icon : 'fa-th-list',
icon : 'fas 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');
},
return dedent`
##### Character Advancement
| Experience Points | Level | Proficiency Bonus |
|:------------------|:-----:|:-----------------:|
| 0 | 1 | +2 |
| 300 | 2 | +2 |
| 900 | 3 | +2 |
| 2,700 | 4 | +2 |
| 6,500 | 5 | +3 |
| 14,000 | 6 | +3 |
\n`;
}
},
{
name : 'Wide Table',
icon : 'fa-list',
icon : 'fas 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');
},
return dedent`
{{wide
##### Weapons
| Name | Cost | Damage | Weight | Properties |
|:------------------------|:-----:|:----------------|--------:|:-----------|
| *Simple Melee Weapons* | | | | |
| &emsp; Club | 1 sp | 1d4 bludgeoning | 2 lb. | Light |
| &emsp; Dagger | 2 gp | 1d4 piercing | 1 lb. | Finesse |
| &emsp; Spear | 1 gp | 1d6 piercing | 3 lb. | Thrown |
| *Simple Ranged Weapons* | | | | |
| &emsp; Dart | 5 cp | 1d4 piercig | 1/4 lb. | Finesse |
| &emsp; Shortbow | 25 gp | 1d6 piercing | 2 lb. | Ammunition |
| &emsp; Sling | 1 sp | 1d4 bludgeoning | &mdash; | Ammunition |
}}
\n`;
}
},
{
name : 'Split Table',
icon : 'fa-th-large',
icon : 'fas 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');
},
return dedent`
##### Typical Difficulty Classes
{{column-count:2
| Task Difficulty | DC |
|:----------------|:--:|
| Very easy | 5 |
| Easy | 10 |
| Medium | 15 |
| Task Difficulty | DC |
|:------------------|:--:|
| Hard | 20 |
| Very hard | 25 |
| Nearly impossible | 30 |
}}
\n`;
}
}
]
},
@@ -238,11 +282,11 @@ module.exports = [
{
groupName : 'Print',
icon : 'fa-print',
icon : 'fas fa-print',
snippets : [
{
name : 'A4 PageSize',
icon : 'fa-file-o',
icon : 'far fa-file',
gen : ['<style>',
' .phb{',
' width : 210mm;',
@@ -253,7 +297,7 @@ module.exports = [
},
{
name : 'Ink Friendly',
icon : 'fa-tint',
icon : 'fas fa-tint',
gen : ['<style>',
' .phb{ background : white;}',
' .phb img{ display : none;}',

View File

@@ -1,4 +1,5 @@
const _ = require('lodash');
const dedent = require('dedent-tabs').default;
const getTOC = (pages)=>{
const add1 = (title, page)=>{
@@ -9,7 +10,7 @@ const getTOC = (pages)=>{
});
};
const add2 = (title, page)=>{
if(!_.last(res)) add1('', page);
if(!_.last(res)) add1(null, page);
_.last(res).children.push({
title : title,
page : page + 1,
@@ -17,8 +18,8 @@ const getTOC = (pages)=>{
});
};
const add3 = (title, page)=>{
if(!_.last(res)) add1('', page);
if(!_.last(_.last(res).children)) add2('', page);
if(!_.last(res)) add1(null, page);
if(!_.last(_.last(res).children)) add2(null, page);
_.last(_.last(res).children).children.push({
title : title,
page : page + 1,
@@ -48,16 +49,24 @@ const getTOC = (pages)=>{
};
module.exports = function(brew){
const pages = brew.split('\\page');
const pages = brew.text.split('\\page');
const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`);
if(g1.title !== null) {
r.push(`\t\t- ### [{{ ${g1.title}}}{{ ${g1.page}}}](#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.title !== null) {
r.push(`\t\t - #### [{{ ${g2.title}}}{{ ${g2.page}}}](#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})`);
if(g2.title !== null) {
r.push(`\t\t - [{{ ${g3.title}}}{{ ${g3.page}}}](#p${g3.page})`);
} else { // Don't over-indent if no level-2 parent entry
r.push(`\t\t - [{{ ${g3.title}}}{{ ${g3.page}}}](#p${g3.page})`);
}
});
}
});
@@ -65,8 +74,11 @@ module.exports = function(brew){
return r;
}, []).join('\n');
return `<div class='toc'>
##### Table Of Contents
return dedent`
{{toc,wide
# Table Of Contents
${markdown}
</div>\n`;
};
}}
\n`;
};

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,117 @@
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; }
</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 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,289 @@
/* 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 : 'fas fa-pencil-alt',
snippets : [
{
name : 'Column Break',
icon : 'fas fa-columns',
gen : '```\n```\n\n'
},
{
name : 'New Page',
icon : 'fas fa-file-alt',
gen : '\\page\n\n'
},
{
name : 'Vertical Spacing',
icon : 'fas fa-arrows-alt-v',
gen : '<div style=\'margin-top:140px\'></div>\n\n'
},
{
name : 'Wide Block',
icon : 'fas fa-arrows-alt-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 : 'fas 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 : 'fas 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 : 'fas fa-bookmark',
gen : '<div class=\'pageNumber\'>1</div>\n<div class=\'footnote\'>PART 1 | FANCINESS</div>\n\n'
},
{
name : 'Auto-incrementing Page Number',
icon : 'fas fa-sort-numeric-down',
gen : '<div class=\'pageNumber auto\'></div>\n'
},
{
name : 'Link to page',
icon : 'fas fa-link',
gen : '[Click here](#p3) to go to page 3\n'
},
{
name : 'Table of Contents',
icon : 'fas fa-book',
gen : TableOfContentsGen
},
{
name : 'Remove Drop Cap',
icon : 'fas fa-remove-format',
gen : '<style>\n' +
' .phb h1+p:first-letter {\n' +
' all: unset;\n' +
' }\n' +
'</style>'
},
{
name : 'Tweak Drop Cap',
icon : 'fas fa-sliders-h',
gen : '<style>\n' +
' /* Drop Cap settings */\n' +
' .phb h1 + p::first-letter {\n' +
' float: left;\n' +
' font-family: Solberry;\n' +
' font-size: 10em;\n' +
' color: #222;\n' +
' line-height: .8em;\n' +
' }\n' +
'</style>'
},
]
},
/************************* PHB ********************/
{
groupName : 'PHB',
icon : 'fas fa-book',
snippets : [
{
name : 'Spell',
icon : 'fas fa-magic',
gen : MagicGen.spell,
},
{
name : 'Spell List',
icon : 'fas fa-list',
gen : MagicGen.spellList,
},
{
name : 'Class Feature',
icon : 'fas fa-trophy',
gen : ClassFeatureGen,
},
{
name : 'Note',
icon : 'fas 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 : 'far fa-sticky-note',
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 : 'fas fa-bug',
gen : MonsterBlockGen.half,
},
{
name : 'Wide Monster Stat Block',
icon : 'fas fa-paw',
gen : MonsterBlockGen.full,
},
{
name : 'Cover Page',
icon : 'far fa-file-word',
gen : CoverPageGen,
},
]
},
/********************* TABLES *********************/
{
groupName : 'Tables',
icon : 'fas fa-table',
snippets : [
{
name : 'Class Table',
icon : 'fas fa-table',
gen : ClassTableGen.full,
},
{
name : 'Half Class Table',
icon : 'fas fa-list-alt',
gen : ClassTableGen.half,
},
{
name : 'Table',
icon : 'fas 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 : 'fas 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 : 'fas 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 : 'fas fa-print',
snippets : [
{
name : 'A4 PageSize',
icon : 'far fa-file',
gen : ['<style>',
' .phb{',
' width : 210mm;',
' height : 296.8mm;',
' }',
'</style>'
].join('\n')
},
{
name : 'Ink Friendly',
icon : 'fas 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.text.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`;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,8 +1,6 @@
require('./homebrew.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const { StaticRouter:Router, Switch, Route } = require('react-router-dom');
const queryString = require('query-string');
@@ -22,6 +20,7 @@ const Homebrew = createClass({
changelog : '',
version : '0.0.0',
account : null,
enable_v3 : false,
brew : {
title : '',
text : '',
@@ -35,7 +34,7 @@ const Homebrew = createClass({
componentWillMount : function() {
global.account = this.props.account;
global.version = this.props.version;
global.enable_v3 = this.props.enable_v3;
},
render : function (){
return (
@@ -44,12 +43,13 @@ const Homebrew = createClass({
<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='/new/:id' component={(routeProps)=><NewPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/new' exact component={(routeProps)=><NewPage />}/>
<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}/>}/>
<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='/changelog' exact component={()=><SharePage brew={this.props.brew} />}/>
<Route path='/' component={()=><HomePage brew={this.props.brew} />}/>
</Switch>
</div>
</Router>

View File

@@ -1,7 +1,7 @@
@import 'naturalcrit/styles/core.less';
.homebrew{
height : 100%;
.page{
.sitePage{
display : flex;
height : 100%;
background-color : @steel;

View File

@@ -2,17 +2,33 @@ const React = require('react');
const createClass = require('create-react-class');
const Nav = require('naturalcrit/nav/nav.jsx');
module.exports = function(props){
if(global.account){
return <Nav.item href={`/user/${global.account.username}`} color='yellow' icon='fa-user'>
{global.account.username}
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='fas fa-user'>
{global.account.username}
</Nav.item>;
}
return <Nav.item href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`} color='teal' icon='fas fa-sign-in-alt'>
login
</Nav.item>;
}
let url = '';
if(typeof window !== 'undefined'){
url = window.location.href;
}
return <Nav.item href={`http://naturalcrit.com/login?redirect=${url}`} color='teal' icon='fa-sign-in'>
login
</Nav.item>;
};
});
module.exports = Account;

View File

@@ -1,6 +1,5 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const Nav = require('naturalcrit/nav/nav.jsx');
@@ -31,4 +30,4 @@ const EditTitle = createClass({
});
module.exports = EditTitle;
module.exports = EditTitle;

View File

@@ -6,8 +6,8 @@ module.exports = function(props){
return <Nav.item
newTab={true}
color='red'
icon='fa-bug'
icon='fas fa-bug'
href={`https://www.reddit.com/r/homebrewery/submit?selftext=true&title=${encodeURIComponent('[Issue] Describe Your Issue Here')}`} >
report issue
</Nav.item>;
};
};

View File

@@ -1,9 +1,9 @@
require('./navbar.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const Nav = require('naturalcrit/nav/nav.jsx');
const PatreonNavItem = require('./patreon.navitem.jsx');
const Navbar = createClass({
getInitialState : function() {
@@ -40,7 +40,7 @@ const Navbar = createClass({
<div>The Homebrewery</div>
</Nav.item>
<Nav.item>{`v${this.state.ver}`}</Nav.item>
<PatreonNavItem />
{/*this.renderChromeWarning()*/}
</Nav.section>
{this.props.children}

View File

@@ -1,4 +1,12 @@
@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);
@@ -47,11 +55,16 @@
text-transform : initial;
}
.patreon.navItem{
border-left : 1px solid #666;
border-right : 1px solid #666;
&:hover i {
color: red;
}
i{
.animate(color);
&:hover{
color : @red;
}
animation-name: coloring;
animation-duration: 2s;
color: pink;
}
}
.recent.navItem{
@@ -125,4 +138,4 @@
text-align : center;
}
}
}
}

View File

@@ -0,0 +1,11 @@
const React = require('react');
const Nav = require('naturalcrit/nav/nav.jsx');
module.exports = function(props){
return <Nav.item
href='/new'
color='purple'
icon='fas fa-plus-square'>
new
</Nav.item>;
};

View File

@@ -1,14 +1,13 @@
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'>
icon='fas fa-heart'>
help out
</Nav.item>;
};
};

View File

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

View File

@@ -35,24 +35,32 @@ const RecentItems = createClass({
//== 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 !== this.props.brew.editId;
return brew.id !== editId;
});
edited.unshift({
id : this.props.brew.editId,
id : editId,
title : this.props.brew.title,
url : `/edit/${this.props.brew.editId}`,
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 !== this.props.brew.shareId;
return brew.id !== shareId;
});
viewed.unshift({
id : this.props.brew.shareId,
id : shareId,
title : this.props.brew.title,
url : `/share/${this.props.brew.shareId}`,
url : `/share/${shareId}`,
ts : Date.now()
});
}
@@ -70,6 +78,41 @@ const RecentItems = createClass({
});
},
componentDidUpdate : function(prevProps) {
if(prevProps.brew && this.props.brew.editId !== prevProps.brew.editId) {
let edited = JSON.parse(localStorage.getItem(EDIT_KEY) || '[]');
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
@@ -101,7 +144,7 @@ const RecentItems = createClass({
},
render : function(){
return <Nav.item icon='fa-clock-o' color='grey' className='recent'
return <Nav.item icon='fas fa-history' color='grey' className='recent'
onMouseEnter={()=>this.handleDropdown(true)}
onMouseLeave={()=>this.handleDropdown(false)}>
{this.props.text}
@@ -140,4 +183,4 @@ module.exports = {
showView={true}
/>;
}
};
};

View File

@@ -1,10 +1,6 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
//var striptags = require('striptags');
const Nav = require('naturalcrit/nav/nav.jsx');
const MAX_URL_SIZE = 2083;
@@ -31,10 +27,7 @@ const RedditShare = createClass({
const url = [
MAIN_URL,
`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');
@@ -49,4 +42,4 @@ const RedditShare = createClass({
});
module.exports = RedditShare;
module.exports = RedditShare;

View File

@@ -1,14 +1,15 @@
/* eslint-disable max-lines */
require('./editPage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const request = require('superagent');
const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
const ReportIssue = require('../../navbar/issue.navitem.jsx');
const PrintLink = require('../../navbar/print.navitem.jsx');
const Account = require('../../navbar/account.navitem.jsx');
@@ -20,42 +21,58 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const Markdown = require('naturalcrit/markdown.js');
const SAVE_TIMEOUT = 3000;
const googleDriveActive = require('../../googleDrive.png');
const googleDriveInactive = require('../../googleDriveMono.png');
const SAVE_TIMEOUT = 3000;
const EditPage = createClass({
getDefaultProps : function() {
return {
brew : {
text : '',
style : '',
shareId : null,
editId : null,
createdAt : null,
updatedAt : null,
gDrive : false,
trashed : false,
title : '',
description : '',
tags : '',
published : false,
authors : [],
systems : []
systems : [],
renderer : 'legacy'
}
};
},
getInitialState : function() {
return {
brew : this.props.brew,
isSaving : false,
isPending : false,
errors : null,
htmlErrors : Markdown.validate(this.props.brew.text),
brew : this.props.brew,
isSaving : false,
isPending : false,
alertTrashedGoogleBrew : this.props.brew.trashed,
alertLoginToTransfer : 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.setState({
url : window.location.href
});
this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy
this.trySave();
window.onbeforeunload = ()=>{
if(this.state.isSaving || this.state.isPending){
@@ -74,13 +91,12 @@ const EditPage = createClass({
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.props.brew.shareId}?dialog=true`, '_blank').focus();
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();
@@ -91,17 +107,8 @@ const EditPage = createClass({
this.refs.editor.update();
},
handleMetadataChange : function(metadata){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, metadata),
isPending : true,
}), ()=>this.trySave());
},
handleTextChange : function(text){
//If there are errors, run the validator on everychange to give quick feedback
//If there are errors, run the validator on every change to give quick feedback
let htmlErrors = this.state.htmlErrors;
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
@@ -112,9 +119,23 @@ const EditPage = createClass({
}), ()=>this.trySave());
},
handleStyleChange : function(style){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { style: style }),
isPending : true
}), ()=>this.trySave());
},
handleMetaChange : function(metadata){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, metadata),
isPending : true,
}), ()=>this.trySave());
},
hasChanges : function(){
const savedBrew = this.savedBrew ? this.savedBrew : this.props.brew;
return !_.isEqual(this.state.brew, savedBrew);
return !_.isEqual(this.state.brew, this.savedBrew);
},
trySave : function(){
@@ -126,7 +147,45 @@ const EditPage = createClass({
}
},
save : function(){
handleGoogleClick : function(){
if(!global.account?.googleId) {
this.setState({
alertLoginToTransfer : true
});
return;
}
this.setState((prevState)=>({
confirmGoogleTransfer : !prevState.confirmGoogleTransfer
}));
this.clearErrors();
},
closeAlerts : function(event){
event.stopPropagation(); //Only handle click once so alert doesn't reopen
this.setState({
alertTrashedGoogleBrew : false,
alertLoginToTransfer : false,
confirmGoogleTransfer : false
});
},
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)=>({
@@ -135,22 +194,127 @@ const EditPage = createClass({
htmlErrors : Markdown.validate(prevState.brew.text)
}));
request
.put(`/api/${this.props.brew.editId}`)
.send(this.state.brew)
.end((err, res)=>{
if(err){
this.setState({
errors : err,
});
} else {
this.savedBrew = res.body;
this.setState({
isPending : false,
isSaving : false,
});
}
});
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 Transferring 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');
});
this.savedBrew = res.body;
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 });
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(){
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
{this.state.saveGoogle
? <img src={googleDriveActive} alt='googleDriveActive'/>
: <img src={googleDriveInactive} alt='googleDriveInactive'/>
}
{this.state.confirmGoogleTransfer &&
<div className='errorContainer' onClick={this.closeAlerts}>
{ this.state.saveGoogle
? `Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?`
: `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>
}
{this.state.alertLoginToTransfer &&
<div className='errorContainer' onClick={this.closeAlerts}>
You must be signed in to a Google account to transfer
between the homebrewery and Google Drive!
<a target='_blank' rel='noopener noreferrer'
href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
<div className='confirm'>
Sign In
</div>
</a>
<div className='deny'>
Not Now
</div>
</div>
}
</Nav.item>;
},
renderSaveButton : function(){
@@ -161,12 +325,31 @@ const EditPage = createClass({
errMsg += `\`\`\`\n${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``;
} catch (e){}
return <Nav.item className='save error' icon='fa-warning'>
if(this.state.errors.status == '401'){
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
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={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
<div className='confirm'>
Sign In
</div>
</a>
<div className='deny'>
Not Now
</div>
</div>
</Nav.item>;
}
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
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/stolksdorf/naturalcrit/issues/new?body=${encodeURIComponent(errMsg)}`}>
href={`https://github.com/naturalcrit/homebrewery/issues/new?body=${encodeURIComponent(errMsg)}`}>
here
</a>.
</div>
@@ -174,36 +357,56 @@ const EditPage = createClass({
}
if(this.state.isSaving){
return <Nav.item className='save' icon='fa-spinner fa-spin'>saving...</Nav.item>;
return <Nav.item className='save' icon='fas 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='fas 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>
{this.state.alertTrashedGoogleBrew &&
<div className='errorContainer' onClick={this.closeAlerts}>
This brew is currently in your Trash folder on Google Drive!<br />If you want to keep it, make sure to move it before it is deleted permanently!<br />
<div className='confirm'>
OK
</div>
</div>
}
<Nav.section>
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
{this.renderGoogleDriveIcon()}
{this.renderSaveButton()}
<NewBrew />
<ReportIssue />
<Nav.item newTab={true} href={`/share/${this.props.brew.shareId}`} color='teal' icon='fa-share-alt'>
<Nav.item newTab={true} href={`/share/${this.processShareId()}`} color='teal' icon='fas fa-share-alt'>
Share
</Nav.item>
<PrintLink shareId={this.props.brew.shareId} />
<RecentNavItem brew={this.props.brew} storageKey='edit' />
<PrintLink shareId={this.processShareId()} />
<RecentNavItem brew={this.state.brew} storageKey='edit' />
<Account />
</Nav.section>
</Navbar>;
},
render : function(){
return <div className='editPage page'>
return <div className='editPage sitePage'>
<Meta name='robots' content='noindex, nofollow' />
{this.renderNavbar()}
@@ -211,12 +414,13 @@ const EditPage = createClass({
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor
ref='editor'
value={this.state.brew.text}
onChange={this.handleTextChange}
metadata={this.state.brew}
onMetadataChange={this.handleMetadataChange}
brew={this.state.brew}
onTextChange={this.handleTextChange}
onStyleChange={this.handleStyleChange}
onMetaChange={this.handleMetaChange}
renderer={this.state.brew.renderer}
/>
<BrewRenderer text={this.state.brew.text} errors={this.state.htmlErrors} />
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} errors={this.state.htmlErrors} />
</SplitPane>
</div>
</div>;

View File

@@ -1,8 +1,14 @@
@keyframes glideDown {
0% {transform : translate(-50% + 3px, 0px);
opacity : 0;}
100% {transform : translate(-50% + 3px, 10px);
opacity : 1;}
}
.editPage{
.navItem.save{
width : 105px;
width : 106px;
text-align : center;
position : relative;
&.saved{
cursor : initial;
color : #666;
@@ -10,18 +16,84 @@
&.error{
position : relative;
background-color : @red;
.errorContainer{
position : absolute;
top : 29px;
left : -20px;
z-index : 1000;
width : 120px;
padding : 8px;
background-color : #333;
a{
color : @teal;
}
}
}
.googleDriveStorage {
position : relative;
}
.googleDriveStorage img{
height : 20px;
padding : 0px;
margin : -5px;
}
.errorContainer{
animation-name: glideDown;
animation-duration: 0.4s;
position : absolute;
top : 100%;
left : 50%;
z-index : 100000;
width : 140px;
padding : 3px;
color : white;
background-color : #333;
border : 3px solid #444;
border-radius : 5px;
transform : translate(-50% + 3px, 10px);
text-align : center;
font-size : 10px;
font-weight : 800;
text-transform : uppercase;
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

@@ -23,7 +23,7 @@ const ErrorPage = createClass({
text : '# Oops \n We could not find a brew with that id. **Sorry!**',
render : function(){
return <div className='errorPage page'>
return <div className='errorPage sitePage'>
<Navbar ver={this.props.ver}>
<Nav.section>
<Nav.item className='errorTitle'>

View File

@@ -1,14 +1,13 @@
require('./homePage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const request = require('superagent');
const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const PatreonNavItem = require('../../navbar/patreon.navitem.jsx');
const NewBrewItem = require('../../navbar/newbrew.navitem.jsx');
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const AccountNavItem = require('../../navbar/account.navitem.jsx');
@@ -23,21 +22,22 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const HomePage = createClass({
getDefaultProps : function() {
return {
welcomeText : '',
ver : '0.0.0'
brew : {
text : '',
},
ver : '0.0.0'
};
},
getInitialState : function() {
return {
text : this.props.welcomeText
brew : this.props.brew,
welcomeText : this.props.brew.text
};
},
handleSave : function(){
request.post('/api')
.send({
text : this.state.text
text : this.state.brew.text
})
.end((err, res)=>{
if(err) return;
@@ -50,46 +50,47 @@ const HomePage = createClass({
},
handleTextChange : function(text){
this.setState({
text : text
brew : { text: text }
});
},
renderNavbar : function(){
return <Navbar ver={this.props.ver}>
<Nav.section>
<PatreonNavItem />
<NewBrewItem />
<IssueNavItem />
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-file-text-o'>
<Nav.item newTab={true} href='/changelog' color='purple' icon='far fa-file-alt'>
Changelog
</Nav.item>
<RecentNavItem />
<AccountNavItem />
{/*}
<Nav.item href='/new' color='green' icon='fa-external-link'>
New Brew
</Nav.item>
*/}
</Nav.section>
</Navbar>;
},
render : function(){
return <div className='homePage page'>
return <div className='homePage sitePage'>
<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'/>
<BrewRenderer text={this.state.text} />
<Editor
ref='editor'
brew={this.state.brew}
onTextChange={this.handleTextChange}
renderer={this.state.brew.renderer}
showEditButtons={false}
/>
<BrewRenderer text={this.state.brew.text} />
</SplitPane>
</div>
<div className={cx('floatingSaveButton', { show: this.props.welcomeText != this.state.text })} onClick={this.handleSave}>
Save current <i className='fa fa-save' />
<div className={cx('floatingSaveButton', { show: this.state.welcomeText != this.state.brew.text })} onClick={this.handleSave}>
Save current <i className='fas fa-save' />
</div>
<a href='/new' className='floatingNewButton'>
Create your own <i className='fa fa-magic' />
Create your own <i className='fas fa-magic' />
</a>
</div>;
}

View File

@@ -1,8 +1,8 @@
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
require('./newPage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const request = require('superagent');
const Markdown = require('naturalcrit/markdown.js');
@@ -17,33 +17,70 @@ const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const BREWKEY = 'homebrewery-new';
const STYLEKEY = 'homebrewery-new-style';
const KEY = 'homebrewery-new';
const NewPage = createClass({
getInitialState : function() {
getDefaultProps : function() {
return {
metadata : {
brew : {
text : '',
style : undefined,
shareId : null,
editId : null,
createdAt : null,
updatedAt : null,
gDrive : false,
title : '',
description : '',
tags : '',
published : false,
authors : [],
systems : []
},
text : '',
isSaving : false,
errors : []
}
};
},
getInitialState : function() {
return {
brew : {
text : this.props.brew.text || '',
style : this.props.brew.style || undefined,
gDrive : false,
title : this.props.brew.title || '',
description : this.props.brew.description || '',
tags : this.props.brew.tags || '',
published : false,
authors : [],
systems : this.props.brew.systems || [],
renderer : this.props.brew.renderer || 'legacy'
},
isSaving : false,
saveGoogle : (global.account && global.account.googleId ? true : false),
errors : [],
htmlErrors : Markdown.validate(this.props.brew.text)
};
},
componentDidMount : function() {
const storage = localStorage.getItem(KEY);
if(storage){
this.setState({
text : storage
});
const brewStorage = localStorage.getItem(BREWKEY);
const styleStorage = localStorage.getItem(STYLEKEY);
const brew = this.state.brew;
if(!this.props.brew.text || !this.props.brew.style){
brew.text = this.props.brew.text || (brewStorage ?? '');
brew.style = this.props.brew.style || (styleStorage ?? undefined);
}
this.setState((prevState)=>({
brew : brew,
htmlErrors : Markdown.validate(prevState.brew.text)
}));
document.addEventListener('keydown', this.handleControlKeys);
},
componentWillUnmount : function() {
@@ -66,29 +103,66 @@ const NewPage = createClass({
this.refs.editor.update();
},
handleMetadataChange : function(metadata){
this.setState({
metadata : _.merge({}, this.state.metadata, metadata)
});
},
handleTextChange : function(text){
this.setState({
text : text,
errors : Markdown.validate(text)
});
localStorage.setItem(KEY, text);
//If there are errors, run the validator on every change to give quick feedback
let htmlErrors = this.state.htmlErrors;
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { text: text }),
htmlErrors : htmlErrors
}));
localStorage.setItem(BREWKEY, text);
},
save : function(){
handleStyleChange : function(style){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, { style: style }),
}));
localStorage.setItem(STYLEKEY, style);
},
handleMetaChange : function(metadata){
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, metadata),
}));
},
save : async function(){
this.setState({
isSaving : true
});
request.post('/api')
.send(_.merge({}, this.state.metadata, {
text : this.state.text
}))
console.log('saving new brew');
let brew = this.state.brew;
// Split out CSS to Style if CSS codefence exists
if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) {
const index = brew.text.indexOf('```\n\n');
brew.style = `${brew.style ? `${brew.style}\n` : ''}${brew.text.slice(7, index - 1)}`;
brew.text = brew.text.slice(index + 5);
};
if(this.state.saveGoogle) {
const res = await request
.post('/api/newGoogle/')
.send(brew)
.catch((err)=>{
console.log(err.status === 401
? 'Not signed in!'
: 'Error Creating New Google Brew!');
this.setState({ isSaving: false });
return;
});
brew = res.body;
localStorage.removeItem(BREWKEY);
localStorage.removeItem(STYLEKEY);
window.location = `/edit/${brew.googleId}${brew.editId}`;
} else {
request.post('/api')
.send(brew)
.end((err, res)=>{
if(err){
this.setState({
@@ -97,31 +171,33 @@ const NewPage = createClass({
return;
}
window.onbeforeunload = function(){};
const brew = res.body;
localStorage.removeItem(KEY);
brew = res.body;
localStorage.removeItem(BREWKEY);
localStorage.removeItem(STYLEKEY);
window.location = `/edit/${brew.editId}`;
});
}
},
renderSaveButton : function(){
if(this.state.isSaving){
return <Nav.item icon='fa-spinner fa-spin' className='saveButton'>
return <Nav.item icon='fas fa-spinner fa-spin' className='saveButton'>
save...
</Nav.item>;
} else {
return <Nav.item icon='fa-save' className='saveButton' onClick={this.save}>
return <Nav.item icon='fas fa-save' className='saveButton' onClick={this.save}>
save
</Nav.item>;
}
},
print : function(){
localStorage.setItem('print', this.state.text);
localStorage.setItem('print', `<style>\n${this.state.brew.style}\n</style>\n\n${this.state.brew.text}`);
window.open('/print?dialog=true&local=print', '_blank');
},
renderLocalPrintButton : function(){
return <Nav.item color='purple' icon='fa-file-pdf-o' onClick={this.print}>
return <Nav.item color='purple' icon='far fa-file-pdf' onClick={this.print}>
get PDF
</Nav.item>;
},
@@ -130,7 +206,7 @@ const NewPage = createClass({
return <Navbar>
<Nav.section>
<Nav.item className='brewTitle'>{this.state.metadata.title}</Nav.item>
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
@@ -144,18 +220,19 @@ const NewPage = createClass({
},
render : function(){
return <div className='newPage page'>
return <div className='newPage sitePage'>
{this.renderNavbar()}
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor
ref='editor'
value={this.state.text}
onChange={this.handleTextChange}
metadata={this.state.metadata}
onMetadataChange={this.handleMetadataChange}
brew={this.state.brew}
onTextChange={this.handleTextChange}
onStyleChange={this.handleStyleChange}
onMetaChange={this.handleMetaChange}
renderer={this.state.brew.renderer}
/>
<BrewRenderer text={this.state.text} errors={this.state.errors} />
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} errors={this.state.htmlErrors}/>
</SplitPane>
</div>
</div>;

View File

@@ -4,6 +4,7 @@ const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const { Meta } = require('vitreum/headtags');
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
const Markdown = require('naturalcrit/markdown.js');
const PrintPage = createClass({
@@ -11,7 +12,9 @@ const PrintPage = createClass({
return {
query : {},
brew : {
text : '',
text : '',
style : '',
renderer : 'legacy'
}
};
},
@@ -33,18 +36,32 @@ const PrintPage = createClass({
},
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} />;
});
if(this.props.brew.renderer == 'legacy') {
return _.map(this.state.brewText.split('\\page'), (page, index)=>{
return <div
className='phb page'
id={`p${index + 1}`}
dangerouslySetInnerHTML={{ __html: MarkdownLegacy.render(page) }}
key={index} />;
});
} else {
return _.map(this.state.brewText.split(/^\\page/gm), (page, index)=>{
return <div
className='phb3 page'
id={`p${index + 1}`}
dangerouslySetInnerHTML={{ __html: Markdown.render(page) }}
key={index} />;
});
}
},
render : function(){
return <div>
<Meta name='robots' content='noindex, nofollow' />
<link href={`${this.props.brew.renderer == 'legacy' ? '/themes/5ePhbLegacy.style.css' : '/themes/5ePhb.style.css'}`} rel='stylesheet'/>
{/* Apply CSS from Style tab */}
<div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${this.props.brew.style} </style>` }} />
{this.renderPages()}
</div>;
}

View File

@@ -1,8 +1,6 @@
require('./sharePage.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx');
@@ -21,14 +19,22 @@ const SharePage = createClass({
brew : {
title : '',
text : '',
style : '',
shareId : null,
createdAt : null,
updatedAt : null,
views : 0
views : 0,
renderer : ''
}
};
},
getInitialState : function() {
return {
showDropdown : false
};
},
componentDidMount : function() {
document.addEventListener('keydown', this.handleControlKeys);
},
@@ -45,8 +51,36 @@ const SharePage = createClass({
}
},
processShareId : function() {
return this.props.brew.googleId ?
this.props.brew.googleId + this.props.brew.shareId :
this.props.brew.shareId;
},
handleDropdown : function(show){
this.setState({
showDropdown : show
});
},
renderDropdown : function(){
if(!this.state.showDropdown) return null;
return <div className='dropdown'>
<a href={`/source/${this.processShareId()}`} className='item'>
view
</a>
<a href={`/download/${this.processShareId()}`} className='item'>
download
</a>
<a href={`/new/${this.processShareId()}`} className='item'>
clone to new
</a>
</div>;
},
render : function(){
return <div className='sharePage page'>
return <div className='sharePage sitePage'>
<Meta name='robots' content='noindex, nofollow' />
<Navbar>
<Nav.section>
@@ -54,17 +88,22 @@ const SharePage = createClass({
</Nav.section>
<Nav.section>
<PrintLink shareId={this.props.brew.shareId} />
<Nav.item href={`/source/${this.props.brew.shareId}`} color='teal' icon='fa-code'>
source
</Nav.item>
{this.props.brew.shareId && <>
<PrintLink shareId={this.processShareId()} />
<Nav.item icon='fas fa-code' color='red' className='source'
onMouseEnter={()=>this.handleDropdown(true)}
onMouseLeave={()=>this.handleDropdown(false)}>
source
{this.renderDropdown()}
</Nav.item>
</>}
<RecentNavItem brew={this.props.brew} storageKey='view' />
<Account />
</Nav.section>
</Navbar>
<div className='content'>
<BrewRenderer text={this.props.brew.text} />
<BrewRenderer text={this.props.brew.text} style={this.props.brew.style} renderer={this.props.brew.renderer} />
</div>
</div>;
}

View File

@@ -2,4 +2,49 @@
.content{
overflow-y : hidden;
}
.source.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;
width : 100%;
vertical-align : middle;
padding : 13px 5px;
box-sizing : border-box;
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;
}
}
}
}
}

View File

@@ -6,6 +6,8 @@ const cx = require('classnames');
const moment = require('moment');
const request = require('superagent');
const googleDriveIcon = require('../../../googleDrive.png');
const BrewItem = createClass({
getDefaultProps : function() {
return {
@@ -27,28 +29,76 @@ const BrewItem = createClass({
if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return;
}
request.delete(`/api/${this.props.brew.editId}`)
.send()
.end(function(err, res){
location.reload();
});
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' />
<i className='fas fa-trash-alt' />
</a>;
},
renderEditLink : function(){
if(!this.props.brew.editId) return;
return <a href={`/edit/${this.props.brew.editId}`} target='_blank' rel='noopener noreferrer'>
<i className='fa fa-pencil' />
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='fas fa-pencil-alt' />
</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='fas fa-share-alt' />
</a>;
},
renderDownloadLink : 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={`/download/${shareLink}`}>
<i className='fas fa-download' />
</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'>
@@ -58,21 +108,21 @@ const BrewItem = createClass({
<div className='info'>
<span>
<i className='fa fa-user' /> {brew.authors.join(', ')}
<i className='fas fa-user' /> {brew.authors.join(', ')}
</span>
<span>
<i className='fa fa-eye' /> {brew.views}
<i className='fas fa-eye' /> {brew.views}
</span>
<span>
<i className='fa fa-refresh' /> {moment(brew.updatedAt).fromNow()}
<i className='fas fa-sync-alt' /> {moment(brew.updatedAt).fromNow()}
</span>
{this.renderGoogleDriveIcon()}
</div>
<div className='links'>
<a href={`/share/${brew.shareId}`} target='_blank' rel='noopener noreferrer'>
<i className='fa fa-share-alt' />
</a>
{this.renderShareLink()}
{this.renderEditLink()}
{this.renderDownloadLink()}
{this.renderDeleteBrewLink()}
</div>
</div>;

View File

@@ -7,6 +7,7 @@
box-sizing : border-box;
overflow : hidden;
width : 48%;
min-height : 105px;
margin-right : 15px;
margin-bottom : 15px;
padding : 5px 15px 5px 8px;
@@ -21,10 +22,13 @@
font-size : 2.2em;
}
.info{
position: absolute;
bottom: 0px;
margin-bottom: 4px;
font-family : ScalySans;
font-size : 1.2em;
&>span{
margin-right : 15px;
margin-right : 12px;
}
}
&:hover{
@@ -55,6 +59,14 @@
&:hover{
opacity : 1;
}
i{
cursor : pointer;
}
}
}
}
.googleDriveIcon {
height : 20px;
padding : 0px;
margin : -5px;
}
}

View File

@@ -9,6 +9,7 @@ const Navbar = require('../../navbar/navbar.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const Account = require('../../navbar/account.navitem.jsx');
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
const BrewItem = require('./brewItem/brewItem.jsx');
// const brew = {
@@ -23,20 +24,101 @@ const UserPage = createClass({
getDefaultProps : function() {
return {
username : '',
brews : []
brews : [],
};
},
getInitialState : function() {
return {
sortType : 'alpha',
sortDir : 'asc'
};
},
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; });
const sortedBrews = this.sortBrews(brews);
return _.map(sortedBrews, (brew, idx)=>{
return <BrewItem brew={brew} key={idx}/>;
});
},
sortBrewOrder : function(brew){
if(!brew.title){brew.title = 'No Title';};
const mapping = {
'alpha' : _.deburr(brew.title.toLowerCase()),
'created' : brew.createdAt,
'updated' : brew.updatedAt,
'views' : brew.views,
'latest' : brew.lastViewed
};
return mapping[this.state.sortType];
},
sortBrews : function(brews){
return _.orderBy(brews, (brew)=>{ return this.sortBrewOrder(brew); }, this.state.sortDir);
},
handleSortOptionChange : function(event){
this.setState({
sortType : event.target.value
});
},
handleSortDirChange : function(event){
this.setState({
sortDir : `${(this.state.sortDir == 'asc' ? 'desc' : 'asc')}`
});
},
renderSortOption : function(sortTitle, sortValue){
return <td>
<button
value={`${sortValue}`}
onClick={this.handleSortOptionChange}
className={`${(this.state.sortType == sortValue ? 'active' : '')}`}
>
{`${sortTitle}`}
</button>
</td>;
},
renderSortOptions : function(){
return <div className='sort-container'>
<table>
<tbody>
<tr>
<td>
<h6>Sort by :</h6>
</td>
{this.renderSortOption('Title', 'alpha')}
{this.renderSortOption('Created Date', 'created')}
{this.renderSortOption('Updated Date', 'updated')}
{this.renderSortOption('Views', 'views')}
{/* {this.renderSortOption('Latest', 'latest')} */}
<td>
<h6>Direction :</h6>
</td>
<td>
<button
onClick={this.handleSortDirChange}
className='sortDir'
>
{`${(this.state.sortDir == 'asc' ? '\u25B2 ASC' : '\u25BC DESC')}`}
</button>
</td>
</tr>
</tbody>
</table>
</div>;
},
getSortedBrews : function(){
return _.groupBy(this.props.brews, (brew)=>{
return (brew.published ? 'published' : 'private');
@@ -46,22 +128,25 @@ const UserPage = createClass({
render : function(){
const brews = this.getSortedBrews();
return <div className='userPage page'>
return <div className='userPage sitePage'>
<link href='/themes/5ePhbLegacy.style.css' rel='stylesheet'/>
<Navbar>
<Nav.section>
<NewBrew />
<RecentNavItem />
<Account />
</Nav.section>
</Navbar>
<div className='content'>
<div className='content V3'>
<div className='phb'>
{this.renderSortOptions()}
<div>
<h1>{this.props.username}'s brews</h1>
<h1>{this.getUsernameWithS()} brews</h1>
{this.renderBrews(brews.published)}
</div>
<div>
<h1>{this.props.username}'s unpublished brews</h1>
<h1>{this.getUsernameWithS()} unpublished brews</h1>
{this.renderBrews(brews.private)}
</div>
</div>

View File

@@ -30,4 +30,44 @@
}
}
.sort-container{
font-family : 'Open Sans', sans-serif;
position : fixed;
top : 35px;
border : 2px solid #58180D;
width : 675px;
background-color : #EEE5CE;
padding : 2px;
text-align : center;
z-index : 15;
h6{
text-transform : uppercase;
font-family : 'Open Sans', sans-serif;
font-size : 11px;
font-weight : bold;
color : #58180D;
}
table{
margin : 0px;
vertical-align : middle;
tbody tr{
background-color: transparent !important;
button{
background-color : transparent;
color : #58180D;
font-family : 'Open Sans', sans-serif;
font-size : 11px;
text-transform : uppercase;
font-weight : normal;
&.active{
font-weight : bold;
border : 2px solid #58180D;
}
&.sortDir{
width : 75px;
}
}
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 530 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,19 +1,19 @@
module.exports = async (name, props={})=>{
module.exports = async(name, title = '', props = {})=>{
return `
<!DOCTYPE html>
<html>
<head>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link href=${`/${name}/bundle.css`} rel='stylesheet'></link>
<link href=${`/${name}/bundle.css`} rel='stylesheet' />
<link rel="icon" href="/assets/homebrew/favicon.ico" type="image/x-icon" />
<title>The Homebrewery - NaturalCrit</title>
<title>${title.length ? `${title} - The Homebrewery`: 'The Homebrewery - NaturalCrit'}</title>
</head>
<body>
<main id="reactRoot">${require(`../build/${name}/ssr.js`)(props)}</main>
<script src=${`/${name}/bundle.js`}></script>
<script>start_app(${JSON.stringify(props)})</script>
</body>
<script src=${`/${name}/bundle.js`}></script>
<script>start_app(${JSON.stringify(props)})</script>
</html>
`;
};

View File

@@ -1,5 +1,6 @@
{
"host" : "homebrewery.local.naturalcrit.com:8000",
"naturalcrit_url" : "local.naturalcrit.com:8010",
"secret" : "secret"
}
"secret" : "secret",
"web_port" : 8000
}

20
freebsd/install.sh Normal file
View File

@@ -0,0 +1,20 @@
#!/bin/sh
pkg install -y git nano node npm mongodb44
sysrc mongod_enable=YES
service mongod start
cd /usr/local/
git clone https://github.com/naturalcrit/homebrewery.git
cd homebrewery
npm install
npm audit fix
npm run postinstall
cp freebsd/rc.d/homebrewery /usr/local/etc/rc.d/
chmod +x /usr/local/etc/rc.d/homebrewery
sysrc homebrewery_enable=YES
service homebrewery start

65
freebsd/rc.d/homebrewery Normal file
View File

@@ -0,0 +1,65 @@
#!/bin/sh
#
# PROVIDE: homebrewery
# REQUIRE: NETWORKING
# KEYWORD: shutdown
# Author: S Robertson
# Version: 1.0.0
# Description:
# This script runs HomeBrewery as a service under the supplied user on boot
# How to use:
# Place this file in /usr/local/etc/rc.d/
# Add homebrewery_enable="YES" to /etc/rc.config
# (Optional) To run as non-root, add homebrewery_runAs="homebrewery" to /etc/rc.config
# (Optional) To pass HomeBrewery args, add homebrewery_args="" to /etc/rc.config
# Freebsd rc library
. /etc/rc.subr
# General Info
name="homebrewery" # Safe name of program
location="/usr/local/" # Install location
program_name="homebrewery" # Name of exec
title="HomeBrewery" # Title to display in top/htop
# RC.config vars
load_rc_config $name # Loading rc config vars
: ${homebrewery_enable="NO"} # Default: Do not enable HomeBrewery
: ${homebrewery_runAs="root"} # Default: Run HomeBrewery as root
: ${homebrewery_port=8000} # Default: Run HomeBrewery on port 8000
: ${homebrewery_NODE_ENV="local"} # Default: Run HomeBrewery in local mode
# Freebsd Setup
rcvar=homebrewery_enable # Enables the rc.conf YES/NO flag
pidfile="/var/run/${program_name}.pid" # File that allows the system to keep track of HomeBrewery status
# Env Setup
export HOME=$( getent passwd "homebrewery_runAs" | cut -d: -f6 ) # Gets the home directory of the runAs user
export NODE_ENV=${homebrewery_NODE_ENV}
export PORT=${homebrewery_port}
# Command Setup
exec_cmd="${location}/${program_name}/server.js" # Path to the HomeBrewery server.js, /usr/local/bin/ when installed globally
output_file="/var/log/${program_name}.log" # Path to HomeBrewery output file
# Command
command="/usr/sbin/daemon"
command_args="-r -t ${title} -u ${homebrewery_runAs} -o ${output_file} -P ${pidfile} /usr/local/bin/node ${exec_cmd} ${homebrewery_args}"
# Extra Commands
extra_commands="dev_mode"
dev_mode_cmd="homebrewery_dev_mode"
homebrewery_dev_mode() {
echo "Starting HomeBrewery in live rebuild Developer mode..."
cd ${location}/${program_name}/
/usr/local/bin/node ${location}/${program_name}/scripts/buildHomebrew.js --dev
}
# Loading Config
load_rc_config ${name}
run_rc_command "$1"

14935
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
"version": "2.8.2",
"version": "2.13.2",
"engines": {
"node": "12.16.x"
"node": "14.15.x"
},
"repository": {
"type": "git",
@@ -35,40 +35,48 @@
},
"babel": {
"presets": [
"env",
"react"
"@babel/preset-env",
"@babel/preset-react"
]
},
"dependencies": {
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.6",
"@babel/preset-react": "^7.9.4",
"@babel/core": "^7.14.8",
"@babel/plugin-transform-runtime": "^7.14.5",
"@babel/preset-env": "^7.14.8",
"@babel/preset-react": "^7.14.5",
"body-parser": "^1.19.0",
"classnames": "^2.2.6",
"codemirror": "^5.54.0",
"classnames": "^2.3.1",
"codemirror": "^5.62.2",
"cookie-parser": "^1.4.5",
"create-react-class": "^15.6.3",
"create-react-class": "^15.7.0",
"dedent-tabs": "^0.9.0",
"express": "^4.17.1",
"fs-extra": "9.0.0",
"express-async-handler": "^1.1.4",
"express-static-gzip": "2.1.1",
"fs-extra": "10.0.0",
"googleapis": "82.0.0",
"jwt-simple": "^0.5.6",
"less": "^3.11.1",
"lodash": "^4.17.15",
"marked": "^0.3.19",
"moment": "^2.26.0",
"mongoose": "^5.9.15",
"nconf": "^0.10.0",
"less": "^3.13.1",
"lodash": "^4.17.21",
"marked": "2.1.3",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.29.1",
"mongoose": "^5.13.4",
"nanoid": "3.1.23",
"nconf": "^0.11.3",
"prop-types": "15.7.2",
"query-string": "6.12.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"query-string": "7.0.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-frame-component": "4.1.3",
"react-router-dom": "5.2.0",
"shortid": "^2.2.15",
"superagent": "^5.2.2",
"vitreum": "github:calculuschild/vitreum#21a8e1c9421f1d3a3b474c12f480feb2fbd28c5b"
"sanitize-filename": "1.6.3",
"superagent": "^6.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
"eslint": "^7.0.0",
"eslint-plugin-react": "^7.20.0",
"pico-check": "^1.3.2"
"eslint": "^7.31.0",
"eslint-plugin-react": "^7.24.0",
"pico-check": "^2.1.3"
}
}

View File

@@ -1,2 +1,4 @@
# Notes
User-agent: *
Disallow: /edit/

View File

@@ -1,26 +1,64 @@
const fs = require('fs-extra');
const zlib = require('zlib');
const Proj = require('./project.json');
const { pack } = require('vitreum');
const { pack, watchFile, livereload } = require('vitreum');
const isDev = !!process.argv.find((arg)=>arg=='--dev');
const lessTransform = require('vitreum/transforms/less.js');
const assetTransform = require('vitreum/transforms/asset.js');
//const Meta = require('vitreum/headtags');
const babel = require('@babel/core');
const less = require('less');
const babelify = async (code)=>(await babel.transformAsync(code, { presets: ['@babel/preset-env', '@babel/preset-react'], plugins: ['@babel/plugin-transform-runtime'] })).code;
const transforms = {
'.js' : (code, filename, opts)=>babelify(code),
'.jsx' : (code, filename, opts)=>babelify(code),
'.less' : lessTransform,
'*' : assetTransform('./build')
};
const build = async ({ bundle, render, ssr })=>{
await fs.outputFile('./build/homebrew/bundle.css', await lessTransform.generate({ paths: './shared' }));
const css = await lessTransform.generate({ paths: './shared' });
await fs.outputFile('./build/homebrew/bundle.css', css);
await fs.outputFile('./build/homebrew/bundle.js', bundle);
await fs.outputFile('./build/homebrew/ssr.js', ssr);
await fs.outputFile('./build/homebrew/render.js', render);
await fs.copy('./themes/fonts', './build/fonts');
let src = './themes/5ePhbLegacy.style.less';
//Parse brew theme files
less.render(fs.readFileSync(src).toString(), {
compress : !isDev
}, function(e, output) {
fs.outputFile('./build/themes/5ePhbLegacy.style.css', output.css);
});
src = './themes/5ePhb.style.less';
less.render(fs.readFileSync(src).toString(), {
compress : !isDev
}, function(e, output) {
fs.outputFile('./build/themes/5ePhb.style.css', output.css);
});
// await less.render(lessCode, {
// compress : !dev,
// sourceMap : (dev ? {
// sourceMapFileInline: true,
// outputSourceFiles: true
// } : false),
// })
//compress files in production
if(!isDev){
await fs.outputFile('./build/homebrew/bundle.css.br', zlib.brotliCompressSync(css));
await fs.outputFile('./build/homebrew/bundle.js.br', zlib.brotliCompressSync(bundle));
await fs.outputFile('./build/homebrew/ssr.js.br', zlib.brotliCompressSync(ssr));
} else {
await fs.remove('./build/homebrew/bundle.css.br');
await fs.remove('./build/homebrew/bundle.js.br');
await fs.remove('./build/homebrew/ssr.js.br');
}
};
fs.emptyDirSync('./build/homebrew');
fs.emptyDirSync('./build');
pack('./client/homebrew/homebrew.jsx', {
paths : ['./shared'],
libs : Proj.libs,
@@ -29,3 +67,12 @@ pack('./client/homebrew/homebrew.jsx', {
})
.then(build)
.catch(console.error);
//In development set up a watch server and livereload
if(isDev){
livereload('./build');
watchFile('./server.js', {
watch : ['./client'] // Watch additional folders if you want
});
}

View File

@@ -10,6 +10,7 @@
"classnames",
"codemirror",
"codemirror/mode/gfm/gfm.js",
"codemirror/mode/css/css.js",
"codemirror/mode/javascript/javascript.js",
"moment",
"superagent",

264
server.js
View File

@@ -1,9 +1,59 @@
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
const _ = require('lodash');
const jwt = require('jwt-simple');
const express = require('express');
const app = express();
app.use(express.static(`${__dirname}/build`));
const homebrewApi = require('./server/homebrew.api.js');
const GoogleActions = require('./server/googleActions.js');
const serveCompressedStaticAssets = require('./server/static-assets.mv.js');
const sanitizeFilename = require('sanitize-filename');
const asyncHandler = require('express-async-handler');
const brewAccessTypes = ['edit', 'share', 'raw'];
//Get the brew object from the HB database or Google Drive
const getBrewFromId = asyncHandler(async (id, accessType)=>{
if(!brewAccessTypes.includes(accessType))
throw ('Invalid Access Type when getting brew');
let brew;
if(id.length > 12) {
const googleId = id.slice(0, -12);
id = id.slice(-12);
brew = await GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, id, accessType);
} else {
brew = await HomebrewModel.get(accessType == 'edit' ? { editId: id } : { shareId: id });
brew = brew.toObject(); // Convert MongoDB object to standard Javascript Object
}
brew = sanitizeBrew(brew, accessType === 'edit' ? false : true);
//Split brew.text into text and style
//unless the Access Type is RAW, in which case return immediately
if(accessType == 'raw') {
return brew;
}
if(brew.text.startsWith('```css')) {
const index = brew.text.indexOf('```\n\n');
brew.style = brew.text.slice(7, index - 1);
brew.text = brew.text.slice(index + 5);
}
return brew;
});
const sanitizeBrew = (brew, full=false)=>{
delete brew._id;
delete brew.__v;
if(full){
delete brew.editId;
}
return brew;
};
app.use('/', serveCompressedStaticAssets(`${__dirname}/build`));
process.chdir(__dirname);
//app.use(express.static(`${__dirname}/build`));
app.use(require('body-parser').json({ limit: '25mb' }));
app.use(require('cookie-parser')());
app.use(require('./server/forcessl.mw.js'));
@@ -17,29 +67,33 @@ const config = require('nconf')
//DB
const mongoose = require('mongoose');
mongoose.connect(config.get('mongodb_uri') || config.get('mongolab_uri') || 'mongodb://localhost/naturalcrit',
{ retryWrites: false, useNewUrlParser: true });
{ retryWrites: false, useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true });
mongoose.connection.on('error', ()=>{
console.log('Error : Could not connect to a Mongo Database.');
console.log(' If you are running locally, make sure mongodb.exe is running.');
throw 'Can not connect to Mongo';
});
//Account Middleware
app.use((req, res, next)=>{
if(req.cookies && req.cookies.nc_session){
try {
req.account = jwt.decode(req.cookies.nc_session, config.get('secret'));
//console.log("Just loaded up JWT from cookie:");
//console.log(req.account);
} catch (e){}
}
req.config = {
google_client_id : config.get('google_client_id'),
google_client_secret : config.get('google_client_secret')
};
return next();
});
app.use(require('./server/homebrew.api.js'));
app.use(homebrewApi);
app.use(require('./server/admin.api.js'));
const HomebrewModel = require('./server/homebrew.model.js').model;
const welcomeText = require('fs').readFileSync('./client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
const changelogText = require('fs').readFileSync('./changelog.md', 'utf8');
@@ -51,97 +105,163 @@ app.get('/robots.txt', (req, res)=>{
return res.sendFile(`${__dirname}/robots.txt`);
});
//Source page
app.get('/source/:id', (req, res)=>{
HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{
const text = brew.text.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
return res.send(`<code><pre style="white-space: pre-wrap;">${text}</pre></code>`);
})
.catch((err)=>{
console.log(err);
return res.status(404).send('Could not find Homebrew with that id');
});
//Home page
app.get('/', async (req, res, next)=>{
const brew = {
text : welcomeText
};
req.brew = brew;
return next();
});
//Changelog page
app.get('/changelog', async (req, res, next)=>{
const brew = {
title : 'Changelog',
text : changelogText
};
req.brew = brew;
return next();
});
//Source page
app.get('/source/:id', asyncHandler(async (req, res)=>{
const brew = await getBrewFromId(req.params.id, 'raw');
const replaceStrings = { '&': '&amp;', '<': '&lt;', '>': '&gt;' };
let text = brew.text;
for (const replaceStr in replaceStrings) {
text = text.replaceAll(replaceStr, replaceStrings[replaceStr]);
}
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
res.status(200).send(text);
}));
//Download brew source page
app.get('/download/:id', asyncHandler(async (req, res)=>{
const brew = await getBrewFromId(req.params.id, 'raw');
const prefix = 'HB - ';
let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', '');
if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; };
res.set({
'Cache-Control' : 'no-cache',
'Content-Type' : 'text/plain',
'Content-Disposition' : `attachment; filename="${fileName}.txt"`
});
res.status(200).send(brew.text);
}));
//User Page
app.get('/user/:username', (req, res, next)=>{
const fullAccess = req.account && (req.account.username == req.params.username);
HomebrewModel.getByUser(req.params.username, fullAccess)
.then((brews)=>{
req.brews = brews;
return next();
})
app.get('/user/:username', async (req, res, next)=>{
const ownAccount = req.account && (req.account.username == req.params.username);
let brews = await HomebrewModel.getByUser(req.params.username, ownAccount)
.catch((err)=>{
console.log(err);
});
if(ownAccount && req?.account?.googleId){
const googleBrews = await GoogleActions.listGoogleBrews(req, res)
.catch((err)=>{
console.log(err);
console.error(err);
});
if(googleBrews)
brews = _.concat(brews, googleBrews);
}
req.brews = _.map(brews, (brew)=>{
return sanitizeBrew(brew, !ownAccount);
});
return next();
});
//Edit Page
app.get('/edit/:id', (req, res, next)=>{
HomebrewModel.get({ editId: req.params.id })
.then((brew)=>{
req.brew = brew.sanatize();
return next();
})
.catch((err)=>{
console.log(err);
return res.status(400).send(`Can't get that`);
});
});
app.get('/edit/:id', asyncHandler(async (req, res, next)=>{
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
const brew = await getBrewFromId(req.params.id, 'edit');
req.brew = brew;
return next();
}));
//New Page
app.get('/new/:id', asyncHandler(async (req, res, next)=>{
const brew = await getBrewFromId(req.params.id, 'share');
brew.title = `CLONE - ${brew.title}`;
req.brew = brew;
return next();
}));
//Share Page
app.get('/share/:id', (req, res, next)=>{
HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{
return brew.increaseView();
})
.then((brew)=>{
req.brew = brew.sanatize(true);
return next();
})
.catch((err)=>{
console.log(err);
return res.status(400).send(`Can't get that`);
});
});
app.get('/share/:id', asyncHandler(async (req, res, next)=>{
const brew = await getBrewFromId(req.params.id, 'share');
if(req.params.id.length > 12) {
const googleId = req.params.id.slice(0, -12);
const shareId = req.params.id.slice(-12);
await GoogleActions.increaseView(googleId, shareId, 'share', brew)
.catch((err)=>{next(err);});
} else {
await HomebrewModel.increaseView({ shareId: brew.shareId });
}
req.brew = brew;
return next();
}));
//Print Page
app.get('/print/:id', (req, res, next)=>{
HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{
req.brew = brew.sanatize(true);
return next();
})
.catch((err)=>{
console.log(err);
return res.status(400).send(`Can't get that`);
});
});
app.get('/print/:id', asyncHandler(async (req, res, next)=>{
const brew = await getBrewFromId(req.params.id, 'share');
req.brew = brew;
return next();
}));
//Render the page
//const render = require('.build/render');
const templateFn = require('./client/template.js');
app.use((req, res)=>{
const props = {
version : require('./package.json').version,
url : req.originalUrl,
welcomeText : welcomeText,
changelog : changelogText,
brew : req.brew,
brews : req.brews,
googleBrews : req.googleBrews,
account : req.account,
enable_v3 : config.get('enable_v3')
};
templateFn('homebrew', props)
.then((page)=>res.send(page))
.catch((err)=>{
console.log(err);
return res.sendStatus(500);
});
templateFn('homebrew', title = req.brew ? req.brew.title : '', props)
.then((page)=>{ res.send(page); })
.catch((err)=>{
console.log(err);
return res.sendStatus(500);
});
});
//v=====----- Error-Handling Middleware -----=====v//
//Format Errors so all fields will be sent
const replaceErrors = (key, value)=>{
if(value instanceof Error) {
const error = {};
Object.getOwnPropertyNames(value).forEach(function (key) {
error[key] = value[key];
});
return error;
}
return value;
};
const PORT = process.env.PORT || 8000;
const getPureError = (error)=>{
return JSON.parse(JSON.stringify(error, replaceErrors));
};
app.use((err, req, res, next)=>{
const status = err.status || 500;
console.error(err);
res.status(status).send(getPureError(err));
});
//^=====--------------------------------------=====^//
const PORT = process.env.PORT || config.get('web_port') || 8000;
app.listen(PORT);
console.log(`server on port:${PORT}`);

View File

@@ -37,7 +37,7 @@ const junkBrewQuery = HomebrewModel.find({
/* Search for brews that aren't compressed (missing the compressed text field) */
const uncompressedBrewQuery = HomebrewModel.find({
'textBin' : null
'text' : { '$exists': true }
}).lean().limit(10000).select('_id');
router.get('/admin/cleanup', mw.adminOnly, (req, res)=>{

374
server/googleActions.js Normal file
View File

@@ -0,0 +1,374 @@
/* eslint-disable max-lines */
const _ = require('lodash');
const { google } = require('googleapis');
const { nanoid } = require('nanoid');
const token = require('./token.js');
const config = require('nconf')
.argv()
.env({ lowerCase: true }) // Load environment variables
.file('environment', { file: `config/${process.env.NODE_ENV}.json` })
.file('defaults', { file: 'config/default.json' });
//let oAuth2Client;
GoogleActions = {
authCheck : (account, res)=>{
if(!account || !account.googleId){ // If not signed into Google
const err = new Error('Not Signed In');
err.status = 401;
throw err;
}
const oAuth2Client = new google.auth.OAuth2(
config.get('google_client_id'),
config.get('google_client_secret'),
'/auth/google/redirect'
);
oAuth2Client.setCredentials({
access_token : account.googleAccessToken, //Comment out to refresh token
refresh_token : account.googleRefreshToken
});
oAuth2Client.on('tokens', (tokens)=>{
if(tokens.refresh_token) {
account.googleRefreshToken = tokens.refresh_token;
}
account.googleAccessToken = tokens.access_token;
const JWTToken = token.generateAccessToken(account);
//Save updated token to cookie
//res.cookie('nc_session', JWTToken, { maxAge: 1000*60*60*24*365, path: '/', sameSite: 'lax' });
res.cookie('nc_session', JWTToken, { maxAge: 1000*60*60*24*365, path: '/', sameSite: 'lax', domain: '.naturalcrit.com' });
});
return oAuth2Client;
},
getGoogleFolder : async (auth)=>{
const drive = google.drive({ version: 'v3', auth: auth });
fileMetadata = {
'name' : 'Homebrewery',
'mimeType' : 'application/vnd.google-apps.folder'
};
const obj = await drive.files.list({
q : 'mimeType = \'application/vnd.google-apps.folder\''
})
.catch((err)=>{
console.log('Error searching Google Drive Folders');
console.error(err);
});
let folderId;
if(obj.data.files.length == 0){
const obj = await drive.files.create({
resource : fileMetadata
})
.catch((err)=>{
console.log('Error creating google app folder');
console.error(err);
});
folderId = obj.data.id;
} else {
folderId = obj.data.files[0].id;
}
return folderId;
},
listGoogleBrews : async (req, res)=>{
oAuth2Client = GoogleActions.authCheck(req.account, res);
//TODO: Change to service account to allow non-owners to view published files.
// Requires a driveId parameter in the drive.files.list command
// const keys = JSON.parse(config.get('service_account'));
// const auth = google.auth.fromJSON(keys);
// auth.scopes = ['https://www.googleapis.com/auth/drive'];
const drive = google.drive({ version: 'v3', auth: oAuth2Client });
const obj = await drive.files.list({
pageSize : 100,
fields : 'nextPageToken, files(id, name, description, modifiedTime, properties)',
q : 'mimeType != \'application/vnd.google-apps.folder\' and trashed = false'
})
.catch((err)=>{
return console.error(`Error Listing Google Brews: ${err}`);
//TODO: Should break out here, but continues on for some reason.
});
if(!obj.data.files.length) {
console.log('No files found.');
}
const brews = obj.data.files.map((file)=>{
return {
text : '',
shareId : file.properties.shareId,
editId : file.properties.editId,
createdAt : file.createdTime,
updatedAt : file.modifiedTime,
gDrive : true,
googleId : file.id,
title : file.properties.title,
description : file.description,
views : file.properties.views,
tags : '',
published : file.properties.published ? file.properties.published == 'true' : false,
authors : [req.account.username], //TODO: properly save and load authors to google drive
systems : []
};
});
return brews;
},
existsGoogleBrew : async (auth, id)=>{
const drive = google.drive({ version: 'v3', auth: auth });
const result = await drive.files.get({ fileId: id })
.catch((err)=>{
console.log('error checking file exists...');
console.log(err);
return false;
});
if(result){return true;}
return false;
},
updateGoogleBrew : async (auth, brew)=>{
const drive = google.drive({ version: 'v3', auth: auth });
if(await GoogleActions.existsGoogleBrew(auth, brew.googleId) == true) {
await drive.files.update({
fileId : brew.googleId,
resource : { name : `${brew.title}.txt`,
description : `${brew.description}`,
properties : { title : brew.title,
published : brew.published,
lastViewed : brew.lastViewed,
views : brew.views,
version : brew.version,
renderer : brew.renderer,
tags : brew.tags,
systems : brew.systems.join() }
},
media : { mimeType : 'text/plain',
body : brew.text }
})
.catch((err)=>{
console.log('Error saving to google');
console.error(err);
//return res.status(500).send('Error while saving');
});
}
return (brew);
},
newGoogleBrew : async (auth, brew)=>{
const drive = google.drive({ version: 'v3', auth: auth });
const media = {
mimeType : 'text/plain',
body : brew.text
};
const folderId = await GoogleActions.getGoogleFolder(auth);
const fileMetadata = {
'name' : `${brew.title}.txt`,
'description' : `${brew.description}`,
'parents' : [folderId],
'properties' : { //AppProperties is not accessible
'shareId' : nanoid(12),
'editId' : nanoid(12),
'title' : brew.title,
'views' : '0'
}
};
const obj = await drive.files.create({
resource : fileMetadata,
media : media
})
.catch((err)=>{
console.error(err);
return res.status(500).send('Error while creating google brew');
});
if(!obj) return;
await drive.permissions.create({
resource : { type : 'anyone',
role : 'writer' },
fileId : obj.data.id,
fields : 'id',
})
.catch((err)=>{
console.log('Error updating permissions');
console.error(err);
});
const newHomebrew = {
text : brew.text,
shareId : fileMetadata.properties.shareId,
editId : fileMetadata.properties.editId,
createdAt : new Date(),
updatedAt : new Date(),
gDrive : true,
googleId : obj.data.id,
title : brew.title,
description : brew.description,
tags : '',
published : brew.published,
renderer : brew.renderer,
authors : [],
systems : []
};
return newHomebrew;
},
readFileMetadata : async (auth, id, accessId, accessType)=>{
const drive = google.drive({ version: 'v3', auth: auth });
const obj = await drive.files.get({
fileId : id,
fields : 'properties, createdTime, modifiedTime, description, trashed'
})
.catch((err)=>{
console.log('Error loading from Google');
throw (err);
return;
});
if(obj) {
if(accessType == 'edit' && obj.data.properties.editId != accessId){
throw ('Edit ID does not match');
} else if(accessType == 'share' && obj.data.properties.shareId != accessId){
throw ('Share ID does not match');
}
//Access file using service account. Using API key only causes "automated query" lockouts after a while.
const keys = typeof(config.get('service_account')) == 'string' ?
JSON.parse(config.get('service_account')) :
config.get('service_account');
const serviceAuth = google.auth.fromJSON(keys);
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
const serviceDrive = google.drive({ version: 'v3', auth: serviceAuth });
const file = await serviceDrive.files.get({
fileId : id,
fields : 'description, properties',
alt : 'media'
})
.catch((err)=>{
console.log('Error getting file contents from Google');
console.error(err);
});
const brew = {
shareId : obj.data.properties.shareId,
editId : obj.data.properties.editId,
title : obj.data.properties.title,
text : file.data,
description : obj.data.description,
tags : obj.data.properties.tags ? obj.data.properties.tags : '',
systems : obj.data.properties.systems ? obj.data.properties.systems.split(',') : [],
authors : [],
published : obj.data.properties.published ? obj.data.properties.published == 'true' : false,
trashed : obj.data.trashed,
createdAt : obj.data.createdTime,
updatedAt : obj.data.modifiedTime,
lastViewed : obj.data.properties.lastViewed,
views : parseInt(obj.data.properties.views) || 0, //brews with no view parameter will return undefined
version : parseInt(obj.data.properties.version) || 0,
renderer : obj.data.properties.renderer ? obj.data.properties.renderer : 'legacy',
gDrive : true,
googleId : id
};
return (brew);
}
},
deleteGoogleBrew : async (req, res, id)=>{
oAuth2Client = GoogleActions.authCheck(req.account, res);
const drive = google.drive({ version: 'v3', auth: oAuth2Client });
const googleId = id.slice(0, -12);
const accessId = id.slice(-12);
const obj = await drive.files.get({
fileId : googleId,
fields : 'properties'
})
.catch((err)=>{
console.log('Error loading from Google');
console.error(err);
return;
});
if(obj && obj.data.properties.editId != accessId) {
throw ('Not authorized to delete this Google brew');
}
await drive.files.update({
fileId : googleId,
resource : { trashed: true }
})
.catch((err)=>{
console.log('Can\'t delete Google file');
console.error(err);
});
return res.status(200).send();
},
increaseView : async (id, accessId, accessType, brew)=>{
//service account because this is modifying another user's file properties
//so we need extended scope
const keys = typeof(config.get('service_account')) == 'string' ?
JSON.parse(config.get('service_account')) :
config.get('service_account');
const auth = google.auth.fromJSON(keys);
auth.scopes = ['https://www.googleapis.com/auth/drive'];
const drive = google.drive({ version: 'v3', auth: auth });
await drive.files.update({
fileId : brew.googleId,
resource : { properties : { views : brew.views + 1,
lastViewed : new Date() } }
})
.catch((err)=>{
console.log('Error updating Google views');
console.error(err);
//return res.status(500).send('Error while saving');
});
return;
}
};
module.exports = GoogleActions;

View File

@@ -2,6 +2,8 @@ const _ = require('lodash');
const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router();
const zlib = require('zlib');
const GoogleActions = require('./googleActions.js');
const Markdown = require('../shared/naturalcrit/markdown.js');
// const getTopBrews = (cb) => {
// HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
@@ -9,28 +11,39 @@ const zlib = require('zlib');
// });
// };
const MAX_TITLE_LENGTH = 100;
const getGoodBrewTitle = (text)=>{
const titlePos = text.indexOf('# ');
if(titlePos !== -1) {
const ending = text.indexOf('\n', titlePos);
return text.substring(titlePos + 2, ending);
} else {
return _.find(text.split('\n'), (line)=>line);
const tokens = Markdown.marked.lexer(text);
return (tokens.find((token)=>token.type == 'heading' || token.type == 'paragraph')?.text || 'No Title')
.slice(0, MAX_TITLE_LENGTH);
};
const mergeBrewText = (text, style)=>{
if(typeof style !== 'undefined') {
text = `\`\`\`css\n` +
`${style}\n` +
`\`\`\`\n\n` +
`${text}`;
}
return text;
};
const newBrew = (req, res)=>{
const authors = (req.account) ? [req.account.username] : [];
const brew = req.body;
const newHomebrew = new HomebrewModel(_.merge({},
req.body,
{ authors: authors }
));
if(!newHomebrew.title) {
newHomebrew.title = getGoodBrewTitle(newHomebrew.text);
if(!brew.title) {
brew.title = getGoodBrewTitle(brew.text);
}
brew.authors = (req.account) ? [req.account.username] : [];
brew.text = mergeBrewText(brew.text, brew.style);
delete brew.editId;
delete brew.shareId;
delete brew.googleId;
const newHomebrew = new HomebrewModel(brew);
// Compress brew text to binary before saving
newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text);
// Delete the non-binary text field since it's not needed anymore
@@ -41,7 +54,10 @@ const newBrew = (req, res)=>{
console.error(err, err.toString(), err.stack);
return res.status(500).send(`Error while creating new brew, ${err.toString()}`);
}
return res.json(obj);
obj = obj.toObject();
obj.gDrive = false;
return res.status(200).send(obj);
});
};
@@ -49,8 +65,10 @@ const updateBrew = (req, res)=>{
HomebrewModel.get({ editId: req.params.id })
.then((brew)=>{
brew = _.merge(brew, req.body);
brew.text = mergeBrewText(brew.text, brew.style);
// Compress brew text to binary before saving
brew.textBin = zlib.deflateRawSync(req.body.text);
brew.textBin = zlib.deflateRawSync(brew.text);
// Delete the non-binary text field since it's not needed anymore
brew.text = undefined;
brew.updatedAt = new Date();
@@ -103,49 +121,51 @@ const deleteBrew = (req, res)=>{
});
};
const newGoogleBrew = async (req, res, next)=>{
let oAuth2Client;
try { oAuth2Client = GoogleActions.authCheck(req.account, res); } catch (err) { return res.status(err.status).send(err.message); }
const brew = req.body;
if(!brew.title) {
brew.title = getGoodBrewTitle(brew.text);
}
brew.authors = (req.account) ? [req.account.username] : [];
brew.text = mergeBrewText(brew.text, brew.style);
delete brew.editId;
delete brew.shareId;
delete brew.googleId;
req.body = brew;
const newBrew = await GoogleActions.newGoogleBrew(oAuth2Client, brew);
return res.status(200).send(newBrew);
};
const updateGoogleBrew = async (req, res, next)=>{
let oAuth2Client;
try { oAuth2Client = GoogleActions.authCheck(req.account, res); } catch (err) { return res.status(err.status).send(err.message); }
const brew = req.body;
brew.text = mergeBrewText(brew.text, brew.style);
const updatedBrew = await GoogleActions.updateGoogleBrew(oAuth2Client, brew);
return res.status(200).send(updatedBrew);
};
router.post('/api', newBrew);
router.post('/api/newGoogle/', newGoogleBrew);
router.put('/api/:id', updateBrew);
router.put('/api/update/:id', updateBrew);
router.put('/api/updateGoogle/:id', updateGoogleBrew);
router.delete('/api/:id', deleteBrew);
router.get('/api/remove/:id', deleteBrew);
router.get('/api/removeGoogle/:id', (req, res)=>{GoogleActions.deleteGoogleBrew(req, res, req.params.id);});
module.exports = router;
/*
module.exports = function(app) {
app;
app.get('/api/search', mw.adminOnly, function(req, res) {
var page = req.query.page || 0;
var count = req.query.count || 20;
var query = {};
if (req.query && req.query.id) {
query = {
"$or": [{
editId : req.query.id
}, {
shareId : req.query.id
}]
};
}
HomebrewModel.find(query, {
text : 0 //omit the text
}, {
skip: page*count,
limit: count*1
}, function(err, objs) {
if (err) console.error(err);
return res.json({
page : page,
count : count,
total : homebrewTotal,
brews : objs
});
});
})
return app;
}
*/

View File

@@ -1,11 +1,11 @@
const mongoose = require('mongoose');
const shortid = require('shortid');
const { nanoid } = require('nanoid');
const _ = require('lodash');
const zlib = require('zlib');
const HomebrewSchema = mongoose.Schema({
shareId : { type: String, default: shortid.generate, index: { unique: true } },
editId : { type: String, default: shortid.generate, index: { unique: true } },
shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
editId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
title : { type: String, default: '' },
text : { type: String, default: '' },
textBin : { type: Buffer },
@@ -13,6 +13,7 @@ const HomebrewSchema = mongoose.Schema({
description : { type: String, default: '' },
tags : { type: String, default: '' },
systems : [String],
renderer : { type: String, default: '' },
authors : [String],
published : { type: Boolean, default: false },
@@ -23,32 +24,17 @@ const HomebrewSchema = mongoose.Schema({
version : { type: Number, default: 1 }
}, { versionKey: false });
HomebrewSchema.methods.sanatize = function(full=false){
const brew = this.toJSON();
delete brew._id;
delete brew.__v;
if(full){
delete brew.editId;
}
HomebrewSchema.statics.increaseView = async function(query) {
const brew = await Homebrew.findOne(query).exec();
brew.lastViewed = new Date();
brew.views = brew.views + 1;
await brew.save()
.catch((err)=>{
return err;
});
return brew;
};
HomebrewSchema.methods.increaseView = function(){
return new Promise((resolve, reject)=>{
this.lastViewed = new Date();
this.views = this.views + 1;
this.save((err)=>{
if(err) return reject(err);
return resolve(this);
});
});
};
HomebrewSchema.statics.get = function(query){
return new Promise((resolve, reject)=>{
Homebrew.find(query, (err, brews)=>{
@@ -57,6 +43,8 @@ HomebrewSchema.statics.get = function(query){
unzipped = zlib.inflateRawSync(brews[0].textBin);
brews[0].text = unzipped.toString();
}
if(!brews[0].renderer)
brews[0].renderer = 'legacy';
return resolve(brews[0]);
});
});
@@ -68,20 +56,16 @@ HomebrewSchema.statics.getByUser = function(username, allowAccess=false){
if(allowAccess){
delete query.published;
}
Homebrew.find(query, (err, brews)=>{
Homebrew.find(query).lean().exec((err, brews)=>{ //lean() converts results to JSObjects
if(err) return reject('Can not find brew');
return resolve(_.map(brews, (brew)=>{
return brew.sanatize(!allowAccess);
}));
return resolve(brews);
});
});
};
const Homebrew = mongoose.model('Homebrew', HomebrewSchema);
module.exports = {
schema : HomebrewSchema,
model : Homebrew,
};
};

View File

@@ -0,0 +1,31 @@
const expressStaticGzip = require('express-static-gzip');
// Serve brotli-compressed static files if available
const customCacheControlHandler=(response, path)=>{
if(path.endsWith('.br')) {
// Drop .br suffix to help mime understand the actual type of the file
path = path.slice(0, -3);
}
if(path.endsWith('.js') || path.endsWith('.css')) {
// .js and .css files are allowed to be cached up to 12 hours, but then
// they must be revalidated to see if there are any updates
response.setHeader('Cache-Control', 'public, max-age: 43200, must-revalidate');
} else {
// Everything else is cached up to a months as we don't update our images
// or fonts frequently
response.setHeader('Cache-Control', 'public, max-age=2592000, must-revalidate');
}
};
const init=(pathToAssets)=>{
return expressStaticGzip(pathToAssets, {
enableBrotli : true,
orderPreference : ['br'],
index : false,
serveStatic : {
cacheControl : false, // we are going to use custom cache-control
setHeaders : customCacheControlHandler
} });
};
module.exports = init;

33
server/token.js Normal file
View File

@@ -0,0 +1,33 @@
const jwt = require('jwt-simple');
// Load configuration values
const config = require('nconf')
.argv()
.env({ lowerCase: true }) // Load environment variables
.file('environment', { file: `config/${process.env.NODE_ENV}.json` })
.file('defaults', { file: 'config/default.json' });
// Generate an Access Token for the given User ID
const generateAccessToken = (account)=>{
const payload = account;
// When the token was issued
payload.issued = (new Date());
// Which service issued the Token
payload.issuer = config.get('authentication_token_issuer');
// Which service is the token intended for
payload.audience = config.get('authentication_token_audience');
// The signing key for signing the token
delete payload.password;
delete payload._id;
const secret = config.get('authentication_token_secret');
const token = jwt.encode(payload, secret);
return token;
};
module.exports = {
generateAccessToken : generateAccessToken
};

View File

@@ -53,8 +53,8 @@ const RenderWarnings = createClass({
if(_.isEmpty(this.state.warnings)) return null;
return <div className='renderWarnings'>
<i className='fa fa-times dismiss' onClick={this.dismiss}/>
<i className='fa fa-exclamation-triangle ohno' />
<i className='fas fa-times dismiss' onClick={this.dismiss}/>
<i className='fas fa-exclamation-triangle ohno' />
<h3>Render Warnings</h3>
<small>If this homebrew is rendering badly if might be because of the following:</small>
<ul>{_.values(this.state.warnings)}</ul>

View File

@@ -11,75 +11,97 @@ if(typeof navigator !== 'undefined'){
//Language Modes
require('codemirror/mode/gfm/gfm.js'); //Github flavoured markdown
require('codemirror/mode/css/css.js');
require('codemirror/mode/javascript/javascript.js');
}
const CodeEditor = createClass({
getDefaultProps : function() {
return {
language : '',
value : '',
wrap : false,
onChange : function(){},
onCursorActivity : function(){},
language : '',
value : '',
wrap : true,
onChange : ()=>{}
};
},
componentDidMount : function() {
this.buildEditor();
},
componentDidUpdate : function(prevProps) {
if(prevProps.language !== this.props.language){ //rebuild editor when switching tabs
this.buildEditor();
}
if(this.codeMirror && this.codeMirror.getValue() != this.props.value) { //update editor contents if brew.text is changed from outside
this.codeMirror.setValue(this.props.value);
}
},
buildEditor : function() {
this.codeMirror = CodeMirror(this.refs.editor, {
value : this.props.value,
lineNumbers : true,
lineWrapping : this.props.wrap,
mode : this.props.language,
extraKeys : {
value : this.props.value,
lineNumbers : true,
lineWrapping : this.props.wrap,
mode : this.props.language, //TODO: CSS MODE DOESN'T SEEM TO LOAD PROPERLY
indentWithTabs : true,
tabSize : 2,
extraKeys : {
'Ctrl-B' : this.makeBold,
'Ctrl-I' : this.makeItalic
'Cmd-B' : this.makeBold,
'Ctrl-I' : this.makeItalic,
'Cmd-I' : this.makeItalic,
'Ctrl-M' : this.makeSpan,
'Cmd-M' : this.makeSpan,
}
});
this.codeMirror.on('change', this.handleChange);
this.codeMirror.on('cursorActivity', this.handleCursorActivity);
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
this.updateSize();
},
makeBold : function() {
const selection = this.codeMirror.getSelection();
this.codeMirror.replaceSelection(`**${selection}**`, 'around');
},
makeItalic : function() {
const selection = this.codeMirror.getSelection();
this.codeMirror.replaceSelection(`*${selection}*`, 'around');
},
componentWillReceiveProps : function(nextProps){
if(this.codeMirror && nextProps.value !== undefined && this.codeMirror.getValue() != nextProps.value) {
this.codeMirror.setValue(nextProps.value);
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '**' && selection.slice(-2) === '**';
this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `**${selection}**`, 'around');
if(selection.length === 0){
const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 });
}
},
shouldComponentUpdate : function(nextProps, nextState) {
return false;
makeItalic : function() {
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 1) === '_' && selection.slice(-1) === '_';
this.codeMirror.replaceSelection(t ? selection.slice(1, -1) : `_${selection}_`, 'around');
if(selection.length === 0){
const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 1 });
}
},
makeSpan : function() {
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '{{' && selection.slice(-2) === '}}';
this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `{{ ${selection}}}`, 'around');
if(selection.length === 0){
const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 });
}
},
//=-- Externally used -==//
setCursorPosition : function(line, char){
setTimeout(()=>{
this.codeMirror.focus();
this.codeMirror.doc.setCursor(line, char);
}, 10);
},
getCursorPosition : function(){
return this.codeMirror.getCursor();
},
updateSize : function(){
this.codeMirror.refresh();
},
handleChange : function(editor){
this.props.onChange(editor.getValue());
},
handleCursorActivity : function(){
this.props.onCursorActivity(this.codeMirror.doc.getCursor());
},
//----------------------//
render : function(){
return <div className='codeEditor' ref='editor' />;

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
const _ = require('lodash');
const Markdown = require('marked');
const renderer = new Markdown.Renderer();
@@ -13,6 +14,300 @@ renderer.html = function (html) {
return html;
};
// Don't wrap {{ Divs or {{ empty Spans in <p> tags
renderer.paragraph = function(text){
let match;
if(text.startsWith('<div') || text.startsWith('</div'))
return `${text}`;
else if(match = text.match(/(^|^.*?\n)<span class="inline(.*?<\/span>)$/)) {
return `${match[1].trim() ? `<p>${match[1]}</p>` : ''}<span class="inline-block${match[2]}`;
} else
return `<p>${text}</p>\n`;
};
const mustacheSpans = {
name : 'mustacheSpans',
level : 'inline', // Is this a block-level or inline-level tokenizer?
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
const inlineRegex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g;
const match = completeSpan.exec(src);
if(match) {
//Find closing delimiter
let blockCount = 0;
let tags = '';
let endTags = 0;
let endToken = 0;
let delim;
while (delim = inlineRegex.exec(match[0])) {
if(!tags) {
tags = ` ${processStyleTags(delim[0].substring(2))}`;
endTags = delim[0].length;
}
if(delim[0].startsWith('{{')) {
blockCount++;
} else if(delim[0] == '}}' && blockCount !== 0) {
blockCount--;
if(blockCount == 0) {
endToken = inlineRegex.lastIndex;
break;
}
}
}
if(endToken) {
const raw = src.slice(0, endToken);
const text = raw.slice(endTags || -2, -2);
return { // Token to generate
type : 'mustacheSpans', // Should match "name" above
raw : raw, // Text to consume from the source
text : text, // Additional custom properties
tags : tags,
tokens : this.inlineTokens(text) // inlineTokens to process **bold**, *italics*, etc.
};
}
}
},
renderer(token) {
return `<span class="inline${token.tags}>${this.parseInline(token.tokens)}</span>`; // parseInline to turn child tokens into HTML
}
};
const mustacheDivs = {
name : 'mustacheDivs',
level : 'block',
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
const blockRegex = /^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/gm;
const match = completeBlock.exec(src);
if(match) {
//Find closing delimiter
let blockCount = 0;
let tags = '';
let endTags = 0;
let endToken = 0;
let delim;
while (delim = blockRegex.exec(match[0])?.[0].trim()) {
if(!tags) {
tags = ` ${processStyleTags(delim.substring(2))}`;
endTags = delim.length;
}
if(delim.startsWith('{{')) {
blockCount++;
} else if(delim == '}}' && blockCount !== 0) {
blockCount--;
if(blockCount == 0) {
endToken = blockRegex.lastIndex;
break;
}
}
}
if(endToken) {
const raw = src.slice(0, endToken);
const text = raw.slice(endTags || -2, -2);
return { // Token to generate
type : 'mustacheDivs', // Should match "name" above
raw : raw, // Text to consume from the source
text : text, // Additional custom properties
tags : tags,
tokens : this.inline(this.blockTokens(text))
};
}
}
},
renderer(token) {
return `<div class="block${token.tags}>${this.parse(token.tokens)}</div>`; // parseInline to turn child tokens into HTML
}
};
const mustacheInjectInline = {
name : 'mustacheInjectInline',
level : 'inline',
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
const inlineRegex = /^ *{((?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*)}/g;
const match = inlineRegex.exec(src);
if(match) {
const lastToken = tokens[tokens.length - 1];
if(!lastToken)
return false;
const tags = ` ${processStyleTags(match[1])}`;
lastToken.originalType = lastToken.type;
lastToken.type = 'mustacheInjectInline';
lastToken.tags = tags;
return {
type : 'text', // Should match "name" above
raw : match[0], // Text to consume from the source
text : ''
};
}
},
renderer(token) {
token.type = token.originalType;
const text = this.parseInline([token]);
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
if(openingTag) {
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
}
return text;
}
};
const mustacheInjectBlock = {
extensions : [{
name : 'mustacheInjectBlock',
level : 'block',
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
const inlineRegex = /^ *{((?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*)}/ym;
const match = inlineRegex.exec(src);
if(match) {
const lastToken = tokens[tokens.length - 1];
if(!lastToken)
return false;
lastToken.originalType = 'mustacheInjectBlock';
lastToken.tags = ` ${processStyleTags(match[1])}`;
return {
type : 'text', // Should match "name" above
raw : match[0], // Text to consume from the source
text : ''
};
}
},
renderer(token) {
token.type = token.originalType;
const text = this.parse([token]);
const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text);
if(openingTag) {
return `${openingTag[1]} class="${token.tags}${openingTag[2]}`;
}
return text;
}
}],
walkTokens(token) {
// After token tree is finished, tag tokens to apply styles to so Renderer can find them
// Does not work with tables since Marked.js tables generate invalid "tokens", and changing "type" ruins Marked handling that edge-case
if(token.originalType == 'mustacheInjectBlock' && token.type !== 'table') {
token.originalType = token.type;
token.type = 'mustacheInjectBlock';
}
}
};
const definitionLists = {
name : 'definitionLists',
level : 'block',
start(src) { return src.match(/^.*?::.*/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
const regex = /^([^\n]*?)::([^\n]*)/ym;
let match;
let endIndex = 0;
const definitions = [];
while (match = regex.exec(src)) {
definitions.push({
dt : this.inlineTokens(match[1].trim()),
dd : this.inlineTokens(match[2].trim())
});
endIndex = regex.lastIndex;
}
if(definitions.length) {
return {
type : 'definitionLists',
raw : src.slice(0, endIndex),
definitions
};
}
},
renderer(token) {
return `<dl>
${token.definitions.reduce((html, def)=>{
return `${html}<dt>${this.parseInline(def.dt)}</dt>`
+ `<dd>${this.parseInline(def.dd)}</dd>\n`;
}, '')}
</dl>`;
}
};
Markdown.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists] });
Markdown.use(mustacheInjectBlock);
Markdown.use({ smartypants: true });
//Fix local links in the Preview iFrame to link inside the frame
renderer.link = function (href, title, text) {
let self = false;
if(href[0] == '#') {
self = true;
}
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
if(href === null) {
return text;
}
let out = `<a href="${escape(href)}"`;
if(title) {
out += ` title="${title}"`;
}
if(self) {
out += ' target="_self"';
}
out += `>${text}</a>`;
return out;
};
const nonWordAndColonTest = /[^\w:]/g;
const cleanUrl = function (sanitize, base, href) {
if(sanitize) {
let prot;
try {
prot = decodeURIComponent(unescape(href))
.replace(nonWordAndColonTest, '')
.toLowerCase();
} catch (e) {
return null;
}
if(prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
return null;
}
}
try {
href = encodeURI(href).replace(/%25/g, '%');
} catch (e) {
return null;
}
return href;
};
const escapeTest = /[&<>"']/;
const escapeReplace = /[&<>"']/g;
const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
const escapeReplacements = {
'&' : '&amp;',
'<' : '&lt;',
'>' : '&gt;',
'"' : '&quot;',
'\'' : '&#39;'
};
const getEscapeReplacement = (ch)=>escapeReplacements[ch];
const escape = function (html, encode) {
if(encode) {
if(escapeTest.test(html)) {
return html.replace(escapeReplace, getEscapeReplacement);
}
} else {
if(escapeTestNoEncode.test(html)) {
return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
}
}
return html;
};
const sanatizeScriptTags = (content)=>{
return content
.replace(/<script/ig, '&lt;script')
@@ -25,10 +320,24 @@ const tagRegex = new RegExp(`(${
return `\\<${type}|\\</${type}>`;
}).join('|')})`, 'g');
const processStyleTags = (string)=>{
//split tags up. quotes can only occur right after colons.
//TODO: can we simplify to just split on commas?
const tags = string.match(/(?:[^, ":]+|:(?:"[^"]*"|))+/g);
if(!tags) return '"';
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0];
const classes = _.remove(tags, (tag)=>!tag.includes(':'));
const styles = tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;'));
return `${classes.join(' ')}" ${id ? `id="${id}"` : ''} ${styles.length ? `style="${styles.join(' ')}"` : ''}`;
};
module.exports = {
marked : Markdown,
render : (rawBrewText)=>{
rawBrewText = rawBrewText.replace(/^\\column$/gm, `<div class='columnSplit'></div>`)
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
return Markdown(
sanatizeScriptTags(rawBrewText),
{ renderer: renderer }
@@ -87,4 +396,3 @@ module.exports = {
return errors;
},
};

View File

@@ -0,0 +1,166 @@
const _ = require('lodash');
const Markdown = require('markedLegacy');
const renderer = new Markdown.Renderer();
//Processes the markdown within an HTML block if it's just a class-wrapper
renderer.html = function (html) {
if(_.startsWith(_.trim(html), '<div') && _.endsWith(_.trim(html), '</div>')){
const openTag = html.substring(0, html.indexOf('>')+1);
html = html.substring(html.indexOf('>')+1);
html = html.substring(0, html.lastIndexOf('</div>'));
return `${openTag} ${Markdown(html)} </div>`;
}
// if(_.startsWith(_.trim(html), '<style') && _.endsWith(_.trim(html), '</style>')){
// const openTag = html.substring(0, html.indexOf('>')+1);
// html = html.substring(html.indexOf('>')+1);
// html = html.substring(0, html.lastIndexOf('</style>'));
// html = html.replaceAll(/\s(\.[^{]*)/gm, '.legacy $1');
// return `${openTag} ${html} </style>`;
// }
return html;
};
renderer.link = function (href, title, text) {
let self = false;
if(href[0] == '#') {
self = true;
}
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
if(href === null) {
return text;
}
let out = `<a href="${escape(href)}"`;
if(title) {
out += ` title="${title}"`;
}
if(self) {
out += ' target="_self"';
}
out += `>${text}</a>`;
return out;
};
const nonWordAndColonTest = /[^\w:]/g;
const cleanUrl = function (sanitize, base, href) {
if(sanitize) {
let prot;
try {
prot = decodeURIComponent(unescape(href))
.replace(nonWordAndColonTest, '')
.toLowerCase();
} catch (e) {
return null;
}
if(prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
return null;
}
}
try {
href = encodeURI(href).replace(/%25/g, '%');
} catch (e) {
return null;
}
return href;
};
const escapeTest = /[&<>"']/;
const escapeReplace = /[&<>"']/g;
const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
const escapeReplacements = {
'&' : '&amp;',
'<' : '&lt;',
'>' : '&gt;',
'"' : '&quot;',
'\'' : '&#39;'
};
const getEscapeReplacement = (ch)=>escapeReplacements[ch];
const escape = function (html, encode) {
if(encode) {
if(escapeTest.test(html)) {
return html.replace(escapeReplace, getEscapeReplacement);
}
} else {
if(escapeTestNoEncode.test(html)) {
return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
}
}
return html;
};
const sanatizeScriptTags = (content)=>{
return content
.replace(/<script/ig, '&lt;script')
.replace(/<\/script>/ig, '&lt;/script&gt;');
};
const tagTypes = ['div', 'span', 'a'];
const tagRegex = new RegExp(`(${
_.map(tagTypes, (type)=>{
return `\\<${type}|\\</${type}>`;
}).join('|')})`, 'g');
module.exports = {
marked : Markdown,
render : (rawBrewText)=>{
return Markdown(
sanatizeScriptTags(rawBrewText),
{ renderer: renderer }
);
},
validate : (rawBrewText)=>{
const errors = [];
const leftovers = _.reduce(rawBrewText.split('\n'), (acc, line, _lineNumber)=>{
const lineNumber = _lineNumber + 1;
const matches = line.match(tagRegex);
if(!matches || !matches.length) return acc;
_.each(matches, (match)=>{
_.each(tagTypes, (type)=>{
if(match == `<${type}`){
acc.push({
type : type,
line : lineNumber
});
}
if(match === `</${type}>`){
if(!acc.length){
errors.push({
line : lineNumber,
type : type,
text : 'Unmatched closing tag',
id : 'CLOSE'
});
} else if(_.last(acc).type == type){
acc.pop();
} else {
errors.push({
line : `${_.last(acc).line} to ${lineNumber}`,
type : type,
text : 'Type mismatch on closing tag',
id : 'MISMATCH'
});
acc.pop();
}
}
});
});
return acc;
}, []);
_.each(leftovers, (unmatched)=>{
errors.push({
line : unmatched.line,
type : unmatched.type,
text : 'Unmatched opening tag',
id : 'OPEN'
});
});
return errors;
},
};

View File

@@ -17,7 +17,7 @@ const Nav = {
}
}),
logo : function(){
return <a className='navLogo' href='http://naturalcrit.com'>
return <a className='navLogo' href='https://www.naturalcrit.com'>
<NaturalCritIcon />
<span className='name'>
Natural<span className='crit'>Crit</span>
@@ -50,7 +50,7 @@ const Nav = {
const classes = cx('navItem', this.props.color, this.props.className);
let icon;
if(this.props.icon) icon = <i className={`fa ${this.props.icon}`} />;
if(this.props.icon) icon = <i className={this.props.icon} />;
const props = _.omit(this.props, ['newTab']);

View File

@@ -1,7 +1,7 @@
//@import (less) 'naturalcrit/styles/style.fonts.css';
nav{
background-color : #333;
.navContent{
position : relative;
display : flex;
justify-content : space-between;
}

View File

@@ -54,11 +54,11 @@ const SplitPane = createClass({
},
*/
renderDivider : function(){
return <div className='divider' onMouseDown={this.handleDown}>
return <div className='divider' onMouseDown={this.handleDown} >
<div className='dots'>
<i className='fa fa-circle' />
<i className='fa fa-circle' />
<i className='fa fa-circle' />
<i className='fas fa-circle' />
<i className='fas fa-circle' />
<i className='fas fa-circle' />
</div>
</div>;
},
@@ -67,16 +67,11 @@ const SplitPane = createClass({
return <div className='splitPane' onMouseMove={this.handleMove} onMouseUp={this.handleUp}>
<Pane ref='pane1' width={this.state.size}>{this.props.children[0]}</Pane>
{this.renderDivider()}
<Pane ref='pane2'>{this.props.children[1]}</Pane>
<Pane ref='pane2' isDragging={this.state.isDragging}>{this.props.children[1]}</Pane>
</div>;
}
});
const Pane = createClass({
getDefaultProps : function() {
return {
@@ -90,12 +85,16 @@ const Pane = createClass({
flex : 'none',
width : `${this.props.width}px`
};
} else {
styles = {
pointerEvents : this.props.isDragging ? 'none' : 'auto' //Disable mouse capture in the rightmost pane; dragging into the iframe drops the divider otherwise
};
}
return <div className={cx('pane', this.props.className)} style={styles}>
{this.props.children}
</div>;
}
});
module.exports = SplitPane;

View File

@@ -15,7 +15,7 @@
height : 100%;
width : 12px;
cursor : ew-resize;
background-color : #ddd;
background-color : #bbb;
text-align : center;
.dots{
display : table-cell;

651
themes/5ePhb.style.less Normal file
View File

@@ -0,0 +1,651 @@
@import (less) './themes/fonts/5e/fonts.less';
@import (less) './themes/assets/assets.less';
//Colors
@background : #EEE5CE;
@noteGreen : #e0e5c1;
@headerUnderline : #c9ad6a;
@horizontalRule : #9c2b1b;
@headerText : #58180D;
@monsterStatBackground : #EEDBAB;
@page { margin: 0; }
body {
counter-reset : phb-page-numbers;
}
*{
-webkit-print-color-adjust : exact;
}
.useSansSerif(){
font-family : ScalySansRemake;
font-size : 0.325cm;
line-height : 1.2em;
p,dl,ul,ol {
line-height : 1.2em;
}
ul, ol {
padding-left : 1em;
}
em{
font-style : italic;
}
strong{
font-weight : 800;
letter-spacing : -0.02em;
}
}
.useColumns(@multiplier : 1){
column-count : 2;
column-fill : auto;
column-gap : 0.9cm;
column-width : 8cm * @multiplier;
-webkit-column-count : 2;
-moz-column-count : 2;
-webkit-column-width : 8cm * @multiplier;
-moz-column-width : 8cm * @multiplier;
-webkit-column-gap : 0.9cm;
-moz-column-gap : 0.9cm;
}
.page{
.useColumns();
counter-increment : phb-page-numbers;
position : relative;
z-index : 15;
box-sizing : border-box;
overflow : hidden;
height : 279.4mm;
width : 215.9mm;
padding : 1.4cm 1.9cm 1.7cm;
background-color : @background;
background-image : @backgroundImage;
font-family : BookInsanityRemake;
font-size : 0.34cm;
text-rendering : optimizeLegibility;
page-break-before : always;
page-break-after : always;
//*****************************
// * BASE
// *****************************/
p{
overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
margin-bottom : 0.8em;
line-height : 1.3em;
&+p{
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;
}
ol{
margin-bottom : 0.8em;
padding-left : 1.4em;
line-height : 1.3em;
list-style-position : outside;
list-style-type : decimal;
}
//Indents after p or lists
p+p, ul+p, ol+p{
text-indent : 1em;
}
img{
z-index : -1;
}
strong{
font-weight : bold;
letter-spacing : -0.02em;
}
em{
font-style : italic;
}
sup{
vertical-align : super;
font-size : smaller;
line-height : 0;
}
sub{
vertical-align : sub;
font-size : smaller;
line-height : 0;
}
//*****************************
// * HEADERS
// *****************************/
h1,h2,h3,h4{
font-family : MrEavesRemake;
font-weight : 800;
color : @headerText;
}
h1{
margin-bottom : 0.18cm;
column-span : all;
font-size : 0.89cm;
-webkit-column-span : all;
-moz-column-span : all;
&+p::first-letter{
float : left;
font-family : SolberaImitationRemake;
line-height : 0.8em;
font-size: 3.5cm;
padding-left: 40px;
margin-left: -40px;
padding-top:10px;
margin-top:-8px;
padding-bottom:10px;
margin-bottom:-20px;
background-image: linear-gradient(-45deg, #322814, #998250, #322814);
background-clip: text;
-webkit-background-clip: text;
color: rgba(0, 0, 0, 0);
}
&+p::first-line{
font-variant : small-caps;
}
}
h2{
margin-top : 0px;
margin-bottom : 0.05cm;
font-size : 0.75cm;
}
h3{
margin-top : -0.1cm;
margin-bottom : 0.1cm;
font-size : 0.575cm;
border-bottom : 2px solid @headerUnderline;
}
h4{
margin-top : -0.02cm;
margin-bottom : 0.02cm;
font-size : 0.458cm;
}
h5{
margin-top : -0.02cm;
margin-bottom : 0.02cm;
font-family : ScalySansSmallCapsRemake;
font-size : 0.423cm;
font-weight : 900;
}
//*****************************
// * TABLE
// *****************************/
table{
.useSansSerif();
width : 100%;
margin-bottom : 1em;
thead{
display: table-row-group;
font-weight : 800;
th{
vertical-align : bottom;
padding : 0.14em 0.4em;
}
}
tbody{
tr{
td{
padding : 0.14em 0.4em;
}
&:nth-child(odd){
background-color : @noteGreen;
}
}
}
}
//*****************************
// * NOTE
// *****************************/
.note{
&::before{
content : "";
box-sizing : border-box;
border-style : solid;
border-width : 11px;
border-image : @noteBorderImage 12;
border-image-outset : 9px 0px;
box-shadow : 1px 4px 14px #888;
position : absolute;
width : 100%;
height : 100%;
top : 0;
left : 0;
}
.useSansSerif();
position : relative;
margin-top : 1.3em;
margin-left : -0.1em;
margin-right : -0.1em;
background-color : @noteGreen;
padding : 0.5em 0.6em;
& + * {
margin-top : 1.3em;
}
p{
display : block;
padding-bottom : 0px;
}
p + p {
padding-top : .8em;
}
:last-child {
margin-bottom : 0em;
}
}
//************************************
// * DESCRIPTIVE TEXT BOX
// ************************************/
.descriptive{
.useSansSerif();
display : block-inline;
margin-top : 1.4em;
background-color : #faf7ea;
font-family : ScalySansRemake;
border-style : solid;
border-width : 7px;
border-image : @descriptiveBoxImage 12 stretch;
border-image-outset : 4px;
box-shadow : 0px 0px 6px #faf7ea;
padding : 0.1em;
& + * {
margin-top : 1.4em;
}
p{
display : block;
padding-bottom : 0px;
line-height : 1.5em;
}
p + p {
padding-top : .8em;
}
:last-child {
margin-bottom : 0em;
}
}
//*****************************
// * MONSTER STAT BLOCK
// *****************************/
.monster {
&.frame {
border-style : solid;
border-width : 7px 6px;
background-color : @monsterStatBackground;
background-image : @monsterBlockBackground;
border-image : @monsterBorderImage 14 round;
border-image-outset : 0px 2px;
background-blend-mode : overlay;
background-attachment : fixed;
box-shadow : 1px 4px 14px #888;
padding : 4px 2px;
margin : 0px -6px 1em;
}
.useSansSerif();
//-webkit-transform : translateZ(0); //Prevents shadows from breaking across columns, but breaks internal columns...
position : relative;
padding : 0px;
margin-bottom : 1em;
p{
margin-bottom : 0.3cm;
}
p+p {
margin-top : 0; //May not be needed
text-indent : 0;
}
p:last-of-type {
margin-bottom: 0;
}
//Headers
h2{
font-size : 0.62cm;
line-height : 1em;
margin : 0;
&+p {
font-size : 0.304cm; //Monster size and type subtext
margin-bottom : 0;
}
}
h3{
font-family : ScalySansRemake;
font-weight : 800;
font-variant : small-caps;
border-bottom : 2px solid @headerText;
margin-top : 0.05cm;
padding-bottom : 0.05cm;
}
//Triangle dividers
hr{
visibility : visible;
height : 6px;
margin : 0.12cm 0cm;
background-image : @redTriangleImage;
background-size : 100% 100%;
border : none;
}
//Attribute Lists
dl {
color : @headerText;
}
// Monster Ability table
hr + table:first-of-type{
margin : 0;
column-span : 1;
color : @headerText;
background-color : transparent;
border-style : none;
border-image : none;
-webkit-column-span : 1;
tr {
background-color : transparent;
}
td,th {
padding: 0px;
}
}
}
//Full Width
.monster.wide{
.useColumns(0.96);
}
hr+hr+blockquote{
.useColumns(0.96);
}
//*****************************
// * FOOTER
// *****************************/
&:after{
content : "";
position : absolute;
bottom : 0px;
left : 0px;
z-index : 100;
height : 50px;
width : 100%;
background-image : @footerAccentImage;
background-size : cover;
}
&:nth-child(even){
&:after{
transform : scaleX(-1);
}
.pageNumber{
left : 2px;
}
.footnote{
left : 80px;
text-align : left;
}
}
.pageNumber{
position : absolute;
right : 2px;
bottom : 22px;
width : 50px;
font-size : 0.9em;
color : #c9ad6a;
text-align : center;
text-indent : 0;
&.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;
text-align : right;
}
//************************************
// * CODE BLOCKS
// ************************************/
code{
font-family: "Courier New", Courier, monospace;
font-size: 0.325;
padding: 2px 4px;
color: #58180d;
background-color: #faf7ea;
border-radius: 4px;
}
pre code{
width : 100%;
display : block;
border : 4px solid;
border-image : @codeBorderImage 26 stretch;
border-image-width : 10px;
border-image-outset : 2px;
border-radius : 12px;
}
//*****************************
// * EXTRAS
// *****************************/
hr{
visibility : hidden;
margin : 0px;
}
//Modified unorder list, used in spells
hr+ul{
margin-bottom : 0.5em;
padding-left : 1em;
text-indent : -1em;
list-style-type : none;
}
.columnSplit {
visibility : hidden;
-webkit-column-break-after : always;
break-after : always;
-moz-column-break-after : always;
break-before : column;
}
//Avoid breaking up
p,blockquote,table{
z-index : 15;
-webkit-column-break-inside : avoid;
page-break-inside : avoid;
break-inside : avoid;
}
//Better spacing for spell blocks
h4+p+hr+ul{
margin-top : -0.5em
}
//Text indent right after table
table+p{
text-indent : 1em;
}
// Nested lists
ul ul,ol ol,ul ol,ol ul{
margin-bottom : 0px;
margin-left : 1.5em;
}
li{
-webkit-column-break-inside : avoid;
page-break-inside : avoid;
break-inside : avoid;
}
}
//*****************************
// * SPELL LIST
// *****************************/
.page .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
// *****************************/
.page .wide{
column-span : all;
-webkit-column-span : all;
-moz-column-span : all;
}
//*****************************
// * CLASS TABLE
// *****************************/
.page .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;
}
}
//*****************************
// * TABLE OF CONTENTS
// *****************************/
.page .toc{
-webkit-column-break-inside : avoid;
page-break-inside : avoid;
break-inside : avoid;
h1 {
text-align : center;
margin-bottom : 0.1cm;
}
a{
display : table;
color : inherit;
text-decoration : none;
&:hover{
text-decoration : underline;
}
}
h4 {
margin-top : 0.1cm;
}
ul{
padding-left : 0;
list-style-type : none;
li + li h3 {
margin-top : 0.26cm;
line-height : 1em
}
h3 span:first-child::after {
border : none;
}
span {
display : table-cell;
&:first-child {
position : relative;
overflow : hidden;
&::after {
content : "";
position : absolute;
bottom : 0.08cm; /* Set as you want */
margin-left : 0.06cm; /* Spacing before dot leaders */
width : 100%;
border-bottom : 0.05cm dotted #000;
}
}
&:last-child {
font-family : BookInsanityRemake;
font-size : 0.34cm;
font-weight : normal;
color : black;
text-align : right;
vertical-align : bottom; /* Keep Price text bottom-aligned */
width : 1%;
padding-left : 0.06cm; /* Spacing after dot leaders */
/*white-space: nowrap; /* Uncomment if needed */
}
}
ul { /*List indent*/
margin-left : 1em;
}
}
&.wide{
.useColumns(0.96);
}
}
//*****************************
// * MUSTACHE DIVS/SPANS
// *****************************/
.page {
.block {
break-inside : avoid;
-webkit-transform : translateZ(0); //Prevents shadows from breaking across columns
}
.inline-block {
display : inline-block;
text-indent : initial;
line-height : 1.3em;
}
div {
column-gap : 0.5cm; //Default spacing if a div uses multicolumns
}
}
//*****************************
// * DEFINITION LISTS
// *****************************/
.page {
dl {
line-height : 1.3em;
padding-left : 1em;
text-indent : -1em;
}
dl + p {
margin-top: 0.5em;
}
p + dl {
margin-top: -0.5em;
}
dt {
float: left;
//clear: left; //Doesn't seem necessary
margin-right: 5px;
}
dd {
margin-left : 0px;
text-indent : 0px;
}
}
//*****************************
// * BLANK LINE
// *****************************/
.page {
.blank {
height: 0.75em;
}
p + .blank {
margin-top: -1em;
}
}

View File

@@ -1,7 +1,6 @@
@import (less) 'shared/naturalcrit/styles/reset.less';
@import (less) './client/homebrew/phbStyle/phb.fonts.css';
@import (less) './client/homebrew/phbStyle/phb.assets.less';
@import (less) './client/homebrew/phbStyle/phb.depricated.less';
@import (less) './themes/fonts/5e legacy/fonts.less';
@import (less) './themes/assets/assets.less';
@import (less) './themes/phb.depricated.less';
//Colors
@background : #EEE5CE;
@noteGreen : #e0e5c1;
@@ -207,7 +206,7 @@ body {
background-color : @monsterStatBackground;
border-style : solid;
border-width : 10px;
border-image : @monsterBorderImage 10;
border-image : @monsterBorderImageLegacy 10;
h2{
margin-top : -8px;
margin-bottom : 0px;

10
themes/assets/assets.less Normal file
View File

@@ -0,0 +1,10 @@
@footerAccentImage : data-uri('./themes/assets/footerAccent.png');
@frameBorderImage : data-uri('./themes/assets/frameBorder.png');
@backgroundImage : data-uri('./themes/assets/parchmentBackground.jpg');
@redTriangleImage : data-uri('./themes/assets/redTriangle.png');
@monsterBorderImageLegacy : data-uri('./themes/assets/monsterBorderLegacy.png');
@noteBorderImage : data-uri('./themes/assets/noteBorder.png');
@descriptiveBoxImage : data-uri('./themes/assets/descriptiveBorder.png');
@monsterBlockBackground : data-uri('./themes/assets/parchmentBackgroundGrayscale.jpg');
@monsterBorderImage : data-uri('./themes/assets/monsterBorderFancy.png');
@codeBorderImage : data-uri('./themes/assets/codeBorder.png');

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

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