From e367cb2152ec2c74eebd85ab7ec52f1df51458d9 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 23 Jan 2021 00:36:10 +1300 Subject: [PATCH 01/46] Initial commit - adds a `New` button to the Nav bar on the Home, User, and Edit pages. --- client/homebrew/navbar/newbrew.navitem.jsx | 32 +++++++++++++++++++++ client/homebrew/pages/editPage/editPage.jsx | 2 ++ client/homebrew/pages/homePage/homePage.jsx | 2 ++ client/homebrew/pages/userPage/userPage.jsx | 2 ++ 4 files changed, 38 insertions(+) create mode 100644 client/homebrew/navbar/newbrew.navitem.jsx diff --git a/client/homebrew/navbar/newbrew.navitem.jsx b/client/homebrew/navbar/newbrew.navitem.jsx new file mode 100644 index 000000000..404b5c063 --- /dev/null +++ b/client/homebrew/navbar/newbrew.navitem.jsx @@ -0,0 +1,32 @@ +const React = require('react'); +const createClass = require('create-react-class'); +const Nav = require('naturalcrit/nav/nav.jsx'); + +const NewBrew = createClass({ + + getInitialState : function() { + return { + url : '' + }; + }, + + componentDidMount : function(){ + if(typeof window !== 'undefined'){ + this.setState({ + url : window.location.href + }); + } + }, + + render : function(){ + if(global.account){ + return + new + ; + } + + return; + } +}); + +module.exports = NewBrew; diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index d75b647e6..1988f9434 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -9,6 +9,7 @@ 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'); @@ -348,6 +349,7 @@ const EditPage = createClass({ + {this.renderGoogleDriveIcon()} {this.renderSaveButton()} diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index e245b990b..1172c3131 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -7,6 +7,7 @@ const { Meta } = require('vitreum/headtags'); const Nav = require('naturalcrit/nav/nav.jsx'); const Navbar = require('../../navbar/navbar.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'); @@ -54,6 +55,7 @@ const HomePage = createClass({ renderNavbar : function(){ return + Changelog diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 5081a4942..a48e86ae4 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -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 = { @@ -54,6 +55,7 @@ const UserPage = createClass({ return
+ From 6a2e39355c2f17b1716ad2cbb5db5a396cbe5fa3 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 25 Jan 2021 21:47:36 -0500 Subject: [PATCH 02/46] 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 Co-authored-by: Trevor Buckner * 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] 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] 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] 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] 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] 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] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Trevor Buckner * 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] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Trevor Buckner * 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 * Up Version Co-authored-by: G.Ambatte Co-authored-by: Sean Robertson Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: ericscheid --- changelog.md | 34 +++-- .../homebrew/editor/snippetbar/snippetbar.jsx | 7 +- .../editor/snippetbar/snippetbar.less | 55 ++++----- client/homebrew/phbStyle/phb.style.less | 1 + package-lock.json | 116 +++++++++--------- package.json | 16 +-- server/homebrew.api.js | 10 +- shared/naturalcrit/splitPane/splitPane.less | 2 +- 8 files changed, 119 insertions(+), 122 deletions(-) diff --git a/changelog.md b/changelog.md index 8381a8d9a..c666d3d7a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # changelog +### Friday, 25/1/2021 - v2.10.7 +- Cover Page snippet now flips left-right page numbering. +- Added instructions for [installing on a FreeBSD Jail](https://github.com/naturalcrit/homebrewery/blob/master/README.FREEBSD.md). +- Fix for box-shadows breaking across columns.
(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 @@ -27,6 +33,9 @@ ### 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 @@ -37,9 +46,6 @@ ### 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 @@ -73,7 +79,9 @@ ### 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) -### Thursday, 19/01/2017 - v2.7.0 +\page + +### Thursday, 19/01/2017 - v2.7.1 - Fixed saving multiple authors and multiple systems on brew metadata (thanks u/PalaNolho re:282) - Adding in line highlight for new pages - Added in a simple brew lookup for admin @@ -81,8 +89,6 @@ ### 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 -\page - ### Sunday, 25/12/2016 - v2.7.0 - Switching over to using Vitreum v4 - Removed gulp, all tasks are run through npm scripts @@ -115,6 +121,10 @@ - Added a hover tooltip to fully read the brew description - Made the brew items take up only 25% allowing you to view more per row. + +``` +``` + ### Wednesday, 23/11/2016 - v2.5.0 - Metadata can now be added to brews - Added a metadata editor onto the edit and new pages @@ -125,8 +135,6 @@ - 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 @@ -163,11 +171,12 @@ - Allows adding in hyperlinks to specific pages - Even works after you print to pdf! -### Tuesday, 07/06/2016 - v2.2.2 -- Fixed bug with new markdown lexer and aprser not working on print page \page +### Tuesday, 07/06/2016 - v2.2.2 +- Fixed bug with new markdown lexer and aprser not working on print page + ### Sunday, 05/06/2016 - v2.2.1 - Adding in a new Class table div block. The old Class table block used weird stacking of HTML elements, resulting is difficult to control behaviour and poor interactiosn with the rest of the page. This new block is much easier to style and work with. - Added in a new wide table snippet @@ -188,12 +197,13 @@ - Updated the issue template for (hopefully) better reporting - Added suggestion to use chrome while PDF printing -### Wednesday, 25/05/2016 -v2.0.5 -- The class table generators have the proper ability score improvement progression. ``` ``` +### Wednesday, 25/05/2016 -v2.0.5 +- The class table generators have the proper ability score improvement progression. + ### Tuesday, 24/05/2016 - v2.0.4 - Fixed extra wide monster stat blocks sometimes only being one column - The class table generators now follow the proper progression from the PHB (thakns u/IrishBandit) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 18fcdb9af..c347e397f 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -42,9 +42,10 @@ const Snippetbar = createClass({ renderMetadataButton : function(){ if(!this.props.showMetaButton) return; - return
- + + Properties
; }, @@ -86,7 +87,7 @@ const SnippetGroup = createClass({ }, render : function(){ - return
+ return
{this.props.groupName} diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index 4a2f812ab..e84d373b5 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -4,44 +4,33 @@ position : relative; height : @height; background-color : #ddd; - .toggleMeta{ - position : absolute; - top : 0px; - right : 0px; - height : @height; - width : @height; - cursor : pointer; - line-height : @height; - text-align : center; - .tooltipLeft("Edit Brew Metadata"); + .snippetBarButton{ + height : @height; + line-height : @height; + display : inline-block; + padding : 0px 5px; + font-weight : 800; + font-size : 0.625em; + text-transform : uppercase; + cursor : pointer; &:hover, &.selected{ background-color : #999; } - } - .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 +51,7 @@ font-size : 10px; i{ margin-right : 8px; - font-size : 13px; + font-size : 1.2em; } &:hover{ background-color : #999; @@ -70,4 +59,4 @@ } } } -} \ No newline at end of file +} diff --git a/client/homebrew/phbStyle/phb.style.less b/client/homebrew/phbStyle/phb.style.less index 1b4df58fc..95bf5f23b 100644 --- a/client/homebrew/phbStyle/phb.style.less +++ b/client/homebrew/phbStyle/phb.style.less @@ -189,6 +189,7 @@ body { border-image : @noteBorderImage 11; border-image-outset : 9px 0px; box-shadow : 1px 4px 14px #888; + -webkit-transform : translateZ(0); //Prevents shadows from breaking across columns p, ul{ font-size : 0.352cm; line-height : 1.1em; diff --git a/package-lock.json b/package-lock.json index d50e090ef..daee93bbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "homebrewery", - "version": "2.10.6", + "version": "2.10.7", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1110,9 +1110,9 @@ } }, "@eslint/eslintrc": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", - "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -1122,7 +1122,7 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, @@ -1186,9 +1186,9 @@ } }, "@types/node": { - "version": "14.14.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz", - "integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==" + "version": "14.14.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", + "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==" }, "JSONStream": { "version": "1.3.5", @@ -2202,9 +2202,9 @@ } }, "codemirror": { - "version": "5.59.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.59.1.tgz", - "integrity": "sha512-d0SSW/PCCD4LoSCBPdnP0BzmZB1v3emomCUtVlIWgZHJ06yVeBOvBtOH7vYz707pfAvEeWbO9aP6akh8vl1V3w==" + "version": "5.59.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.59.2.tgz", + "integrity": "sha512-/D5PcsKyzthtSy2NNKCyJi3b+htRkoKv3idswR/tR6UAvMNKA7SrmyZy6fOONJxSRs1JlUWEDAbxqfdArbK8iA==" }, "collection-visit": { "version": "1.0.0", @@ -2560,9 +2560,9 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "denque": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", - "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" }, "depd": { "version": "1.1.2", @@ -2796,13 +2796,13 @@ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "eslint": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz", - "integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", + "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.2", + "@eslint/eslintrc": "^0.3.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2826,7 +2826,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -3152,9 +3152,9 @@ } }, "express-static-gzip": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.0.tgz", - "integrity": "sha512-XGozfjgsfVKBc8lQmPco9zd1+sK9Wy1ZPEAYtH8E34FbBr5BFp2HV+V1PpmIYHaWg8+ecLl/JnQkREbtPSJyxQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.1.tgz", + "integrity": "sha512-J+xSzdr5lj1cIuZey0ac6nUv22VE7GrdwTERqE8DsrqSXLm1zjeYWTVbK37t8exGwobxBXeWU2bM7eSMjBR4YA==", "requires": { "serve-static": "^1.14.1" } @@ -3356,14 +3356,14 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "requires": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", - "universalify": "^1.0.0" + "universalify": "^2.0.0" } }, "fs.realpath": { @@ -3486,9 +3486,9 @@ } }, "google-auth-library": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.3.tgz", - "integrity": "sha512-m9mwvY3GWbr7ZYEbl61isWmk+fvTmOt0YNUfPOUY2VH8K5pZlAIWJjxEi0PqR3OjMretyiQLI6GURMrPSwHQ2g==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.4.tgz", + "integrity": "sha512-q0kYtGWnDd9XquwiQGAZeI2Jnglk7NDi0cChE4tWp6Kpo/kbqnt9scJb0HP+/xqt03Beqw/xQah1OPrci+pOxw==", "requires": { "arrify": "^2.0.0", "base64-js": "^1.3.0", @@ -3510,9 +3510,9 @@ } }, "googleapis": { - "version": "66.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-66.0.0.tgz", - "integrity": "sha512-jdEleRoyo/AeJZjKGC7Z2mHgochn2vR2JKqey6kydRkIBmCZxoQKLisRR4H8CRYZeEd6+c8Ns/LzS1S7qUjoFw==", + "version": "67.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-67.0.0.tgz", + "integrity": "sha512-luhulHrk42DruR+c12W2sY2rrEVoKVdjaZDuHWSxcp1qz+VxvWQpuiK2QDLCXmo36/VFPMaa+Y7rRUR+Qqzd7w==", "requires": { "google-auth-library": "^6.0.0", "googleapis-common": "^4.4.1" @@ -3566,9 +3566,9 @@ }, "dependencies": { "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz", + "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==" } } }, @@ -4209,12 +4209,12 @@ } }, "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "requires": { "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" + "universalify": "^2.0.0" } }, "jsonify": { @@ -4704,16 +4704,16 @@ } }, "mongoose": { - "version": "5.11.9", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.9.tgz", - "integrity": "sha512-lmG6R64jtGGxqtn88BkkY+v470LUfGgyTKUyjswQ5c01GNgQvxA0kQd8h+tm0hZb639hKNRxL9ZBQlLleUpuIQ==", + "version": "5.11.13", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.13.tgz", + "integrity": "sha512-rXbaxSJfLnKKO2RTm8MKt65glrtfKDc4ATEb6vEbbzsVGCiLut753K5axdpyvE7KeTH7GOh4LzmuQLOvaaWOmA==", "requires": { "@types/mongodb": "^3.5.27", "bson": "^1.1.4", "kareem": "2.3.2", "mongodb": "3.6.3", "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.8.1", + "mpath": "0.8.3", "mquery": "3.2.3", "ms": "2.1.2", "regexp-clone": "1.0.0", @@ -4740,9 +4740,9 @@ "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" }, "mpath": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.1.tgz", - "integrity": "sha512-norEinle9aFc05McBawVPwqgFZ7npkts9yu17ztIVLwPwO9rq0OTp89kGVTqvv5rNLMz96E5iWHpVORjI411vA==" + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", + "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==" }, "mquery": { "version": "3.2.3", @@ -4812,9 +4812,9 @@ "dev": true }, "nconf": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.11.0.tgz", - "integrity": "sha512-c4W7QqYF6p5BC7J/eVTOvtUlQgvS5CgbJ11xgjhSr8yyius7km7xgdIYHkFLR4TWY1HjsFkia/3l5OprGqCHvA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.11.1.tgz", + "integrity": "sha512-2XY+7x3GwkkTnmkEVxsKykg0GUqCAtBZUA87FwbcUSaYBfaGCeVSf+82zap16j93B21J2AhpxrsF57jio36t0w==", "requires": { "async": "^1.4.0", "ini": "^1.3.0", @@ -6418,9 +6418,9 @@ } }, "table": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.6.tgz", - "integrity": "sha512-OInCtPmDNieVBkVFi6C8RwU2S2H0h8mF3e3TQK4nreaUNCpooQUkI+A/KuEkm5FawfhWIfNqG+qfelVVR+V00g==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", + "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", "dev": true, "requires": { "ajv": "^7.0.2", @@ -6681,9 +6681,9 @@ } }, "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" }, "unpipe": { "version": "1.0.0", @@ -6752,9 +6752,9 @@ } }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" diff --git a/package.json b/package.json index f5b7aff26..15f108351 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "homebrewery", "description": "Create authentic looking D&D homebrews using only markdown", - "version": "2.10.6", + "version": "2.10.7", "engines": { "node": "12.16.x" }, @@ -45,21 +45,21 @@ "@babel/preset-react": "^7.12.10", "body-parser": "^1.19.0", "classnames": "^2.2.6", - "codemirror": "^5.59.1", + "codemirror": "^5.59.2", "cookie-parser": "^1.4.5", "create-react-class": "^15.7.0", "express": "^4.17.1", - "express-static-gzip": "2.1.0", - "fs-extra": "9.0.1", - "googleapis": "66.0.0", + "express-static-gzip": "2.1.1", + "fs-extra": "9.1.0", + "googleapis": "67.0.0", "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.20", "marked": "^0.3.19", "moment": "^2.29.1", - "mongoose": "^5.11.9", + "mongoose": "^5.11.13", "nanoid": "3.1.20", - "nconf": "^0.11.0", + "nconf": "^0.11.1", "prop-types": "15.7.2", "query-string": "6.13.8", "react": "^16.14.0", @@ -70,7 +70,7 @@ "vitreum": "github:calculuschild/vitreum#21a8e1c9421f1d3a3b474c12f480feb2fbd28c5b" }, "devDependencies": { - "eslint": "^7.16.0", + "eslint": "^7.18.0", "eslint-plugin-react": "^7.22.0", "pico-check": "^2.0.3" } diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 108201d5e..e934aa803 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -3,6 +3,7 @@ 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) { @@ -11,13 +12,8 @@ const GoogleActions = require('./googleActions.js'); // }; 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 title = (tokens.find((token)=>token.type == 'heading' || token.type == 'paragraph') || { text: 'No Title' }).text; }; const newBrew = (req, res)=>{ diff --git a/shared/naturalcrit/splitPane/splitPane.less b/shared/naturalcrit/splitPane/splitPane.less index 2820f68b1..0eae60f0d 100644 --- a/shared/naturalcrit/splitPane/splitPane.less +++ b/shared/naturalcrit/splitPane/splitPane.less @@ -15,7 +15,7 @@ height : 100%; width : 12px; cursor : ew-resize; - background-color : #ddd; + background-color : #bbb; text-align : center; .dots{ display : table-cell; From e2cd7d9f074ff106a107057733e11e971cedd373 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 2 Feb 2021 20:38:25 -0500 Subject: [PATCH 03/46] 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 --- client/admin/admin.jsx | 2 +- client/admin/brewCleanup/brewCleanup.jsx | 6 +- client/admin/brewCompress/brewCompress.jsx | 6 +- client/admin/brewLookup/brewLookup.jsx | 2 +- client/admin/stats/stats.jsx | 2 +- client/homebrew/brewRenderer/brewRenderer.jsx | 25 +- .../homebrew/brewRenderer/brewRenderer.less | 12 +- .../brewRenderer/brewRendererLegacy.jsx | 152 ++++++ .../brewRenderer/errorBar/errorBar.jsx | 2 +- .../notificationPopup/notificationPopup.jsx | 4 +- client/homebrew/editor/editor.jsx | 60 ++- client/homebrew/editor/editor.less | 18 +- .../editor/metadataEditor/metadataEditor.jsx | 50 +- .../homebrew/editor/snippetbar/snippetbar.jsx | 25 +- .../editor/snippetbar/snippets/snippets.js | 89 ++-- .../snippetsLegacy/classfeature.gen.js | 42 ++ .../snippetsLegacy/classtable.gen.js | 114 +++++ .../snippetsLegacy/coverpage.gen.js | 117 +++++ .../snippetsLegacy/fullclass.gen.js | 43 ++ .../snippetbar/snippetsLegacy/magic.gen.js | 91 ++++ .../snippetsLegacy/monsterblock.gen.js | 200 ++++++++ .../snippetbar/snippetsLegacy/snippets.js | 268 ++++++++++ .../snippetsLegacy/tableOfContents.gen.js | 72 +++ client/homebrew/homebrew.jsx | 2 + client/homebrew/navbar/account.navitem.jsx | 4 +- client/homebrew/navbar/issue.navitem.jsx | 4 +- client/homebrew/navbar/patreon.navitem.jsx | 2 +- client/homebrew/navbar/print.navitem.jsx | 4 +- client/homebrew/navbar/recent.navitem.jsx | 2 +- client/homebrew/pages/editPage/editPage.jsx | 48 +- client/homebrew/pages/editPage/editPage.less | 13 +- client/homebrew/pages/homePage/homePage.jsx | 6 +- client/homebrew/pages/newPage/newPage.jsx | 6 +- client/homebrew/pages/sharePage/sharePage.jsx | 7 +- .../pages/userPage/brewItem/brewItem.jsx | 12 +- .../pages/userPage/brewItem/brewItem.less | 3 + client/homebrew/pages/userPage/userPage.jsx | 2 +- client/homebrew/phbStyle/phb.style.less | 7 +- client/homebrew/phbStyle/phb.styleLegacy.less | 469 ++++++++++++++++++ client/template.js | 4 +- package-lock.json | 7 +- package.json | 3 +- scripts/buildHomebrew.js | 14 +- server.js | 1 + server/homebrew.model.js | 1 + .../renderWarnings/renderWarnings.jsx | 4 +- shared/naturalcrit/markdown.js | 87 +++- shared/naturalcrit/markdownLegacy.js | 159 ++++++ shared/naturalcrit/nav/nav.jsx | 2 +- shared/naturalcrit/splitPane/splitPane.jsx | 6 +- 50 files changed, 2127 insertions(+), 154 deletions(-) create mode 100644 client/homebrew/brewRenderer/brewRendererLegacy.jsx create mode 100644 client/homebrew/editor/snippetbar/snippetsLegacy/classfeature.gen.js create mode 100644 client/homebrew/editor/snippetbar/snippetsLegacy/classtable.gen.js create mode 100644 client/homebrew/editor/snippetbar/snippetsLegacy/coverpage.gen.js create mode 100644 client/homebrew/editor/snippetbar/snippetsLegacy/fullclass.gen.js create mode 100644 client/homebrew/editor/snippetbar/snippetsLegacy/magic.gen.js create mode 100644 client/homebrew/editor/snippetbar/snippetsLegacy/monsterblock.gen.js create mode 100644 client/homebrew/editor/snippetbar/snippetsLegacy/snippets.js create mode 100644 client/homebrew/editor/snippetbar/snippetsLegacy/tableOfContents.gen.js create mode 100644 client/homebrew/phbStyle/phb.styleLegacy.less create mode 100644 shared/naturalcrit/markdownLegacy.js diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index a481c9c14..92e0b2aee 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -18,7 +18,7 @@ const Admin = createClass({
- + homebrewery admin
diff --git a/client/admin/brewCleanup/brewCleanup.jsx b/client/admin/brewCleanup/brewCleanup.jsx index 55efbb0e1..b55a70bef 100644 --- a/client/admin/brewCleanup/brewCleanup.jsx +++ b/client/admin/brewCleanup/brewCleanup.jsx @@ -45,8 +45,8 @@ const BrewCleanup = createClass({ return
Found {this.state.count} Brews that could be removed. @@ -59,7 +59,7 @@ const BrewCleanup = createClass({ diff --git a/client/admin/brewCompress/brewCompress.jsx b/client/admin/brewCompress/brewCompress.jsx index ad4e0cd8e..c12f430a2 100644 --- a/client/admin/brewCompress/brewCompress.jsx +++ b/client/admin/brewCompress/brewCompress.jsx @@ -59,8 +59,8 @@ const BrewCompress = createClass({ return
{this.state.pending @@ -76,7 +76,7 @@ const BrewCompress = createClass({ diff --git a/client/admin/brewLookup/brewLookup.jsx b/client/admin/brewLookup/brewLookup.jsx index c6103bef4..c9212d990 100644 --- a/client/admin/brewLookup/brewLookup.jsx +++ b/client/admin/brewLookup/brewLookup.jsx @@ -61,7 +61,7 @@ const BrewLookup = createClass({

Brew Lookup

; } diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index d34025a0a..20256f76f 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -4,6 +4,7 @@ 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'); @@ -18,12 +19,16 @@ const PPR_THRESHOLD = 50; const BrewRenderer = createClass({ getDefaultProps : function() { return { - text : '', - errors : [] + text : '', + renderer : '', + errors : [] }; }, getInitialState : function() { - const pages = this.props.text.split('\\page'); + const pages = this.props.text.split(/^\\page/gm); + let renderer = 'legacy'; + if(this.props.renderer) + renderer = this.props.renderer; return { viewablePageNumber : 0, @@ -33,8 +38,9 @@ const BrewRenderer = createClass({ pages : pages, usePPR : pages.length >= PPR_THRESHOLD, visibility : 'hidden', + renderer : renderer, initialContent : ` - + @@ -49,7 +55,7 @@ const BrewRenderer = createClass({ }, componentWillReceiveProps : function(nextProps) { - const pages = nextProps.text.split('\\page'); + const pages = nextProps.text.split(/^\\page/gm); this.setState({ pages : pages, usePPR : pages.length >= PPR_THRESHOLD @@ -103,12 +109,15 @@ const BrewRenderer = createClass({ renderDummyPage : function(index){ return
- +
; }, renderPage : function(pageText, index){ - return
; + if(this.state.renderer == 'legacy') + return
; + else + return
; }, renderPages : function(){ @@ -159,7 +168,7 @@ const BrewRenderer = createClass({ : null} -
diff --git a/client/homebrew/brewRenderer/brewRenderer.less b/client/homebrew/brewRenderer/brewRenderer.less index 6dee6433f..6fb6f8497 100644 --- a/client/homebrew/brewRenderer/brewRenderer.less +++ b/client/homebrew/brewRenderer/brewRenderer.less @@ -1,8 +1,7 @@ +@import (multiple, less) 'shared/naturalcrit/styles/reset.less'; +.V3 {@import (multiple, less) './client/homebrew/phbStyle/phb.style.less';} +.legacy {@import (multiple, less) './client/homebrew/phbStyle/phb.styleLegacy.less';} -@import (less) './client/homebrew/phbStyle/phb.style.less'; -.pane{ - position : relative; -} .brewRenderer{ will-change : transform; overflow-y : scroll; @@ -16,6 +15,9 @@ } } } +.pane{ + position : relative; +} .pageInfo{ position : absolute; right : 17px; @@ -37,4 +39,4 @@ font-size : 10px; font-weight : 800; color : white; -} \ No newline at end of file +} diff --git a/client/homebrew/brewRenderer/brewRendererLegacy.jsx b/client/homebrew/brewRenderer/brewRendererLegacy.jsx new file mode 100644 index 000000000..70fa593dd --- /dev/null +++ b/client/homebrew/brewRenderer/brewRendererLegacy.jsx @@ -0,0 +1,152 @@ +const React = require('react'); +const createClass = require('create-react-class'); +const _ = require('lodash'); +const cx = require('classnames'); + +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 PAGE_HEIGHT = 1056; +const PPR_THRESHOLD = 50; + +const BrewRenderer = createClass({ + getDefaultProps : function() { + return { + text : '', + errors : [] + }; + }, + getInitialState : function() { + const pages = this.props.text.split('\\page'); + + return { + viewablePageNumber : 0, + height : 0, + isMounted : false, + + pages : pages, + usePPR : pages.length >= PPR_THRESHOLD, + }; + }, + height : 0, + lastRender :
, + + 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 + }); + }, + + updateSize : function() { + this.setState({ + height : this.refs.main.parentNode.clientHeight, + isMounted : true + }); + }, + + handleScroll : function(e){ + const target = e.target; + this.setState((prevState)=>({ + viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * prevState.pages.length) + })); + }, + + shouldRender : function(pageText, index){ + if(!this.state.isMounted) return false; + + const viewIndex = this.state.viewablePageNumber; + if(index == viewIndex - 3) return true; + if(index == viewIndex - 2) return true; + if(index == viewIndex - 1) return true; + if(index == viewIndex) return true; + if(index == viewIndex + 1) return true; + if(index == viewIndex + 2) return true; + if(index == viewIndex + 3) return true; + + //Check for style tages + if(pageText.indexOf(' + +
+ +# ${_.sample(titles)} + +
+
+##### ${_.sample(subtitles)} +
+ +\\page`; +}; \ No newline at end of file diff --git a/client/homebrew/editor/snippetbar/snippetsLegacy/fullclass.gen.js b/client/homebrew/editor/snippetbar/snippetsLegacy/fullclass.gen.js new file mode 100644 index 000000000..5ede9e501 --- /dev/null +++ b/client/homebrew/editor/snippetbar/snippetsLegacy/fullclass.gen.js @@ -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 ``; + })); + + + return `${[ + image, + '', + '```', + '```', + '
\n\n', + `## ${classname}`, + 'Cool intro stuff will go here', + + '\\page', + ClassTableGen(classname), + ClassFeatureGen(classname), + + + + ].join('\n')}\n\n\n`; +}; \ No newline at end of file diff --git a/client/homebrew/editor/snippetbar/snippetsLegacy/magic.gen.js b/client/homebrew/editor/snippetbar/snippetsLegacy/magic.gen.js new file mode 100644 index 000000000..ed17f8692 --- /dev/null +++ b/client/homebrew/editor/snippetbar/snippetsLegacy/magic.gen.js @@ -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 `
\n${content}\n
`; + }, + + spell : function(){ + const level = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th', '8th', '9th']; + const spellSchools = ['abjuration', 'conjuration', 'divination', 'enchantment', 'evocation', 'illusion', 'necromancy', 'transmutation']; + + + let components = _.sampleSize(['V', 'S', 'M'], _.random(1, 3)).join(', '); + if(components.indexOf('M') !== -1){ + components += ` (${_.sampleSize(['a small doll', 'a crushed button worth at least 1cp', 'discarded gum wrapper'], _.random(1, 3)).join(', ')})`; + } + + return [ + `#### ${_.sample(spellNames)}`, + `*${_.sample(level)}-level ${_.sample(spellSchools)}*`, + '___', + '- **Casting Time:** 1 action', + `- **Range:** ${_.sample(['Self', 'Touch', '30 feet', '60 feet'])}`, + `- **Components:** ${components}`, + `- **Duration:** ${_.sample(['Until dispelled', '1 round', 'Instantaneous', 'Concentration, up to 10 minutes', '1 hour'])}`, + '', + 'A flame, equivalent in brightness to a torch, springs from from an object that you touch. ', + 'The effect look like a regular flame, but it creates no heat and doesn\'t use oxygen. ', + 'A *continual flame* can be covered or hidden but not smothered or quenched.', + '\n\n\n' + ].join('\n'); + } +}; \ No newline at end of file diff --git a/client/homebrew/editor/snippetbar/snippetsLegacy/monsterblock.gen.js b/client/homebrew/editor/snippetbar/snippetsLegacy/monsterblock.gen.js new file mode 100644 index 000000000..1e8a0eebd --- /dev/null +++ b/client/homebrew/editor/snippetbar/snippetsLegacy/monsterblock.gen.js @@ -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`; + } +}; diff --git a/client/homebrew/editor/snippetbar/snippetsLegacy/snippets.js b/client/homebrew/editor/snippetbar/snippetsLegacy/snippets.js new file mode 100644 index 000000000..b8410bd9f --- /dev/null +++ b/client/homebrew/editor/snippetbar/snippetsLegacy/snippets.js @@ -0,0 +1,268 @@ +/* eslint-disable max-lines */ + +const MagicGen = require('./magic.gen.js'); +const ClassTableGen = require('./classtable.gen.js'); +const MonsterBlockGen = require('./monsterblock.gen.js'); +const ClassFeatureGen = require('./classfeature.gen.js'); +const CoverPageGen = require('./coverpage.gen.js'); +const TableOfContentsGen = require('./tableOfContents.gen.js'); + + +module.exports = [ + + { + groupName : 'Editor', + icon : '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 : '
\n\n' + }, + { + name : 'Wide Block', + icon : 'fas fa-arrows-alt-h', + gen : '
\nEverything in here will be extra wide. Tables, text, everything! Beware though, CSS columns can behave a bit weird sometimes.\n
\n' + }, + { + name : 'Image', + icon : 'fas fa-image', + gen : [ + '', + 'Credit: Kyounghwan Kim' + ].join('\n') + }, + { + name : 'Background Image', + icon : 'fas fa-tree', + gen : [ + '' + ].join('\n') + }, + + { + name : 'Page Number', + icon : 'fas fa-bookmark', + gen : '
1
\n
PART 1 | FANCINESS
\n\n' + }, + + { + name : 'Auto-incrementing Page Number', + icon : 'fas fa-sort-numeric-down', + gen : '
\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 + }, + + + ] + }, + + + /************************* 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 [ + '
', + '##### Time to Drop Knowledge', + 'Use notes to point out some interesting information. ', + '', + '**Tables and lists** both work within a note.', + '
' + ].join('\n'); + }, + }, + { + name : 'Monster Stat Block', + icon : '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 [ + '
', + '##### 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 : 'Split Table', + icon : 'fas fa-th-large', + gen : function(){ + return [ + '
', + '| 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 |', + '
\n\n', + ].join('\n'); + }, + } + ] + }, + + + + + /**************** PRINT *************/ + + { + groupName : 'Print', + icon : 'fas fa-print', + snippets : [ + { + name : 'A4 PageSize', + icon : 'far fa-file', + gen : ['' + ].join('\n') + }, + { + name : 'Ink Friendly', + icon : 'fas fa-tint', + gen : ['', + '' + ].join('\n') + }, + ] + }, + +]; diff --git a/client/homebrew/editor/snippetbar/snippetsLegacy/tableOfContents.gen.js b/client/homebrew/editor/snippetbar/snippetsLegacy/tableOfContents.gen.js new file mode 100644 index 000000000..ca49526d4 --- /dev/null +++ b/client/homebrew/editor/snippetbar/snippetsLegacy/tableOfContents.gen.js @@ -0,0 +1,72 @@ +const _ = require('lodash'); + +const getTOC = (pages)=>{ + const add1 = (title, page)=>{ + res.push({ + title : title, + page : page + 1, + children : [] + }); + }; + const add2 = (title, page)=>{ + if(!_.last(res)) add1('', page); + _.last(res).children.push({ + title : title, + page : page + 1, + children : [] + }); + }; + const add3 = (title, page)=>{ + if(!_.last(res)) add1('', page); + if(!_.last(_.last(res).children)) add2('', page); + _.last(_.last(res).children).children.push({ + title : title, + page : page + 1, + children : [] + }); + }; + + const res = []; + _.each(pages, (page, pageNum)=>{ + const lines = page.split('\n'); + _.each(lines, (line)=>{ + if(_.startsWith(line, '# ')){ + const title = line.replace('# ', ''); + add1(title, pageNum); + } + if(_.startsWith(line, '## ')){ + const title = line.replace('## ', ''); + add2(title, pageNum); + } + if(_.startsWith(line, '### ')){ + const title = line.replace('### ', ''); + add3(title, pageNum); + } + }); + }); + return res; +}; + +module.exports = function(brew){ + const pages = brew.split('\\page'); + const TOC = getTOC(pages); + const markdown = _.reduce(TOC, (r, g1, idx1)=>{ + r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`); + if(g1.children.length){ + _.each(g1.children, (g2, idx2)=>{ + r.push(` - [${idx1 + 1}.${idx2 + 1} ${g2.title}](#p${g2.page})`); + if(g2.children.length){ + _.each(g2.children, (g3, idx3)=>{ + r.push(` - [${idx1 + 1}.${idx2 + 1}.${idx3 + 1} ${g3.title}](#p${g3.page})`); + }); + } + }); + } + return r; + }, []).join('\n'); + + return `
+##### Table Of Contents +${markdown} +
\n`; +}; \ No newline at end of file diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 93ee8f31f..640a73f77 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -20,6 +20,7 @@ const Homebrew = createClass({ changelog : '', version : '0.0.0', account : null, + enable_v3 : false, brew : { title : '', text : '', @@ -33,6 +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 (){ diff --git a/client/homebrew/navbar/account.navitem.jsx b/client/homebrew/navbar/account.navitem.jsx index 3d36e5bc6..f40fc92de 100644 --- a/client/homebrew/navbar/account.navitem.jsx +++ b/client/homebrew/navbar/account.navitem.jsx @@ -20,12 +20,12 @@ const Account = createClass({ render : function(){ if(global.account){ - return + return {global.account.username} ; } - return + return login ; } diff --git a/client/homebrew/navbar/issue.navitem.jsx b/client/homebrew/navbar/issue.navitem.jsx index d0dfd88bb..529744c29 100644 --- a/client/homebrew/navbar/issue.navitem.jsx +++ b/client/homebrew/navbar/issue.navitem.jsx @@ -6,8 +6,8 @@ module.exports = function(props){ return report issue ; -}; \ No newline at end of file +}; diff --git a/client/homebrew/navbar/patreon.navitem.jsx b/client/homebrew/navbar/patreon.navitem.jsx index 03fb69af4..555e3a15c 100644 --- a/client/homebrew/navbar/patreon.navitem.jsx +++ b/client/homebrew/navbar/patreon.navitem.jsx @@ -8,7 +8,7 @@ module.exports = function(props){ newTab={true} href='https://www.patreon.com/NaturalCrit' color='green' - icon='fa-heart'> + icon='fas fa-heart'> help out ; }; diff --git a/client/homebrew/navbar/print.navitem.jsx b/client/homebrew/navbar/print.navitem.jsx index 7d1509a57..4907cad73 100644 --- a/client/homebrew/navbar/print.navitem.jsx +++ b/client/homebrew/navbar/print.navitem.jsx @@ -3,7 +3,7 @@ const createClass = require('create-react-class'); const Nav = require('naturalcrit/nav/nav.jsx'); module.exports = function(props){ - return + return get PDF ; -}; \ No newline at end of file +}; diff --git a/client/homebrew/navbar/recent.navitem.jsx b/client/homebrew/navbar/recent.navitem.jsx index 5b2895ad7..d12389948 100644 --- a/client/homebrew/navbar/recent.navitem.jsx +++ b/client/homebrew/navbar/recent.navitem.jsx @@ -143,7 +143,7 @@ const RecentItems = createClass({ }, render : function(){ - return this.handleDropdown(true)} onMouseLeave={()=>this.handleDropdown(false)}> {this.props.text} diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index d75b647e6..c8d5b6920 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -41,17 +41,18 @@ const EditPage = createClass({ tags : '', published : false, authors : [], - systems : [] + systems : [], + renderer : '' } }; }, getInitialState : function() { return { - brew : this.props.brew, - + brew : this.props.brew, isSaving : false, isPending : false, + alertRenderChange : false, saveGoogle : this.props.brew.googleId ? true : false, confirmGoogleTransfer : false, errors : null, @@ -66,6 +67,8 @@ const EditPage = createClass({ 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){ @@ -101,6 +104,12 @@ const EditPage = createClass({ }, handleMetadataChange : function(metadata){ + if(metadata.renderer != this.savedBrew.renderer){ + console.log('renderer changed!'); + this.setState({ + alertRenderChange : true + }); + } this.setState((prevState)=>({ brew : _.merge({}, prevState.brew, metadata), isPending : true, @@ -122,8 +131,7 @@ const EditPage = createClass({ }, 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(){ @@ -142,6 +150,12 @@ const EditPage = createClass({ this.clearErrors(); }, + closeAlerts : function(){ + this.setState({ + alertRenderChange : false + }); + }, + toggleGoogleStorage : function(){ this.setState((prevState)=>({ saveGoogle : !prevState.saveGoogle, @@ -294,7 +308,7 @@ const EditPage = createClass({ } catch (e){} if(this.state.errors.status == '401'){ - return + return Oops!
You must be signed in to a Google account @@ -312,7 +326,7 @@ const EditPage = createClass({ ; } - return + return Oops!
Looks like there was a problem saving.
@@ -325,13 +339,22 @@ const EditPage = createClass({ } if(this.state.isSaving){ - return saving...; + return saving...; } if(this.state.isPending && this.hasChanges()){ - return Save Now; + return Save Now; } if(!this.state.isPending && !this.state.isSaving){ - return saved.; + return saved. + {this.state.alertRenderChange && +
+ Rendering mode for this brew has been changed! Refresh the page to load the new renderer.
+
+ OK +
+
+ } +
; } }, @@ -351,7 +374,7 @@ const EditPage = createClass({ {this.renderGoogleDriveIcon()} {this.renderSaveButton()} - + Share @@ -374,8 +397,9 @@ const EditPage = createClass({ onChange={this.handleTextChange} metadata={this.state.brew} onMetadataChange={this.handleMetadataChange} + renderer={this.state.brew.renderer} /> - +
; diff --git a/client/homebrew/pages/editPage/editPage.less b/client/homebrew/pages/editPage/editPage.less index 381b29fca..b73467d50 100644 --- a/client/homebrew/pages/editPage/editPage.less +++ b/client/homebrew/pages/editPage/editPage.less @@ -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 : 106px; text-align : center; + position : relative; &.saved{ cursor : initial; color : #666; @@ -21,12 +27,15 @@ margin : -5px; } .errorContainer{ + animation-name: glideDown; + animation-duration: 0.4s; position : absolute; top : 100%; left : 50%; - z-index : 1000; + z-index : 100000; width : 140px; padding : 3px; + color : white; background-color : #333; border : 3px solid #444; border-radius : 5px; diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index e245b990b..d782b8b81 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -55,7 +55,7 @@ const HomePage = createClass({ return - + Changelog @@ -77,11 +77,11 @@ const HomePage = createClass({
- Save current + Save current
- Create your own + Create your own
; } diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 5e491ee0e..89727cbdb 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -127,11 +127,11 @@ const NewPage = createClass({ renderSaveButton : function(){ if(this.state.isSaving){ - return + return save... ; } else { - return + return save ; } @@ -143,7 +143,7 @@ const NewPage = createClass({ }, renderLocalPrintButton : function(){ - return + return get PDF ; }, diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index c94473b1d..a1490ed28 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -22,7 +22,8 @@ const SharePage = createClass({ shareId : null, createdAt : null, updatedAt : null, - views : 0 + views : 0, + renderer : '' } }; }, @@ -59,7 +60,7 @@ const SharePage = createClass({ - + source @@ -68,7 +69,7 @@ const SharePage = createClass({
- +
; } diff --git a/client/homebrew/pages/userPage/brewItem/brewItem.jsx b/client/homebrew/pages/userPage/brewItem/brewItem.jsx index bddf246bf..b7b13ff4c 100644 --- a/client/homebrew/pages/userPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/userPage/brewItem/brewItem.jsx @@ -48,7 +48,7 @@ const BrewItem = createClass({ if(!this.props.brew.editId) return; return - + ; }, @@ -61,7 +61,7 @@ const BrewItem = createClass({ } return - + ; }, @@ -74,7 +74,7 @@ const BrewItem = createClass({ } return - + ; }, @@ -95,13 +95,13 @@ const BrewItem = createClass({
- {brew.authors.join(', ')} + {brew.authors.join(', ')} - {brew.views} + {brew.views} - {moment(brew.updatedAt).fromNow()} + {moment(brew.updatedAt).fromNow()} {this.renderGoogleDriveIcon()}
diff --git a/client/homebrew/pages/userPage/brewItem/brewItem.less b/client/homebrew/pages/userPage/brewItem/brewItem.less index 9338ff23d..6ab0a893c 100644 --- a/client/homebrew/pages/userPage/brewItem/brewItem.less +++ b/client/homebrew/pages/userPage/brewItem/brewItem.less @@ -22,6 +22,9 @@ font-size : 2.2em; } .info{ + position: absolute; + bottom: 0px; + margin-bottom: 4px; font-family : ScalySans; font-size : 1.2em; &>span{ diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 5081a4942..6697dd20d 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -59,7 +59,7 @@ const UserPage = createClass({ -
+

{this.getUsernameWithS()} brews

diff --git a/client/homebrew/phbStyle/phb.style.less b/client/homebrew/phbStyle/phb.style.less index 95bf5f23b..4a3b632e2 100644 --- a/client/homebrew/phbStyle/phb.style.less +++ b/client/homebrew/phbStyle/phb.style.less @@ -1,10 +1,9 @@ -@import (less) 'shared/naturalcrit/styles/reset.less'; @import (less) './client/homebrew/phbStyle/phb.fonts.css'; @import (less) './client/homebrew/phbStyle/phb.assets.less'; @import (less) './client/homebrew/phbStyle/phb.depricated.less'; //Colors @background : #EEE5CE; -@noteGreen : #e0e5c1; +@noteGreen : #FF0000; @headerUnderline : #c9ad6a; @horizontalRule : #9c2b1b; @headerText : #58180D; @@ -327,12 +326,12 @@ body { text-indent : -1em; list-style-type : none; } - //Column Break - pre, code{ + .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{ diff --git a/client/homebrew/phbStyle/phb.styleLegacy.less b/client/homebrew/phbStyle/phb.styleLegacy.less new file mode 100644 index 000000000..183c89659 --- /dev/null +++ b/client/homebrew/phbStyle/phb.styleLegacy.less @@ -0,0 +1,469 @@ +@import (less) './client/homebrew/phbStyle/phb.fonts.css'; +@import (less) './client/homebrew/phbStyle/phb.assets.less'; +@import (less) './client/homebrew/phbStyle/phb.depricated.less'; +//Colors +@background : #EEE5CE; +@noteGreen : #e0e5c1; +@headerUnderline : #c9ad6a; +@horizontalRule : #9c2b1b; +@headerText : #58180D; +@monsterStatBackground : #FDF1DC; +@page { margin: 0; } +body { + counter-reset : phb-page-numbers; +} +*{ + -webkit-print-color-adjust : exact; +} +.useSansSerif(){ + font-family : ScalySans; + em{ + font-family : ScalySans; + font-style : italic; + } + strong{ + font-family : ScalySans; + font-weight : 800; + letter-spacing : -0.02em; + } +} +.useColumns(@multiplier : 1){ + column-count : 2; + column-fill : auto; + column-gap : 1cm; + column-width : 8cm * @multiplier; + -webkit-column-count : 2; + -moz-column-count : 2; + -webkit-column-width : 8cm * @multiplier; + -moz-column-width : 8cm * @multiplier; + -webkit-column-gap : 1cm; + -moz-column-gap : 1cm; +} +.phb{ + .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.0cm 1.7cm; + padding-bottom : 1.5cm; + background-color : @background; + background-image : @backgroundImage; + font-family : BookSanity; + font-size : 0.317cm; + text-rendering : optimizeLegibility; + page-break-before : always; + page-break-after : always; + //***************************** + // * BASE + // *****************************/ + p{ + padding-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.03em; + } + 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{ + margin-top : 0.2em; + margin-bottom : 0.2em; + font-family : MrJeeves; + font-weight : 800; + color : @headerText; + } + h1{ + column-span : all; + font-size : 0.987cm; + -webkit-column-span : all; + -moz-column-span : all; + &+p::first-letter{ + float : left; + font-family : Solberry; + font-size : 10em; + color : #222; + line-height : 0.8em; + } + } + h2{ + font-size : 0.705cm; + } + h3{ + font-size : 0.529cm; + border-bottom : 2px solid @headerUnderline; + } + h4{ + margin-bottom : 0.00em; + font-size : 0.458cm; + } + h5{ + margin-bottom : 0.2em; + font-family : ScalySansSmallCaps; + font-size : 0.423cm; + font-weight : 900; + } + //***************************** + // * TABLE + // *****************************/ + table{ + .useSansSerif(); + width : 100%; + margin-bottom : 1em; + font-size : 10pt; + thead{ + display: table-row-group; + font-weight : 800; + th{ + vertical-align : bottom; + padding-bottom : 0.3em; + padding-right : 0.1em; + padding-left : 0.1em; + } + } + tbody{ + tr{ + td{ + padding : 0.3em 0.1em; + } + &:nth-child(odd){ + background-color : @noteGreen; + } + } + } + } + //***************************** + // * NOTE + // *****************************/ + blockquote{ + .useSansSerif(); + box-sizing : border-box; + margin-bottom : 1em; + padding : 5px 10px; + background-color : @noteGreen; + border-style : solid; + border-width : 11px; + border-image : @noteBorderImage 11; + border-image-outset : 9px 0px; + box-shadow : 1px 4px 14px #888; + p, ul{ + font-size : 0.352cm; + line-height : 1.1em; + } + } + //If a note starts a column, give it space at the top to render border + pre+blockquote, h2+blockquote, h3+blockquote, h4+blockquote, h5+blockquote { + margin-top : 13px; + } + //***************************** + // * MONSTER STAT BLOCK + // *****************************/ + hr+blockquote{ + position : relative; + padding-top : 15px; + background-color : @monsterStatBackground; + border-style : solid; + border-width : 10px; + border-image : @monsterBorderImage 10; + h2{ + margin-top : -8px; + margin-bottom : 0px; + &+p{ + padding-bottom : 0px; + } + } + h3{ + font-family : ScalySans; + font-weight : 400; + border-bottom : 1px solid @headerText; + } + hr+ul{ + color : @headerText; + } + ul{ + .useSansSerif(); + padding-left : 1em; + font-size : 0.352cm; + } + // Monster Ability table + hr+table{ + margin : 0; + column-span : 1; + background-color : transparent; + border-style : none; + border-image : none; + -webkit-column-span : 1; + tbody{ + tr:nth-child(odd), tr:nth-child(even){ + background-color : transparent; + } + } + } + table{ + color : @headerText; + } + p+p{ + margin-top : 0em; + padding-bottom : 0.5em; + text-indent : 0em; + } + //Triangle dividers + hr{ + visibility : visible; + height : 6px; + margin : 4px 0px; + background-image : @redTriangleImage; + background-size : 100% 100%; + border : none; + } + } + //Full Width + 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; + &.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; + } + //***************************** + // * 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; + } + //Column Break + pre, code{ + visibility : hidden; + -webkit-column-break-after : always; + break-after : always; + -moz-column-break-after : always; + } + //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 +// *****************************/ +.phb .spellList{ + .useSansSerif(); + column-count : 4; + column-span : all; + -webkit-column-span : all; + -moz-column-span : all; + ul+h5{ + margin-top : 15px; + } + p, ul{ + font-size : 0.352cm; + line-height : 1.3em; + } + ul{ + margin-bottom : 0.5em; + padding-left : 1em; + text-indent : -1em; + list-style-type : none; + -webkit-column-break-inside : auto; + page-break-inside : auto; + break-inside : auto; + } +} +//***************************** +// * WIDE +// *****************************/ +.phb .wide{ + column-span : all; + -webkit-column-span : all; + -moz-column-span : all; +} +//***************************** +// * CLASS TABLE +// *****************************/ +.phb .classTable{ + margin-top : 25px; + margin-bottom : 40px; + border-collapse : separate; + background-color : white; + border : initial; + border-style : solid; + border-image-outset : 25px 17px; + border-image-repeat : stretch; + border-image-slice : 150 200 150 200; + border-image-source : @frameBorderImage; + border-image-width : 47px; + h5{ + margin-bottom : 10px; + } +} +//************************************ +// * DESCRIPTIVE TEXT BOX +// ************************************/ +.phb .descriptive{ + display : block-inline; + margin-bottom : 1em; + background-color : #faf7ea; + font-family : ScalySans; + border-style : solid; + border-width : 7px; + border-image : @descriptiveBoxImage 12 stretch; + border-image-outset : 4px; + box-shadow : 0px 0px 6px #faf7ea; + p{ + display : block; + padding-bottom : 0px; + line-height : 1.5em; + } + p + p { + padding-top : .8em; + } + em { + font-family : ScalySans; + font-style : italic; + } + strong { + font-family : ScalySans; + font-weight : 800; + letter-spacing : -0.02em; + } +} +.phb pre+.descriptive{ + margin-top : 8px; +} +//***************************** +// * TABLE OF CONTENTS +// *****************************/ +.phb .toc{ + -webkit-column-break-inside : avoid; + page-break-inside : avoid; + break-inside : avoid; + a{ + color : black; + text-decoration : none; + &:hover{ + text-decoration : underline; + } + } + ul{ + padding-left : 0; + list-style-type : none; + } + &>ul>li{ + margin-bottom : 10px; + } +} diff --git a/client/template.js b/client/template.js index fec379b9d..6307b744b 100644 --- a/client/template.js +++ b/client/template.js @@ -3,7 +3,7 @@ module.exports = async(name, title = '', props = {})=>{ - + @@ -16,4 +16,4 @@ module.exports = async(name, title = '', props = {})=>{ `; -}; \ No newline at end of file +}; diff --git a/package-lock.json b/package-lock.json index daee93bbd..0a72720bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4410,7 +4410,12 @@ } }, "marked": { - "version": "0.3.19", + "version": "npm:marked@1.2.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.7.tgz", + "integrity": "sha512-No11hFYcXr/zkBvL6qFmAp1z6BKY3zqLMHny/JN/ey+al7qwCM2+CMBL9BOgqMxZU36fz4cCWfn2poWIf7QRXA==" + }, + "markedLegacy": { + "version": "npm:marked@0.3.19", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==" }, diff --git a/package.json b/package.json index 15f108351..c516cb83c 100644 --- a/package.json +++ b/package.json @@ -55,10 +55,11 @@ "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.20", - "marked": "^0.3.19", "moment": "^2.29.1", "mongoose": "^5.11.13", "nanoid": "3.1.20", + "markedLegacy": "npm:marked@^0.3.19", + "marked": "npm:marked@^1.2.7", "nconf": "^0.11.1", "prop-types": "15.7.2", "query-string": "6.13.8", diff --git a/scripts/buildHomebrew.js b/scripts/buildHomebrew.js index 76ad07d32..43acd7318 100644 --- a/scripts/buildHomebrew.js +++ b/scripts/buildHomebrew.js @@ -21,10 +21,16 @@ const build = async ({ bundle, render, ssr })=>{ await fs.outputFile('./build/homebrew/ssr.js', ssr); await fs.outputFile('./build/homebrew/render.js', render); - //compress files - 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)); + //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'); diff --git a/server.js b/server.js index 5528a3762..c149e113a 100644 --- a/server.js +++ b/server.js @@ -222,6 +222,7 @@ app.use((req, res)=>{ brews : req.brews, googleBrews : req.googleBrews, account : req.account, + enable_v3 : config.get('enable_v3') }; templateFn('homebrew', title = req.brew ? req.brew.title : '', props) .then((page)=>{ res.send(page); }) diff --git a/server/homebrew.model.js b/server/homebrew.model.js index 05a9ab673..1eeb4409b 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -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 }, diff --git a/shared/homebrewery/renderWarnings/renderWarnings.jsx b/shared/homebrewery/renderWarnings/renderWarnings.jsx index 4e52fa816..3fd290260 100644 --- a/shared/homebrewery/renderWarnings/renderWarnings.jsx +++ b/shared/homebrewery/renderWarnings/renderWarnings.jsx @@ -53,8 +53,8 @@ const RenderWarnings = createClass({ if(_.isEmpty(this.state.warnings)) return null; return
- - + +

Render Warnings

If this homebrew is rendering badly if might be because of the following:
    {_.values(this.state.warnings)}
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 9dc4fa9c9..5c8a1e85e 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ const _ = require('lodash'); const Markdown = require('marked'); const renderer = new Markdown.Renderer(); @@ -13,6 +14,77 @@ renderer.html = function (html) { return html; }; +// Ensure {{ Divs don't confuse paragraph parsing (else it renders empty paragraphs) +renderer.paragraph = function(text){ + if(text.startsWith('${text}

\n`; +}; + +// Mustache-style Divs {{class \n content ... \n}} +let blockCount = 0; +const blockRegex = /^ *{{(?:="[\w, ]*"|[^"'\s])*$|^ *}}$/gm; +const inlineFullRegex = /{{[^\n]*}}/g; +const inlineRegex = /{{(?:="[\w, ]*"|[^"'\s])*\s*|}}/g; + +renderer.text = function(text){ + const newText = text.replaceAll('"', '"'); + let matches; + + //DIV - BLOCK-LEVEL + if(matches = newText.match(blockRegex)) { + let matchIndex = 0; + const res = _.reduce(newText.split(blockRegex), (r, splitText)=>{ + if(splitText) r.push(Markdown.parseInline(splitText, { renderer: renderer })); + + const block = matches[matchIndex] ? matches[matchIndex].trimLeft() : ''; + if(block && block.startsWith('{')) { + const values = processStyleTags(block.substring(2)); + r.push(`
`); + blockCount++; + } else if(block == '}}' && blockCount !== 0){ + r.push('
'); + blockCount--; + } + + matchIndex++; + + return r; + }, []).join(''); + return res; + } else if(matches = newText.match(inlineFullRegex)) { + + //SPAN - INLINE + matches = newText.match(inlineRegex); + let matchIndex = 0; + const res = _.reduce(newText.split(inlineRegex), (r, splitText)=>{ + + if(splitText) r.push(Markdown.parseInline(splitText, { renderer: renderer })); + + const block = matches[matchIndex] ? matches[matchIndex].trimLeft() : ''; + if(block && block.startsWith('{{')) { + const values = processStyleTags(block.substring(2)); + r.push(`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 ? `style="${styles.join(' ')}"` : ''}`; +}; module.exports = { marked : Markdown, render : (rawBrewText)=>{ + blockCount = 0; + rawBrewText = rawBrewText.replace(/^\\column/gm, `
`) + .replace(/^}}/gm, '\n}}') + .replace(/^({{[^\n]*)$/gm, '$1\n'); return Markdown( sanatizeScriptTags(rawBrewText), { renderer: renderer } diff --git a/shared/naturalcrit/markdownLegacy.js b/shared/naturalcrit/markdownLegacy.js new file mode 100644 index 000000000..454f5b1da --- /dev/null +++ b/shared/naturalcrit/markdownLegacy.js @@ -0,0 +1,159 @@ +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), '')){ + const openTag = html.substring(0, html.indexOf('>')+1); + html = html.substring(html.indexOf('>')+1); + html = html.substring(0, html.lastIndexOf('
')); + return `${openTag} ${Markdown(html)}
`; + } + 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 = `${text}`; + 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 = { + '&' : '&', + '<' : '<', + '>' : '>', + '"' : '"', + '\'' : ''' +}; +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(/ `; -}; \ No newline at end of file +}; diff --git a/package-lock.json b/package-lock.json index daee93bbd..0a72720bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4410,7 +4410,12 @@ } }, "marked": { - "version": "0.3.19", + "version": "npm:marked@1.2.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.7.tgz", + "integrity": "sha512-No11hFYcXr/zkBvL6qFmAp1z6BKY3zqLMHny/JN/ey+al7qwCM2+CMBL9BOgqMxZU36fz4cCWfn2poWIf7QRXA==" + }, + "markedLegacy": { + "version": "npm:marked@0.3.19", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==" }, diff --git a/package.json b/package.json index 15f108351..c516cb83c 100644 --- a/package.json +++ b/package.json @@ -55,10 +55,11 @@ "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.20", - "marked": "^0.3.19", "moment": "^2.29.1", "mongoose": "^5.11.13", "nanoid": "3.1.20", + "markedLegacy": "npm:marked@^0.3.19", + "marked": "npm:marked@^1.2.7", "nconf": "^0.11.1", "prop-types": "15.7.2", "query-string": "6.13.8", diff --git a/scripts/buildHomebrew.js b/scripts/buildHomebrew.js index 76ad07d32..43acd7318 100644 --- a/scripts/buildHomebrew.js +++ b/scripts/buildHomebrew.js @@ -21,10 +21,16 @@ const build = async ({ bundle, render, ssr })=>{ await fs.outputFile('./build/homebrew/ssr.js', ssr); await fs.outputFile('./build/homebrew/render.js', render); - //compress files - 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)); + //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'); diff --git a/server.js b/server.js index 5528a3762..c149e113a 100644 --- a/server.js +++ b/server.js @@ -222,6 +222,7 @@ app.use((req, res)=>{ brews : req.brews, googleBrews : req.googleBrews, account : req.account, + enable_v3 : config.get('enable_v3') }; templateFn('homebrew', title = req.brew ? req.brew.title : '', props) .then((page)=>{ res.send(page); }) diff --git a/server/googleActions.js b/server/googleActions.js index 1fc93a861..1a8d656ac 100644 --- a/server/googleActions.js +++ b/server/googleActions.js @@ -157,6 +157,7 @@ GoogleActions = { lastViewed : brew.lastViewed, views : brew.views, version : brew.version, + renderer : brew.renderer, tags : brew.tags, systems : brew.systems.join() } }, @@ -230,6 +231,7 @@ GoogleActions = { description : brew.description, tags : '', published : brew.published, + renderer : brew.renderer, authors : [], systems : [] }; @@ -291,6 +293,7 @@ GoogleActions = { 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 diff --git a/server/homebrew.api.js b/server/homebrew.api.js index e934aa803..e88a3ef88 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -122,8 +122,6 @@ const newGoogleBrew = async (req, res, next)=>{ req.body = brew; - console.log(oAuth2Client); - const newBrew = await GoogleActions.newGoogleBrew(oAuth2Client, brew); return res.status(200).send(newBrew); diff --git a/server/homebrew.model.js b/server/homebrew.model.js index 05a9ab673..9a4b14427 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -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 }, @@ -55,6 +56,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]); }); }); diff --git a/shared/homebrewery/renderWarnings/renderWarnings.jsx b/shared/homebrewery/renderWarnings/renderWarnings.jsx index 4e52fa816..3fd290260 100644 --- a/shared/homebrewery/renderWarnings/renderWarnings.jsx +++ b/shared/homebrewery/renderWarnings/renderWarnings.jsx @@ -53,8 +53,8 @@ const RenderWarnings = createClass({ if(_.isEmpty(this.state.warnings)) return null; return
- - + +

Render Warnings

If this homebrew is rendering badly if might be because of the following:
    {_.values(this.state.warnings)}
diff --git a/shared/naturalcrit/codeEditor/codeEditor.jsx b/shared/naturalcrit/codeEditor/codeEditor.jsx index e8d9cb69f..0f881883d 100644 --- a/shared/naturalcrit/codeEditor/codeEditor.jsx +++ b/shared/naturalcrit/codeEditor/codeEditor.jsx @@ -61,16 +61,12 @@ const CodeEditor = createClass({ } }, - componentWillReceiveProps : function(nextProps){ - if(this.codeMirror && nextProps.value !== undefined && this.codeMirror.getValue() != nextProps.value) { - this.codeMirror.setValue(nextProps.value); + componentDidUpdate : function(prevProps) { + if(this.codeMirror && this.codeMirror.getValue() != this.props.value) { + this.codeMirror.setValue(this.props.value); } }, - shouldComponentUpdate : function(nextProps, nextState) { - return false; - }, - setCursorPosition : function(line, char){ setTimeout(()=>{ this.codeMirror.focus(); diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 9dc4fa9c9..355620d1f 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ const _ = require('lodash'); const Markdown = require('marked'); const renderer = new Markdown.Renderer(); @@ -10,9 +11,87 @@ renderer.html = function (html) { html = html.substring(0, html.lastIndexOf('
')); return `${openTag} ${Markdown(html)}
`; } + // if(_.startsWith(_.trim(html), '')){ + // const openTag = html.substring(0, html.indexOf('>')+1); + // html = html.substring(html.indexOf('>')+1); + // html = html.substring(0, html.lastIndexOf('')); + // html = html.replaceAll(/\s(\.[^{]*)/gm, '.V3 $1'); + // return `${openTag} ${html} `; + // } return html; }; +// Ensure {{ Divs don't confuse paragraph parsing (else it renders empty paragraphs) +renderer.paragraph = function(text){ + if(text.startsWith('${text}

\n`; +}; + +// Mustache-style Divs {{class \n content ... \n}} +let blockCount = 0; +const blockRegex = /^ *{{(?:="[\w, ]*"|[^"'\s])*$|^ *}}$/gm; +const inlineFullRegex = /{{[^\n]*}}/g; +const inlineRegex = /{{(?:="[\w, ]*"|[^"'\s])*\s*|}}/g; + +renderer.text = function(text){ + const newText = text.replaceAll('"', '"'); + let matches; + + //DIV - BLOCK-LEVEL + if(matches = newText.match(blockRegex)) { + let matchIndex = 0; + const res = _.reduce(newText.split(blockRegex), (r, splitText)=>{ + if(splitText) r.push(Markdown.parseInline(splitText, { renderer: renderer })); + + const block = matches[matchIndex] ? matches[matchIndex].trimLeft() : ''; + if(block && block.startsWith('{')) { + const values = processStyleTags(block.substring(2)); + r.push(`
`); + blockCount++; + } else if(block == '}}' && blockCount !== 0){ + r.push('
'); + blockCount--; + } + + matchIndex++; + + return r; + }, []).join(''); + return res; + } else if(matches = newText.match(inlineFullRegex)) { + + //SPAN - INLINE + matches = newText.match(inlineRegex); + let matchIndex = 0; + const res = _.reduce(newText.split(inlineRegex), (r, splitText)=>{ + + if(splitText) r.push(Markdown.parseInline(splitText, { renderer: renderer })); + + const block = matches[matchIndex] ? matches[matchIndex].trimLeft() : ''; + if(block && block.startsWith('{{')) { + const values = processStyleTags(block.substring(2)); + r.push(`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 ? `style="${styles.join(' ')}"` : ''}`; +}; module.exports = { marked : Markdown, render : (rawBrewText)=>{ + blockCount = 0; + rawBrewText = rawBrewText.replace(/^\\column/gm, `
`) + .replace(/^}}/gm, '\n}}') + .replace(/^({{[^\n]*)$/gm, '$1\n'); return Markdown( sanatizeScriptTags(rawBrewText), { renderer: renderer } diff --git a/shared/naturalcrit/markdownLegacy.js b/shared/naturalcrit/markdownLegacy.js new file mode 100644 index 000000000..27cf10e9c --- /dev/null +++ b/shared/naturalcrit/markdownLegacy.js @@ -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), '')){ + const openTag = html.substring(0, html.indexOf('>')+1); + html = html.substring(html.indexOf('>')+1); + html = html.substring(0, html.lastIndexOf('
')); + return `${openTag} ${Markdown(html)}
`; + } + // if(_.startsWith(_.trim(html), '')){ + // const openTag = html.substring(0, html.indexOf('>')+1); + // html = html.substring(html.indexOf('>')+1); + // html = html.substring(0, html.lastIndexOf('')); + // html = html.replaceAll(/\s(\.[^{]*)/gm, '.legacy $1'); + // return `${openTag} ${html} `; + // } + 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 = `${text}`; + 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 = { + '&' : '&', + '<' : '<', + '>' : '>', + '"' : '"', + '\'' : ''' +}; +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(/